-
Notifications
You must be signed in to change notification settings - Fork 4
/
Pandoc.hs
258 lines (229 loc) · 12.3 KB
/
Pandoc.hs
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
module Image.LaTeX.Render.Pandoc (
-- * SVG
convertFormulaSvgInline,
convertFormulaSvgBlock,
convertFormulaSvgPandoc,
-- * Separate Files
convertFormulaFilesInline,
convertFormulaFilesBlock,
convertFormulaFilesPandoc,
-- ** Name Supplies
NameSupply,
newNameSupply,
-- * Options
PandocFormulaOptions(..),
ShrinkSize,
defaultPandocFormulaOptions,
-- ** Error display functions
hideError,
displayError,
-- * Generalised versions
-- ** SVG
convertFormulaSvgInlineWith,
convertFormulaSvgBlockWith,
convertFormulaSvgPandocWith,
-- ** Files
convertFormulaFilesInlineWith,
convertFormulaFilesBlockWith,
convertFormulaFilesPandocWith,
) where
import Data.IORef (IORef, modifyIORef, newIORef, readIORef)
import Data.String (IsString (..))
import Numeric (showFFloat)
import System.FilePath ((<.>), (</>))
import Text.Pandoc.Definition (Block (..), Format (..), Inline (..), MathType (..), Pandoc, nullAttr)
import Text.Pandoc.Walk (walkM)
import qualified Data.Text as T
import Image.LaTeX.Render
-- | All options pertaining to the actual display of formulae.
data PandocFormulaOptions = PandocFormulaOptions
{ errorDisplay :: RenderError -> Inline
-- ^ How to display various errors (such as LaTeX errors). Usually this can just be @displayError@ but you may wish @hideError@
-- to avoid putting potentially secure information into the output page.
, formulaOptions :: Maybe MathType -> FormulaOptions
-- ^ LaTeX environment settings, including the preamble, for each equation type (display and inline)
}
-- | A set of sensible defaults for formula options.
defaultPandocFormulaOptions :: PandocFormulaOptions
defaultPandocFormulaOptions = PandocFormulaOptions
{ errorDisplay = displayError
, formulaOptions = fopts
}
where
fopts (Just DisplayMath) = displaymath
fopts (Just InlineMath) = math
fopts Nothing = defaultFormulaOptions
-- | Denominator for various dimensions. For high DPI displays, it can be useful to use values of 2 or 4, so that the dimensions
-- of the image are a fraction of the actual image size, and the image appears more crisp. Otherwise, a value of 1 will always
-- produce sensible, if somewhat pixelated results.
type ShrinkSize = Int
-- | Convert a formula in a pandoc document to an image, embedding the image into the HTML using Data URIs.
convertFormulaSvgInline
:: EnvironmentOptions -- ^ System environment settings
-> PandocFormulaOptions -- ^ Formula display settings
-> Inline -> IO Inline
convertFormulaSvgInline = convertFormulaSvgInlineWith . imageForFormula
--
-- | Convert all formulae in a pandoc block to images, embedding the images into the HTML using Data URIs.
convertFormulaSvgBlock
:: EnvironmentOptions -- ^ System environment settings
-> PandocFormulaOptions -- ^ Formula display settings
-> Block -> IO Block
convertFormulaSvgBlock = convertFormulaSvgBlockWith . imageForFormula
-- | Convert all formulae in a pandoc document to images, embedding the images into the HTML using Data URIs.
convertFormulaSvgPandoc
:: EnvironmentOptions -- ^ System environment settings
-> PandocFormulaOptions -- ^ Formula display settings
-> Pandoc -> IO Pandoc
convertFormulaSvgPandoc e = walkM . convertFormulaSvgBlock e
-- | A generalisation of 'convertFormulaSvgBlock' which allows the actual image rendering
-- function to be customised, so that (e.g) caching can be added or other image processing.
convertFormulaSvgBlockWith
:: (FormulaOptions -> Formula -> IO (Either RenderError SVG))
-- ^ Function that renders a formula, such as @imageForFormula defaultEnv@
-> PandocFormulaOptions -- ^ Formula display settings
-> Block -> IO Block
convertFormulaSvgBlockWith f o (RawBlock format s)
| format == Format "tex" || format == Format "latex"
= do
res <- f (formulaOptions o Nothing) (toString s)
return $ Para $ singleton $ case res of
Left e -> errorDisplay o e
Right svg -> RawInline (Format "html") $ fromString $ alterForHTML svg
convertFormulaSvgBlockWith f o b = walkM (convertFormulaSvgInlineWith f o) b
-- | A generalisation of 'convertFormulaSvgInline' which allows the actual image rendering
-- function to be customised, so that (e.g) caching can be added or other image processing.
convertFormulaSvgInlineWith
:: (FormulaOptions -> Formula -> IO (Either RenderError SVG))
-- ^ Function that renders a formula, such as @imageForFormula defaultEnv@
-> PandocFormulaOptions -- ^ Formula display settings
-> Inline -> IO Inline
convertFormulaSvgInlineWith f o (Math t s) = do
res <- f (formulaOptions o (Just t)) (toString s)
return $ case res of
Left e -> errorDisplay o e
Right svg -> RawInline (Format "html") $ fromString $ alterForHTML svg
convertFormulaSvgInlineWith _ _ x = return x
-- | A generalisation of 'convertFormulaSvgPandoc' which allows the actual image rendering
-- function to be customised, so that (e.g) caching can be added or other image processing.
convertFormulaSvgPandocWith
:: (FormulaOptions -> Formula -> IO (Either RenderError SVG))
-- ^ Function that renders a formula, such as @imageForFormula defaultEnv@
-> PandocFormulaOptions -- ^ Formula display settings
-> Pandoc -> IO Pandoc
convertFormulaSvgPandocWith f = walkM . convertFormulaSvgBlockWith f
-- | If we use files for the images, we need some way of naming the image files we produce
-- A NameSupply provides us with a source of unique names via an ever-increasing integer.
-- It's important that any invocation of 'convertFormulaFiles' or 'convertFormulaFiles'
-- that shares the same image storage directory will also use the same name supply, or they
-- will overwrite each others images.
--
-- TODO: remove
type NameSupply = IORef Int
-- | Create a new name supply.
newNameSupply :: IO NameSupply
newNameSupply = newIORef 0
-- | A generalisation of 'convertFormulaFilesInline' which allows the actual image rendering
-- function to be customised, so that (e.g) caching can be added or other image processing.
convertFormulaFilesInlineWith
:: (FormulaOptions -> Formula -> IO (Either RenderError SVG))
-- ^ Function that renders a formula, such as @imageForFormula defaultEnv@
-> NameSupply -- ^ Unique file name supply. Reuse this for every invocation that shares the same image directory.
-> FilePath -- ^ Name of image directory where images will be stored
-> PandocFormulaOptions -- ^ Formula display settings
-> Inline -> IO Inline
convertFormulaFilesInlineWith f ns bn o (Math t s) = f (formulaOptions o (Just t)) (toString s) >>= \res -> case res of
Left e -> return $ errorDisplay o e
Right svg -> makeSvgFile ns bn (Just t) svg
convertFormulaFilesInlineWith _ _ _ _ x = return x
makeSvgFile :: NameSupply -> FilePath -> Maybe MathType -> SVG -> IO Inline
makeSvgFile ns bn t svg = do
let baseline = getBaseline svg
fn <- readIORef ns
modifyIORef ns (+1)
let uri = bn </> show fn <.> "svg"
writeFile uri svg
let classArg = case t of
Nothing -> ""
Just InlineMath -> " class='inline-math'"
Just DisplayMath -> " class='display-math'"
return $ RawInline (Format "html") $ fromString $
"<img src=\"" ++ uri ++ "\"" ++ classArg ++
" style=\"margin:0; vertical-align:-" ++ showFFloat (Just 6) baseline "" ++ "pt;\"/>"
-- | A generalisation of 'convertFormulaFilesBlock' which allows the actual image rendering
-- function to be customised, so that (e.g) caching can be added or other image processing.
convertFormulaFilesBlockWith
:: (FormulaOptions -> Formula -> IO (Either RenderError SVG))
-- ^ Function that renders a formula, such as @imageForFormula defaultEnv@
-> NameSupply -- ^ Unique file name supply. Reuse this for every invocation that shares the same image directory.
-> FilePath -- ^ Name of image directory where images will be stored
-> PandocFormulaOptions -- ^ Formula display settings
-> Block -> IO Block
convertFormulaFilesBlockWith f ns bn o (RawBlock format s)
| format == Format "tex" || format == Format "latex"
= do
res <- f (formulaOptions o Nothing) (toString s)
case res of
Left e -> return $ Para $ singleton $ errorDisplay o e
Right svg -> fmap (Para . singleton) $ makeSvgFile ns bn Nothing svg
convertFormulaFilesBlockWith f ns bn o b = walkM (convertFormulaFilesInlineWith f ns bn o) b
-- | Convert a formula in a pandoc document to an image, storing the images in a separate directory.
convertFormulaFilesInline
:: EnvironmentOptions -- ^ System environment settings
-> NameSupply -- ^ Unique file name supply. Reuse this for every invocation that shares the same image directory.
-> FilePath -- ^ Name of image directory where images will be stored
-> PandocFormulaOptions -- ^ Formula display settings
-> Inline -> IO Inline
convertFormulaFilesInline = convertFormulaFilesInlineWith . imageForFormula
--
-- | Convert every formula in a pandoc block to an image, storing the images in a separate directory.
convertFormulaFilesBlock
:: EnvironmentOptions -- ^ System environment settings
-> NameSupply -- ^ Unique file name supply. Reuse this for every invocation that shares the same image directory.
-> FilePath -- ^ Name of image directory where images will be stored
-> PandocFormulaOptions -- ^ Formula display settings
-> Block -> IO Block
convertFormulaFilesBlock = convertFormulaFilesBlockWith . imageForFormula
-- | Convert every formula in a pandoc document to an image, storing the images in a separate directory.
convertFormulaFilesPandoc
:: EnvironmentOptions -- ^ System environment settings
-> NameSupply -- ^ Unique file name supply. Reuse this for every invocation that shares the same image directory.
-> FilePath -- ^ Name of image directory where images will be stored
-> PandocFormulaOptions -- ^ Formula display settings
-> Pandoc -> IO Pandoc
convertFormulaFilesPandoc eo ns fp = walkM . convertFormulaFilesInline eo ns fp
-- | A generalisation of 'convertFormulaFilesPandoc' which allows the actual image rendering
-- function to be customised, so that (e.g) caching can be added or other image processing.
convertFormulaFilesPandocWith
:: (FormulaOptions -> Formula -> IO (Either RenderError SVG))
-- ^ Function that renders a formula, such as @imageForFormula defaultEnv@
-> NameSupply -- ^ Unique file name supply. Reuse this for every invocation that shares the same image directory.
-> FilePath -- ^ Name of image directory where images will be stored
-> PandocFormulaOptions -- ^ Formula display settings
-> Pandoc -> IO Pandoc
convertFormulaFilesPandocWith x y a = walkM . convertFormulaFilesInlineWith x y a
-- | Render all errors simply as "Error"
hideError :: RenderError -> Inline
hideError = const $ Str blank
where
blank = "Error"
-- | Render errors nicely, in order to show any problems clearly, with all information intact.
displayError :: RenderError -> Inline
displayError (LaTeXFailure str) = pandocError [Str "LaTeX failed:", LineBreak, Code nullAttr $ fromString str]
displayError (DVISVGMFailure str) = pandocError [Str "DVIPS failed:", LineBreak, Code nullAttr $ fromString str]
displayError (IOException e) = pandocError [Str "IO Exception:", LineBreak, Code nullAttr $ fromString $ show e]
pandocError :: [Inline] -> Inline
pandocError = Strong . (Emph [Str "Error:"] :)
-------------------------------------------------------------------------------
-- compat
-------------------------------------------------------------------------------
class IsString s => ToString s where
toString :: s -> String
instance Char ~ c => ToString [c] where
toString = id
instance ToString T.Text where
toString = T.unpack
singleton :: a -> [a]
singleton = return