summaryrefslogtreecommitdiff
path: root/src/Text/Pandoc/Filter/Plot/Renderers.hs
blob: 1a7076a4ed0be6122cda57b807bf74ae884970ba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards   #-}

{-|
Module      : $header$
Copyright   : (c) Laurent P René de Cotret, 2020
License     : GNU GPL, version 2 or above
Maintainer  : laurent.decotret@outlook.com
Stability   : internal
Portability : portable

Specification of renderers.
-}

module Text.Pandoc.Filter.Plot.Renderers (
      scriptExtension
    , comment
    , preambleSelector
    , supportedSaveFormats
    , scriptChecks
    , parseExtraAttrs
    , command
    , capture
    , toolkitAvailable
    , availableToolkits
    , unavailableToolkits
) where

import           Control.Concurrent.ParallelIO.Local

import           Data.List                                     ((\\))
import           Data.Map.Strict                               (Map)
import           Data.Maybe                                    (catMaybes)
import           Data.Text                                     (Text)

import           Text.Pandoc.Filter.Plot.Renderers.Mathematica
import           Text.Pandoc.Filter.Plot.Renderers.Matlab
import           Text.Pandoc.Filter.Plot.Renderers.Matplotlib
import           Text.Pandoc.Filter.Plot.Renderers.Octave
import           Text.Pandoc.Filter.Plot.Renderers.Plotly
import           Text.Pandoc.Filter.Plot.Renderers.GGPlot2
import           Text.Pandoc.Filter.Plot.Renderers.GNUPlot

import           Text.Pandoc.Filter.Plot.Types


-- Extension for script files, e.g. ".py", or ".m".
scriptExtension :: Toolkit -> String
scriptExtension Matplotlib   = ".py"
scriptExtension PlotlyPython = ".py"
scriptExtension Matlab       = ".m"
scriptExtension Mathematica  = ".m"
scriptExtension Octave       = ".m"
scriptExtension GGPlot2      = ".r"
scriptExtension GNUPlot      = ".gp"


-- Make a string into a comment
comment :: Toolkit -> (Text -> Text)
comment Matplotlib   = mappend "# "
comment PlotlyPython = mappend "# "
comment Matlab       = mappend "% "
comment Mathematica  = \t -> mconcat ["(*", t, "*)"]
comment Octave       = mappend "% "
comment GGPlot2      = mappend "# "
comment GNUPlot      = mappend "# "


-- | The function that maps from configuration to the preamble.
preambleSelector :: Toolkit -> (Configuration -> Script)
preambleSelector Matplotlib   = matplotlibPreamble
preambleSelector PlotlyPython = plotlyPythonPreamble
preambleSelector Matlab       = matlabPreamble
preambleSelector Mathematica  = mathematicaPreamble
preambleSelector Octave       = octavePreamble
preambleSelector GGPlot2      = ggplot2Preamble
preambleSelector GNUPlot      = gnuplotPreamble


-- | Save formats supported by this renderer.
supportedSaveFormats :: Toolkit -> [SaveFormat]
supportedSaveFormats Matplotlib   = matplotlibSupportedSaveFormats
supportedSaveFormats PlotlyPython = plotlyPythonSupportedSaveFormats
supportedSaveFormats Matlab       = matlabSupportedSaveFormats
supportedSaveFormats Mathematica  = mathematicaSupportedSaveFormats
supportedSaveFormats Octave       = octaveSupportedSaveFormats
supportedSaveFormats GGPlot2      = ggplot2SupportedSaveFormats
supportedSaveFormats GNUPlot      = gnuplotSupportedSaveFormats


-- Checks to perform before running a script. If ANY check fails,
-- the figure is not rendered. This is to prevent, for example,
-- blocking operations to occur.
scriptChecks :: Toolkit -> [Script -> CheckResult]
scriptChecks = const mempty


-- | Parse code block headers for extra attributes that are specific
-- to this renderer. By default, no extra attributes are parsed.
parseExtraAttrs :: Toolkit -> Map Text Text -> Map Text Text
parseExtraAttrs Matplotlib = matplotlibExtraAttrs
parseExtraAttrs _          = return mempty


-- | Generate the appropriate command-line command to generate a figure.
command :: Toolkit -> (Configuration -> FigureSpec -> FilePath -> Text)
command Matplotlib   = matplotlibCommand
command PlotlyPython = plotlyPythonCommand
command Matlab       = matlabCommand
command Mathematica  = mathematicaCommand
command Octave       = octaveCommand
command GGPlot2      = ggplot2Command
command GNUPlot      = gnuplotCommand


-- | Script fragment required to capture a figure.
capture :: Toolkit -> (FigureSpec -> FilePath -> Script)
capture Matplotlib   = matplotlibCapture
capture PlotlyPython = plotlyPythonCapture
capture Matlab       = matlabCapture
capture Mathematica  = mathematicaCapture
capture Octave       = octaveCapture
capture GGPlot2      = ggplot2Capture
capture GNUPlot      = gnuplotCapture


-- | Check if a toolkit is available, based on the current configuration
toolkitAvailable :: Toolkit -> Configuration -> IO Bool
toolkitAvailable Matplotlib   = matplotlibAvailable
toolkitAvailable PlotlyPython = plotlyPythonAvailable
toolkitAvailable Matlab       = matlabAvailable
toolkitAvailable Mathematica  = mathematicaAvailable
toolkitAvailable Octave       = octaveAvailable
toolkitAvailable GGPlot2      = ggplot2Available
toolkitAvailable GNUPlot      = gnuplotAvailable


-- | List of toolkits available on this machine.
-- The executables to look for are taken from the configuration.
availableToolkits :: Configuration -> IO [Toolkit]
availableToolkits conf = do
    -- Certain toolkits (e.g. Python-based toolkits)
    -- may take a long time to startup.
    -- Therefore, we check for available of toolkits in parallel.
    -- TODO: benchmark. Is this overkill?
    maybeToolkits <- withPool (length toolkits) $ 
        \pool -> parallel pool (maybeToolkit <$> toolkits)
    return $ catMaybes maybeToolkits
    where
        maybeToolkit tk = do
            available <- toolkitAvailable tk conf
            if available
                then return $ Just tk
                else return Nothing

    
-- | List of toolkits not available on this machine.
-- The executables to look for are taken from the configuration.
unavailableToolkits :: Configuration -> IO [Toolkit]
unavailableToolkits conf = ((\\) toolkits) <$> availableToolkits conf