summaryrefslogtreecommitdiff
path: root/src/Text/Pandoc/Filter
diff options
context:
space:
mode:
authorLaurentRDC <>2019-11-26 01:13:00 (GMT)
committerhdiff <hdiff@hdiff.luite.com>2019-11-26 01:13:00 (GMT)
commitaeff3d6d7c721a37eb30a788ddad59ceb16e3395 (patch)
treeac15aa4e454b6f4d9b37ef00482f10a277b594ff /src/Text/Pandoc/Filter
parent0896c8b21460ee840bb016aa81192720dfcc1eb4 (diff)
version 2.3.0.02.3.0.0
Diffstat (limited to 'src/Text/Pandoc/Filter')
-rw-r--r--src/Text/Pandoc/Filter/Pyplot.hs6
-rw-r--r--src/Text/Pandoc/Filter/Pyplot/Configuration.hs28
-rw-r--r--src/Text/Pandoc/Filter/Pyplot/FigureSpec.hs91
-rw-r--r--src/Text/Pandoc/Filter/Pyplot/Scripting.hs2
-rw-r--r--src/Text/Pandoc/Filter/Pyplot/Types.hs75
5 files changed, 98 insertions, 104 deletions
diff --git a/src/Text/Pandoc/Filter/Pyplot.hs b/src/Text/Pandoc/Filter/Pyplot.hs
index 37302bd..bac5092 100644
--- a/src/Text/Pandoc/Filter/Pyplot.hs
+++ b/src/Text/Pandoc/Filter/Pyplot.hs
@@ -63,8 +63,8 @@ import Text.Pandoc.Walk (walkM)
import Text.Pandoc.Filter.Pyplot.Internal
--- | Main routine to include Matplotlib plots.
--- Code blocks containing the attributes @.pyplot@ are considered
+-- | Main routine to include plots.
+-- Code blocks containing the attributes @.pyplot@ or @.plotly@ are considered
-- Python plotting scripts. All other possible blocks are ignored.
makePlot' :: Block -> PyplotM (Either PandocPyplotError Block)
makePlot' block = do
@@ -79,7 +79,7 @@ makePlot' block = do
handleResult spec ScriptSuccess = Right $ toImage spec
-- | Highest-level function that can be walked over a Pandoc tree.
--- All code blocks that have the '.pyplot' parameter will be considered
+-- All code blocks that have the @.pyplot@ / @.plotly@ class will be considered
-- figures.
makePlot :: Block -> IO Block
makePlot = makePlotWithConfig def
diff --git a/src/Text/Pandoc/Filter/Pyplot/Configuration.hs b/src/Text/Pandoc/Filter/Pyplot/Configuration.hs
index c2d42c7..7b21231 100644
--- a/src/Text/Pandoc/Filter/Pyplot/Configuration.hs
+++ b/src/Text/Pandoc/Filter/Pyplot/Configuration.hs
@@ -27,8 +27,8 @@ module Text.Pandoc.Filter.Pyplot.Configuration (
import Data.Default.Class (def)
import Data.Maybe (fromMaybe)
-import qualified Data.Text as T
-import qualified Data.Text.IO as T
+import Data.String (fromString)
+import qualified Data.Text.IO as TIO
import Data.Yaml
import Data.Yaml.Config (ignoreEnv, loadYamlSettings)
@@ -61,23 +61,23 @@ data ConfigPrecursor
instance FromJSON ConfigPrecursor where
parseJSON (Object v) =
ConfigPrecursor
- <$> v .:? (T.pack directoryKey) .!= (defaultDirectory def)
- <*> v .:? (T.pack includePathKey)
- <*> v .:? (T.pack withLinksKey) .!= (defaultWithLinks def)
- <*> v .:? (T.pack saveFormatKey) .!= (extension $ defaultSaveFormat def)
- <*> v .:? (T.pack dpiKey) .!= (defaultDPI def)
- <*> v .:? (T.pack isTightBboxKey) .!= (isTightBbox def)
- <*> v .:? (T.pack isTransparentKey) .!= (isTransparent def)
- <*> v .:? "interpreter" .!= (interpreter def)
- <*> v .:? "flags" .!= (flags def)
+ <$> v .:? directoryKey .!= (defaultDirectory def)
+ <*> v .:? includePathKey
+ <*> v .:? withLinksKey .!= (defaultWithLinks def)
+ <*> v .:? saveFormatKey .!= (show $ defaultSaveFormat def)
+ <*> v .:? dpiKey .!= (defaultDPI def)
+ <*> v .:? isTightBboxKey .!= (isTightBbox def)
+ <*> v .:? isTransparentKey .!= (isTransparent def)
+ <*> v .:? "interpreter" .!= (interpreter def)
+ <*> v .:? "flags" .!= (flags def)
parseJSON _ = fail "Could not parse the configuration"
-
+
renderConfiguration :: ConfigPrecursor -> IO Configuration
renderConfiguration prec = do
- includeScript <- fromMaybe mempty $ T.readFile <$> defaultIncludePath_ prec
- let saveFormat' = fromMaybe (defaultSaveFormat def) $ saveFormatFromString $ defaultSaveFormat_ prec
+ includeScript <- fromMaybe mempty $ TIO.readFile <$> defaultIncludePath_ prec
+ let saveFormat' = fromString $ defaultSaveFormat_ prec
return $ Configuration
{ defaultDirectory = defaultDirectory_ prec
, defaultIncludeScript = includeScript
diff --git a/src/Text/Pandoc/Filter/Pyplot/FigureSpec.hs b/src/Text/Pandoc/Filter/Pyplot/FigureSpec.hs
index 0c812b8..1a4148a 100644
--- a/src/Text/Pandoc/Filter/Pyplot/FigureSpec.hs
+++ b/src/Text/Pandoc/Filter/Pyplot/FigureSpec.hs
@@ -1,8 +1,6 @@
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE QuasiQuotes #-}
-{-# LANGUAGE TemplateHaskell #-}
-{-# LANGUAGE TypeSynonymInstances #-}
-
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE QuasiQuotes #-}
+{-# LANGUAGE TemplateHaskell #-}
{-|
Module : $header$
Copyright : (c) Laurent P René de Cotret, 2019
@@ -17,7 +15,6 @@ with keeping track of figure specifications
module Text.Pandoc.Filter.Pyplot.FigureSpec
( FigureSpec(..)
, SaveFormat(..)
- , saveFormatFromString
, toImage
, sourceCodePath
, figurePath
@@ -29,7 +26,7 @@ module Text.Pandoc.Filter.Pyplot.FigureSpec
import Control.Monad (join)
import Control.Monad.IO.Class (liftIO)
-import Control.Monad.Reader
+import Control.Monad.Reader (ask)
import Data.Default.Class (def)
import Data.Hashable (hash)
@@ -37,8 +34,9 @@ import Data.List (intersperse)
import qualified Data.Map.Strict as Map
import Data.Maybe (fromMaybe)
import Data.Monoid ((<>))
-import qualified Data.Text as T
-import qualified Data.Text.IO as T
+import Data.String (fromString)
+import Data.Text (Text, pack, unpack)
+import qualified Data.Text.IO as TIO
import Data.Version (showVersion)
import Paths_pandoc_pyplot (version)
@@ -49,7 +47,8 @@ import System.FilePath (FilePath, addExtension,
import Text.Pandoc.Builder (fromList, imageWith, link,
para, toList)
-import Text.Pandoc.Definition
+import Text.Pandoc.Definition (Block (..), Inline,
+ Pandoc (..))
import Text.Shakespeare.Text (st)
import Text.Pandoc.Class (runPure)
@@ -61,7 +60,7 @@ import Text.Pandoc.Readers (readMarkdown)
import Text.Pandoc.Filter.Pyplot.Types
--- | Determine inclusion specifications from Block attributes.
+-- | Determine inclusion specifications from @Block@ attributes.
-- Note that the @".pyplot"@ OR @.plotly@ class is required, but all other
-- parameters are optional.
parseFigureSpec :: Block -> PyplotM (Maybe FigureSpec)
@@ -72,42 +71,37 @@ parseFigureSpec (CodeBlock (id', cls, attrs) content)
where
attrs' = Map.fromList attrs
filteredAttrs = filter (\(k, _) -> k `notElem` inclusionKeys) attrs
- includePath = Map.lookup includePathKey attrs'
+ includePath = unpack <$> Map.lookup includePathKey attrs'
+ header = "# Generated by pandoc-pyplot " <> ((pack . showVersion) version)
figureSpec :: RenderingLibrary -> PyplotM FigureSpec
figureSpec lib = do
config <- ask
includeScript <- fromMaybe
(return $ defaultIncludeScript config)
- ((liftIO . T.readFile) <$> includePath)
- let header = "# Generated by pandoc-pyplot " <> ((T.pack . showVersion) version)
- fullScript = mconcat $ intersperse "\n" [header, includeScript, T.pack content]
- caption' = Map.findWithDefault mempty captionKey attrs'
- format = fromMaybe (defaultSaveFormat config) $ join $ saveFormatFromString <$> Map.lookup saveFormatKey attrs'
- dir = makeValid $ Map.findWithDefault (defaultDirectory config) directoryKey attrs'
- dpi' = fromMaybe (defaultDPI config) $ read <$> Map.lookup dpiKey attrs'
- withLinks' = fromMaybe (defaultWithLinks config) $ readBool <$> Map.lookup withLinksKey attrs'
- tightBbox' = isTightBbox config
- transparent' = isTransparent config
- blockAttrs' = (id', filter (\c -> c `notElem` ["pyplot", "plotly"]) cls, filteredAttrs)
- return $ FigureSpec
- caption'
- withLinks'
- fullScript
- format
- dir
- dpi'
- lib
- tightBbox'
- transparent'
- blockAttrs'
+ ((liftIO . TIO.readFile) <$> includePath)
+ return $
+ FigureSpec
+ { caption = Map.findWithDefault mempty captionKey attrs'
+ , withLinks = fromMaybe (defaultWithLinks config) $ readBool <$> Map.lookup withLinksKey attrs'
+ , script = mconcat $ intersperse "\n" [header, includeScript, content]
+ , saveFormat = fromMaybe (defaultSaveFormat config) $ (fromString . unpack) <$> Map.lookup saveFormatKey attrs'
+ , directory = makeValid $ unpack $ Map.findWithDefault (pack $ defaultDirectory config) directoryKey attrs'
+ , dpi = fromMaybe (defaultDPI config) $ (read . unpack) <$> Map.lookup dpiKey attrs'
+ , renderingLib = lib
+ , tightBbox = isTightBbox config
+ , transparent = isTransparent config
+ , blockAttrs = (id', filter (\c -> c `notElem` ["pyplot", "plotly"]) cls, filteredAttrs)
+ }
parseFigureSpec _ = return Nothing
--- | Convert a FigureSpec to a Pandoc block component
+-- | Convert a @FigureSpec@ to a Pandoc block component.
+-- Note that the script to generate figure files must still
+-- be run in another function.
toImage :: FigureSpec -> Block
-toImage spec = head . toList $ para $ imageWith attrs' target' "fig:" caption'
+toImage spec = head . toList $ para $ imageWith attrs' (pack target') "fig:" caption'
-- To render images as figures with captions, the target title
-- must be "fig:"
-- Janky? yes
@@ -115,8 +109,8 @@ toImage spec = head . toList $ para $ imageWith attrs' target' "fig:" caption'
attrs' = blockAttrs spec
target' = figurePath spec
withLinks' = withLinks spec
- srcLink = link (replaceExtension target' ".txt") mempty "Source code"
- hiresLink = link (hiresFigurePath spec) mempty "high res."
+ srcLink = link (pack $ replaceExtension target' ".txt") mempty "Source code"
+ hiresLink = link (pack $ hiresFigurePath spec) mempty "high res."
captionText = fromList $ fromMaybe mempty (captionReader $ caption spec)
captionLinks = mconcat [" (", srcLink, ", ", hiresLink, ")"]
caption' = if withLinks' then captionText <> captionLinks else captionText
@@ -155,16 +149,17 @@ addPlotCapture spec = mconcat
, plotCapture (renderingLib spec) (hiresFigurePath spec) (minimum [200, 2 * dpi spec]) False (tight')
]
where
- tight' = if tightBbox spec then ("'tight'" :: T.Text) else ("None" :: T.Text)
+ tight' = if tightBbox spec then ("'tight'" :: Text) else ("None" :: Text)
-- Note that, especially for Windows, raw strings (r"...") must be used because path separators might
-- be interpreted as escape characters
plotCapture Matplotlib = captureMatplotlib
plotCapture Plotly = capturePlotly
-type Tight = T.Text
+type Tight = Text
+type DPI = Int
type IsTransparent = Bool
-type RenderingFunc = (FilePath -> Int -> IsTransparent -> Tight -> PythonScript)
+type RenderingFunc = (FilePath -> DPI -> IsTransparent -> Tight -> PythonScript)
-- | Capture plot from Matplotlib
@@ -176,14 +171,14 @@ import matplotlib.pyplot as plt
plt.savefig(r"#{fname'}", dpi=#{dpi'}, transparent=#{transparent''}, bbox_inches=#{tight'})
|]
where
- transparent'' :: T.Text
+ transparent'' :: Text
transparent'' = if transparent' then "True" else "False"
-- | Capture Plotly figure
--
--- We are trying to emulate the behavior of "matplotlib.pyplot.savefig" which
+-- We are trying to emulate the behavior of @matplotlib.pyplot.savefig@ which
-- knows the "current figure". This saves us from contraining users to always
--- have the same Plotly figure name, e.g. "fig" in all examples
+-- have the same Plotly figure name, e.g. @fig@ in all examples on plot.ly
capturePlotly :: RenderingFunc
capturePlotly fname' _ _ _ = [st|
import plotly.graph_objects as go
@@ -207,8 +202,8 @@ readerOptions = def
-- | Read a figure caption in Markdown format. LaTeX math @$...$@ is supported,
-- as are Markdown subscripts and superscripts.
-captionReader :: String -> Maybe [Inline]
-captionReader t = either (const Nothing) (Just . extractFromBlocks) $ runPure $ readMarkdown' (T.pack t)
+captionReader :: Text -> Maybe [Inline]
+captionReader t = either (const Nothing) (Just . extractFromBlocks) $ runPure $ readMarkdown' t
where
readMarkdown' = readMarkdown readerOptions
@@ -221,7 +216,7 @@ captionReader t = either (const Nothing) (Just . extractFromBlocks) $ runPure $
-- | Flexible boolean parsing
-readBool :: String -> Bool
+readBool :: Text -> Bool
readBool s | s `elem` ["True", "true", "'True'", "'true'", "1"] = True
| s `elem` ["False", "false", "'False'", "'false'", "0"] = False
- | otherwise = error $ mconcat ["Could not parse '", s, "' into a boolean. Please use 'True' or 'False'"]
+ | otherwise = error $ unpack $ mconcat ["Could not parse '", s, "' into a boolean. Please use 'True' or 'False'"]
diff --git a/src/Text/Pandoc/Filter/Pyplot/Scripting.hs b/src/Text/Pandoc/Filter/Pyplot/Scripting.hs
index 15d251c..0da2bb2 100644
--- a/src/Text/Pandoc/Filter/Pyplot/Scripting.hs
+++ b/src/Text/Pandoc/Filter/Pyplot/Scripting.hs
@@ -80,7 +80,7 @@ runTempPythonScript script' = case checkResult of
checkResult = mconcat $ scriptChecks <*> [script']
hashedPath = show . hash $ script'
-
+
-- | Run the Python script. In case the file already exists, we can safely assume
-- there is no need to re-run it.
runScriptIfNecessary :: FigureSpec
diff --git a/src/Text/Pandoc/Filter/Pyplot/Types.hs b/src/Text/Pandoc/Filter/Pyplot/Types.hs
index fd3eae0..af2571a 100644
--- a/src/Text/Pandoc/Filter/Pyplot/Types.hs
+++ b/src/Text/Pandoc/Filter/Pyplot/Types.hs
@@ -19,9 +19,11 @@ import Control.Monad.Reader
import Data.Char (toLower)
import Data.Default.Class (Default, def)
import Data.Hashable (Hashable)
+import Data.List (intersperse)
import Data.Semigroup as Sem
-import Data.Text (Text, pack)
-import Data.Yaml
+import Data.String (IsString(..))
+import Data.Text (Text)
+import Data.Yaml (ToJSON, object, toJSON, (.=))
import GHC.Generics (Generic)
@@ -29,7 +31,7 @@ import Text.Pandoc.Definition (Attr)
-- | Keys that pandoc-pyplot will look for in code blocks. These are only exported for testing purposes.
-directoryKey, captionKey, dpiKey, includePathKey, saveFormatKey, withLinksKey, isTightBboxKey, isTransparentKey :: String
+directoryKey, captionKey, dpiKey, includePathKey, saveFormatKey, withLinksKey, isTightBboxKey, isTransparentKey :: Text
directoryKey = "directory"
captionKey = "caption"
dpiKey = "dpi"
@@ -42,7 +44,7 @@ isTransparentKey = "transparent"
-- | list of all keys related to pandoc-pyplot that
-- can be specified in source material.
-inclusionKeys :: [String]
+inclusionKeys :: [Text]
inclusionKeys = [ directoryKey
, captionKey
, dpiKey
@@ -123,28 +125,25 @@ data SaveFormat
| TIF
deriving (Bounded, Enum, Eq, Show, Generic)
-instance Hashable SaveFormat -- From Generic
+instance IsString SaveFormat where
+ -- | An error is thrown if the save format cannot be parsed.
+ fromString s
+ | s `elem` ["png", "PNG", ".png"] = PNG
+ | s `elem` ["pdf", "PDF", ".pdf"] = PDF
+ | s `elem` ["svg", "SVG", ".svg"] = SVG
+ | s `elem` ["eps", "EPS", ".eps"] = EPS
+ | s `elem` ["gif", "GIF", ".gif"] = GIF
+ | s `elem` ["jpg", "jpeg", "JPG", "JPEG", ".jpg", ".jpeg"] = JPG
+ | s `elem` ["tif", "tiff", "TIF", "TIFF", ".tif", ".tiff"] = TIF
+ | otherwise = error $
+ mconcat [ s
+ , " is not one of valid save format : "
+ , mconcat $ intersperse ", " $ show <$> saveFormats
+ ]
+ where
+ saveFormats = (enumFromTo minBound maxBound) :: [SaveFormat]
--- | Parse an image save format string
---
--- >>> saveFormatFromString ".png"
--- Just PNG
---
--- >>> saveFormatFromString "jpeg"
--- Just JPEG
---
--- >>> SaveFormatFromString "arbitrary"
--- Nothing
-saveFormatFromString :: String -> Maybe SaveFormat
-saveFormatFromString s
- | s `elem` ["png", "PNG", ".png"] = Just PNG
- | s `elem` ["pdf", "PDF", ".pdf"] = Just PDF
- | s `elem` ["svg", "SVG", ".svg"] = Just SVG
- | s `elem` ["eps", "EPS", ".eps"] = Just EPS
- | s `elem` ["gif", "GIF", ".gif"] = Just GIF
- | s `elem` ["jpg", "jpeg", "JPG", "JPEG", ".jpg", ".jpeg"] = Just JPG
- | s `elem` ["tif", "tiff", "TIF", "TIFF", ".tif", ".tiff"] = Just TIF
- | otherwise = Nothing
+instance Hashable SaveFormat -- From Generic
-- | Save format file extension
extension :: SaveFormat -> String
@@ -199,15 +198,15 @@ instance ToJSON Configuration where
toJSON (Configuration dir' _ withLinks' savefmt' dpi' tightbbox' transparent' interp' flags') =
-- We ignore the include script as we want to examplify that
-- this is for a filepath
- object [ pack directoryKey .= dir'
- , pack includePathKey .= ("example.py" :: FilePath)
- , pack withLinksKey .= withLinks'
- , pack dpiKey .= dpi'
- , pack saveFormatKey .= (toLower <$> show savefmt')
- , pack isTightBboxKey .= tightbbox'
- , pack isTransparentKey .= transparent'
- , "interpreter" .= interp'
- , "flags" .= flags'
+ object [ directoryKey .= dir'
+ , includePathKey .= ("example.py" :: FilePath)
+ , withLinksKey .= withLinks'
+ , dpiKey .= dpi'
+ , saveFormatKey .= (fmap toLower . show $ savefmt')
+ , isTightBboxKey .= tightbbox'
+ , isTransparentKey .= transparent'
+ , "interpreter" .= interp'
+ , "flags" .= flags'
]
@@ -216,15 +215,15 @@ instance ToJSON Configuration where
-- It is assumed that once a @FigureSpec@ has been created, no configuration
-- can overload it; hence, a @FigureSpec@ completely encodes a particular figure.
data FigureSpec = FigureSpec
- { caption :: String -- ^ Figure caption.
+ { caption :: Text -- ^ Figure caption.
, withLinks :: Bool -- ^ Append links to source code and high-dpi figure to caption.
, script :: PythonScript -- ^ Source code for the figure.
, saveFormat :: SaveFormat -- ^ Save format of the figure.
, directory :: FilePath -- ^ Directory where to save the file.
- , dpi :: Int -- ^ Dots-per-inch of figure. This option only affects the Matplotlib backend.
+ , dpi :: Int -- ^ Dots-per-inch of figure.
, renderingLib :: RenderingLibrary -- ^ Rendering library.
- , tightBbox :: Bool -- ^ Enforce tight bounding-box with @bbox_inches="tight"@. This option only affects the Matplotlib backend.
- , transparent :: Bool -- ^ Make figure background transparent. This option only affects the Matplotlib backend.
+ , tightBbox :: Bool -- ^ Enforce tight bounding-box with @bbox_inches="tight"@.
+ , transparent :: Bool -- ^ Make figure background transparent.
, blockAttrs :: Attr -- ^ Attributes not related to @pandoc-pyplot@ will be propagated.
} deriving Generic