diagrams-pgf-1.4: src/Diagrams/Backend/PGF.hs
{-# LANGUAGE TypeFamilies #-}
-----------------------------------------------------------------------------
-- |
-- Module : Diagrams.Backend.PGF
-- Copyright : (c) 2015 Christopher Chalmers
-- License : BSD-style (see LICENSE)
-- Maintainer : diagrams-discuss@googlegroups.com
--
-- A full-featured PGF backend for diagrams producing PGF code
-- suitable for LaTeX, ConTeXt or plain TeX consumption.
--
-- To invoke the PGF backend, you have a number of options.
--
-- * You can use 'renderPGF' or 'renderPGF'' to render a 'Diagram' to a
-- ".tex" or ".pdf" file;
--
-- * You can use the most flexible 'renderDia' function to access the
-- resulting PGF code directly in memory as a bytestring 'Builder'.
--
-- * Use 'Diagrams.Backend.PGF.CmdLine.mainWith' or
-- 'Diagrams.Backend.PGF.CmdLine.defaultMain' to make a command line
-- application. See 'Diagrams.Backend.PGF.CmdLine' for more info.
--
-- The 'Surface' provides the necessary information for rendering PGF code and
-- building a PDF using "texrunner". See 'Diagrams.Backend.PGF.Surface'
-- for more info.
--
module Diagrams.Backend.PGF
( -- * Rendering token & options
PGF (..)
, B
-- * Rendering functions
, renderPGF
, renderPGF'
, renderPGFSurf
-- * Options
-- | Options for changing how the diagram is rendered. 'Options'
-- 'PGF' is an instance of 'Default':
--
-- @
-- def = PGFOptions {
-- _surface = 'latexSurface'
-- _sizeSpec = 'absolute'
-- _readable = 'True'
-- _standalone = 'False'
-- }
-- @
--
-- You can edit the default options using lenses.
, readable
, sizeSpec
, surface
, standalone
-- * Surfaces
-- | These surfaces should be suitable for basic diagrams. For more
-- complicated options see 'Diagrams.Backend.PGF.Surface'.
, Surface
, surfOnlineTex
-- ** Predefined surfaces
, latexSurface
, contextSurface
, plaintexSurface
-- ** Lenses
, command
, arguments
, preamble
-- * Online TeX
-- | By using 'OnlineTex', diagrams is able to query tex for sizes
-- of hboxs and give them the corresponding envelopes. These can
-- then be used as any other diagram with the correct size.
--
-- Online diagrams use the 'Surface' to run tex in online mode and
-- get feedback for hbox sizes. To run it you can use
-- 'renderOnlinePGF', 'renderOnlinePGF'' or 'onlineMain' from
-- 'Diagrams.Backend.PGF.CmdLine'.
--
-- See
-- <https://github.com/diagrams/diagrams-pgf/tree/master/examples>
-- for examples.
, OnlineTex
, renderOnlinePGF
, renderOnlinePGF'
-- ** Hbox
, Hbox
, hboxOnline
, hboxPoint
, hboxSurf
) where
import Data.ByteString.Builder
import System.Directory hiding (readable)
import System.FilePath
import System.IO
import System.Texrunner
import System.Texrunner.Online hiding (hbox)
import qualified Data.ByteString.Char8 as B
import qualified Data.ByteString.Lazy.Char8 as LB
import Diagrams.Backend.PGF.Hbox
import Diagrams.Backend.PGF.Render
import Diagrams.Backend.PGF.Surface
import Diagrams.Size
import Diagrams.Prelude hiding (r2)
type B = PGF
-- | Render a pgf diagram and write it to the given filepath. Same as
-- 'renderPGF'' but uses the default options.
renderPGF :: (TypeableFloat n, Monoid' m)
=> FilePath -- ^ path to output
-> SizeSpec V2 n -- ^ size of output
-> QDiagram PGF V2 n m -- ^ 'Diagram' to render
-> IO ()
renderPGF outFile sizeSp = renderPGFSurf outFile sizeSp def
-- | Render a pgf diagram and write it to the given filepath. Same as
-- 'renderPGF' but takes a 'Surface'.
renderPGFSurf :: (TypeableFloat n, Monoid' m)
=> FilePath -- ^ path to output
-> SizeSpec V2 n -- ^ size of output
-> Surface -- ^ surface to render with
-> QDiagram PGF V2 n m -- ^ diagram to render
-> IO ()
renderPGFSurf outFile sizeSp surf =
renderPGF' outFile $
def & sizeSpec .~ sizeSp
& surface .~ surf
-- | Render a pgf diagram and write it to the given filepath. If the file has
-- the extension @.pdf@, a PDF is generated in a temporary directory using
-- options from the given surface, otherwise, the tex output is saved
-- using the surface's 'TexFormat'.
renderPGF' :: (TypeableFloat n, Monoid' m)
=> FilePath -> Options PGF V2 n -> QDiagram PGF V2 n m -> IO ()
renderPGF' outFile opts d = case takeExtension outFile of
".pdf" -> do
let rendered = renderDia PGF (opts & standalone .~ True & readable .~ False) d
currentDir <- getCurrentDirectory
targetDir <- canonicalizePath (takeDirectory outFile)
let source = toLazyByteString rendered
(_, texLog, mPDF) <- runTex (opts^.surface.command)
(opts^.surface.arguments)
[currentDir, targetDir]
source
case mPDF of
Nothing -> putStrLn "Error, no PDF found:"
>> B.putStrLn (prettyPrintLog texLog)
Just pdf -> LB.writeFile outFile pdf
-- tex output
_ -> writeTexFile outFile opts d
-- | Render an online 'PGF' diagram and save it. Same as
-- 'renderOnlinePGF'' using default options.
renderOnlinePGF :: (TypeableFloat n, Monoid' m)
=> FilePath
-> SizeSpec V2 n
-> OnlineTex (QDiagram PGF V2 n m)
-> IO ()
renderOnlinePGF outFile sizeSp = renderOnlinePGF' outFile (def & sizeSpec .~ sizeSp)
-- | Same as 'renderOnlinePDF' but takes 'Options' 'PGF'.
renderOnlinePGF' :: (TypeableFloat n, Monoid' m)
=> FilePath
-> Options PGF V2 n
-> OnlineTex (QDiagram PGF V2 n m)
-> IO ()
renderOnlinePGF' outFile opts dOL = case takeExtension outFile of
".pdf" -> do
((), texLog, mPDF) <-
runOnlineTex'
(surf^.command)
(surf^.arguments)
(toByteString . stringUtf8 $ surf ^. (preamble <> beginDoc)) $ do
d <- dOL
-- we've already output the preamble so don't do it again
let opts' = opts & surface %~ set beginDoc "" . set preamble ""
& readable .~ False
& standalone .~ True
rendered = renderDia PGF opts' d
texPutStrLn $ toByteString rendered
case mPDF of
Nothing -> putStrLn "Error, no PDF found:"
>> print texLog
Just pdf -> LB.writeFile outFile pdf
-- tex output
_ -> surfOnlineTexIO surf dOL >>= writeTexFile outFile opts
where
surf = opts ^. surface
toByteString = LB.toStrict . toLazyByteString
-- | Write the rendered diagram to a text file, ignoring the extension.
writeTexFile :: (TypeableFloat n, Monoid' m)
=> FilePath -> Options PGF V2 n -> QDiagram PGF V2 n m -> IO ()
writeTexFile outFile opts d = do
h <- openFile outFile WriteMode
hPutBuilder h $ renderDia PGF opts d
hClose h