summaryrefslogtreecommitdiffstats
path: root/src/Text/Pandoc/Filter/Plot/Renderers/Matplotlib.hs
blob: 9890ab63279787f786e77703f700175322b2566c (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
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE NoImplicitPrelude #-}

-- |
-- Module      : $header$
-- Copyright   : (c) Laurent P René de Cotret, 2019 - present
-- License     : GNU GPL, version 2 or above
-- Maintainer  : laurent.decotret@outlook.com
-- Stability   : internal
-- Portability : portable
--
-- Rendering Matplotlib code blocks.
--
-- Note that the MatplotlibM renderer supports two extra arguments:
--     * @tight_bbox=True|False@ : Make plot bounding box tight. Default is False
--     * @transparent=True|False@ : Make plot background transparent (perfect for web pages). Default is False.
module Text.Pandoc.Filter.Plot.Renderers.Matplotlib
  ( matplotlib,
    matplotlibSupportedSaveFormats,
  )
where

import qualified Data.Map.Strict as M
import Data.Monoid (Any (..))
import qualified Data.Text as T
import Text.Pandoc.Filter.Plot.Renderers.Prelude

matplotlib :: PlotM Renderer
matplotlib = do
      cmdargs <- asksConfig matplotlibCmdArgs
      return $
        Renderer
          { rendererToolkit = Matplotlib,
            rendererCapture = matplotlibCapture,
            rendererCommand = matplotlibCommand cmdargs,
            rendererAvailability = CommandSuccess $ \exe -> [st|#{pathToExe exe} -c "import matplotlib"|],
            rendererSupportedSaveFormats = matplotlibSupportedSaveFormats,
            rendererChecks = [matplotlibCheckIfShow],
            rendererLanguage = "python",
            rendererComment = mappend "# ",
            rendererScriptExtension = ".py"
          }

matplotlibSupportedSaveFormats :: [SaveFormat]
matplotlibSupportedSaveFormats = [PNG, PDF, SVG, JPG, EPS, GIF, TIF]

matplotlibCommand :: Text -> OutputSpec -> Text
matplotlibCommand cmdargs OutputSpec {..} = [st|#{pathToExe oExecutable} #{cmdargs} "#{oScriptPath}"|]

matplotlibCapture :: FigureSpec -> FilePath -> Script
matplotlibCapture = appendCapture matplotlibCaptureFragment

matplotlibCaptureFragment :: FigureSpec -> FilePath -> Script
matplotlibCaptureFragment FigureSpec {..} fname =
  [st|
import matplotlib.pyplot as plt
plt.savefig(r"#{fname}", dpi=#{dpi}, transparent=#{transparent}, bbox_inches=#{tightBox})
|]
  where
    attrs = M.fromList extraAttrs
    tight_ = readBool $ M.findWithDefault "False" (T.pack $ show MatplotlibTightBBoxK) attrs
    transparent_ = readBool $ M.findWithDefault "False" (T.pack $ show MatplotlibTransparentK) attrs
    tightBox = if tight_ then ("'tight'" :: Text) else ("None" :: Text)
    transparent = if transparent_ then ("True" :: Text) else ("False" :: Text)

-- | Check if `matplotlib.pyplot.show()` calls are present in the script,
-- which would halt pandoc-plot
matplotlibCheckIfShow :: Script -> CheckResult
matplotlibCheckIfShow s =
  if getAny $ mconcat showPresent
    then CheckFailed "encountered a call to `matplotlib.pyplot.show` or `plt.show`, which would stall `pandoc-plot"
    else CheckPassed
  where
    showPresent =
      (\n -> Any (T.isInfixOf n s))
        <$> [ "matplotlib.pyplot.show()",
              "pyplot.show()",
              "plt.show()"
            ]

-- | Flexible boolean parsing
readBool :: Text -> Bool
readBool s
  | s `elem` ["True", "true", "'True'", "'true'", "1"] = True
  | s `elem` ["False", "false", "'False'", "'false'", "0"] = False
  | otherwise = errorWithoutStackTrace $ unpack $ mconcat ["Could not parse '", s, "' into a boolean. Please use 'True' or 'False'"]