pandoc-pyplot 2.1.4.0 → 2.1.5.0
raw patch · 8 files changed
+154/−82 lines, 8 filessetup-changedPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
+ Text.Pandoc.Filter.Pyplot: [isTightBbox] :: Configuration -> Bool
+ Text.Pandoc.Filter.Pyplot: [isTransparent] :: Configuration -> Bool
+ Text.Pandoc.Filter.Pyplot.Internal: [isTightBbox] :: Configuration -> Bool
+ Text.Pandoc.Filter.Pyplot.Internal: [isTransparent] :: Configuration -> Bool
+ Text.Pandoc.Filter.Pyplot.Internal: [tightBbox] :: FigureSpec -> Bool
+ Text.Pandoc.Filter.Pyplot.Internal: [transparent] :: FigureSpec -> Bool
+ Text.Pandoc.Filter.Pyplot.Internal: isTightBboxKey :: String
+ Text.Pandoc.Filter.Pyplot.Internal: isTransparentKey :: String
- Text.Pandoc.Filter.Pyplot: Configuration :: FilePath -> PythonScript -> Bool -> SaveFormat -> Int -> String -> [String] -> Configuration
+ Text.Pandoc.Filter.Pyplot: Configuration :: FilePath -> PythonScript -> Bool -> SaveFormat -> Int -> Bool -> Bool -> String -> [String] -> Configuration
- Text.Pandoc.Filter.Pyplot.Internal: Configuration :: FilePath -> PythonScript -> Bool -> SaveFormat -> Int -> String -> [String] -> Configuration
+ Text.Pandoc.Filter.Pyplot.Internal: Configuration :: FilePath -> PythonScript -> Bool -> SaveFormat -> Int -> Bool -> Bool -> String -> [String] -> Configuration
- Text.Pandoc.Filter.Pyplot.Internal: FigureSpec :: String -> Bool -> PythonScript -> SaveFormat -> FilePath -> Int -> Attr -> FigureSpec
+ Text.Pandoc.Filter.Pyplot.Internal: FigureSpec :: String -> Bool -> PythonScript -> SaveFormat -> FilePath -> Int -> Bool -> Bool -> Attr -> FigureSpec
Files
- CHANGELOG.md +5/−0
- README.md +35/−1
- Setup.hs +7/−7
- pandoc-pyplot.cabal +1/−1
- src/Text/Pandoc/Filter/Pyplot/Configuration.hs +14/−25
- src/Text/Pandoc/Filter/Pyplot/FigureSpec.hs +31/−18
- src/Text/Pandoc/Filter/Pyplot/Types.hs +59/−30
- test/Main.hs +2/−0
CHANGELOG.md view
@@ -2,6 +2,11 @@ pandoc-pyplot uses [Semantic Versioning](http://semver.org/spec/v2.0.0.html) +Release 2.1.5.0 +--------------- + +* Added support for two new configuration values: `tight_bbox: true|false` and `transparent: true|false`. These values are only supported via configuration files `.pandoc-pyplot.yml`. + Release 2.1.4.0 ---------------
README.md view
@@ -6,13 +6,24 @@ `pandoc-pyplot` turns Python code present in your documents into embedded Matplotlib figures. * [Usage](#usage) - * [Markdown](#Markdown) + * [Markdown](#markdown) * [LaTeX](#latex) * [Examples](#examples) * [Features](#features) + * [Captions](#captions) + * [Link to source code and high-resolution + figure](#link-to-source-code-and-high-resolution-figure) + * [Including scripts](#including-scripts) + * [No wasted work](#no-wasted-work) + * [Compatibility with + pandoc-crossref](#compatibility-with-pandoc-crossref) + * [Configurable](#configurable) + * [Configuration-only parameters](#configuration-only-parameters) * [Installation](#installation) * [Running the filter](#running-the-filter) * [Usage as a Haskell library](#usage-as-a-haskell-library) + * [Usage with Hakyll](#usage-with-hakyll) +* [Warning](#warning) ## Usage @@ -164,6 +175,15 @@ This `include` parameter is perfect for longer documents with many plots. Simply define the style you want in a separate script! You can also import packages this way, or define functions you often use. +Customization of figures beyond what is available in `pandoc-pyplot` can also be done through the `include` script. For example, if you wanted to have transparent figures, you can do so via `matplotlib.pyplot.rcParams`: +```python +import matplotlib.pyplot as plt + +plt.rcParams['savefig.transparent'] = True +... +``` +You can take a look at all available `matplotlib` parameters [here](https://matplotlib.org/users/customizing.html). + ### No wasted work `pandoc-pyplot` minimizes work, only generating figures if it absolutely must. Therefore, you can confidently run the filter on very large documents containing dozens of figures --- like a book or a thesis --- and only the figures which have recently changed will be re-generated. @@ -200,6 +220,8 @@ format: jpeg links: false dpi: 150 +tight_bbox: true +transparent: false flags: [-O, -Wignore] ``` @@ -215,9 +237,21 @@ format: png links: true dpi: 80 +tight_bbox: false +transparent: false +flags: [] ``` Using `pandoc-pyplot --write-example-config` will write the default configuration to a file `.pandoc-pyplot.yml`, which you can then customize. + +#### Configuration-only parameters + +There are a few parameters that are __only__ available via the configuration file `.pandoc-pyplot.yml`: + +* `interpreter` is the name of the interpreter to use. For example, `interpreter: python36`; +* `flags` is a list of strings, which are flags that are passed to the python interpreter. For example, `flags: [-O, -Wignore]`; +* `tight_bbox` is a boolean that determines whether to use `bbox_inches="tight"` or not when saving Matplotlib figures. For example, `tight_bbox: true`. See [here](https://matplotlib.org/api/_as_gen/matplotlib.pyplot.savefig.html) for details; +* `transparent` is a boolean that determines whether to make figure background transparent or not. This is useful, for example, for displaying a plot on top of a colored background on a web page. For example, `transparent: true`. ## Installation
Setup.hs view
@@ -1,7 +1,7 @@--- This script is used to build and install your package. Typically you don't--- need to change it. The Cabal documentation has more information about this--- file: <https://www.haskell.org/cabal/users-guide/installing-packages.html>.-import qualified Distribution.Simple--main :: IO ()-main = Distribution.Simple.defaultMain+-- This script is used to build and install your package. Typically you don't +-- need to change it. The Cabal documentation has more information about this +-- file: <https://www.haskell.org/cabal/users-guide/installing-packages.html>. +import qualified Distribution.Simple + +main :: IO () +main = Distribution.Simple.defaultMain
pandoc-pyplot.cabal view
@@ -1,5 +1,5 @@ name: pandoc-pyplot -version: 2.1.4.0 +version: 2.1.5.0 cabal-version: >= 1.12 synopsis: A Pandoc filter to include figures generated from Python code blocks description: A Pandoc filter to include figures generated from Python code blocks. Keep the document and Python code in the same location. Output from Matplotlib is captured and included as a figure.
src/Text/Pandoc/Filter/Pyplot/Configuration.hs view
@@ -21,6 +21,8 @@ , includePathKey , saveFormatKey , withLinksKey + , isTightBboxKey + , isTransparentKey ) where import Data.Maybe (fromMaybe) @@ -34,25 +36,6 @@ import Text.Pandoc.Filter.Pyplot.Types --- | Keys that pandoc-pyplot will look for in code blocks. These are only exported for testing purposes. -directoryKey, captionKey, dpiKey, includePathKey, saveFormatKey, withLinksKey :: String -directoryKey = "directory" -captionKey = "caption" -dpiKey = "dpi" -includePathKey = "include" -saveFormatKey = "format" -withLinksKey = "links" - --- | list of all keys related to pandoc-pyplot. -inclusionKeys :: [String] -inclusionKeys = [ directoryKey - , captionKey - , dpiKey - , includePathKey - , saveFormatKey - , withLinksKey - ] - -- A @Configuration@ cannot be directly created from a YAML file -- for two reasons: -- @@ -69,19 +52,23 @@ , defaultWithLinks_ :: Bool , defaultSaveFormat_ :: String , defaultDPI_ :: Int + , tightBbox_ :: Bool + , transparent_ :: Bool , interpreter_ :: String , flags_ :: [String] } instance FromJSON ConfigPrecursor where parseJSON (Object v) = ConfigPrecursor - <$> v .:? (T.pack directoryKey) .!= (defaultDirectory def) + <$> 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 .:? "interpreter" .!= (interpreter def) - <*> v .:? "flags" .!= (flags def) + <*> 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) parseJSON _ = fail "Could not parse the configuration" @@ -94,6 +81,8 @@ , defaultSaveFormat = saveFormat' , defaultWithLinks = defaultWithLinks_ prec , defaultDPI = defaultDPI_ prec + , isTightBbox = tightBbox_ prec + , isTransparent = transparent_ prec , interpreter = interpreter_ prec , flags = flags_ prec }
src/Text/Pandoc/Filter/Pyplot/FigureSpec.hs view
@@ -50,7 +50,6 @@ import Text.Pandoc.Readers (readMarkdown) import Text.Pandoc.Filter.Pyplot.Types -import Text.Pandoc.Filter.Pyplot.Configuration -- | Determine inclusion specifications from Block attributes. @@ -67,18 +66,21 @@ figureSpec :: IO FigureSpec figureSpec = do includeScript <- fromMaybe (return $ defaultIncludeScript config) $ 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' - blockAttrs' = (id', filter (/= "pyplot") cls, filteredAttrs) - return $ FigureSpec caption' withLinks' fullScript format dir dpi' blockAttrs' + 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 (/= "pyplot") cls, filteredAttrs) + return $ FigureSpec caption' withLinks' fullScript format dir dpi' tightBbox' transparent' blockAttrs' parseFigureSpec _ _ = return Nothing + -- | Convert a FigureSpec to a Pandoc block component toImage :: FigureSpec -> Block toImage spec = head . toList $ para $ imageWith attrs' target' "fig:" caption' @@ -95,6 +97,7 @@ captionLinks = mconcat [" (", srcLink, ", ", hiresLink, ")"] caption' = if withLinks' then captionText <> captionLinks else captionText + -- | Determine the path a figure should have. figurePath :: FigureSpec -> FilePath figurePath spec = directory spec </> stem spec @@ -102,16 +105,19 @@ stem = flip addExtension ext . show . hash ext = extension . saveFormat $ spec + -- | Determine the path to the source code that generated the figure. sourceCodePath :: FigureSpec -> FilePath sourceCodePath = flip replaceExtension ".txt" . figurePath + -- | The path to the high-resolution figure. hiresFigurePath :: FigureSpec -> FilePath hiresFigurePath spec = flip replaceExtension (".hires" <> ext) . figurePath $ spec where ext = extension . saveFormat $ spec + -- | Modify a Python plotting script to save the figure to a filename. -- An additional file will also be captured. addPlotCapture :: FigureSpec -- ^ Path where to save the figure @@ -124,15 +130,21 @@ , plotCapture (hiresFigurePath spec) (minimum [200, 2 * dpi spec]) ] where - plotCapture fname' dpi' = - mconcat - [ "\nplt.savefig(" - , T.pack $ show fname' -- show is required for quotes - , ", dpi=" - , T.pack $ show dpi' - , ")" - ] + tight' = tightBbox spec + transparent' = transparent spec + plotCapture fname' dpi' = mconcat $ + [ "\nplt.savefig(" + , T.pack $ show fname' -- show is required for quotes + , ", dpi=" + , T.pack $ show dpi' + , ", transparent=" + , T.pack $ show transparent' + , if tight' + then ", bbox_inches=\"tight\")" + else ")" + ] + -- | Reader options for captions. readerOptions :: ReaderOptions readerOptions = def @@ -144,6 +156,7 @@ , Ext_raw_tex ] } + -- | Read a figure caption in Markdown format. LaTeX math @$...$@ is supported, -- as are Markdown subscripts and superscripts.
src/Text/Pandoc/Filter/Pyplot/Types.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE DeriveGeneric #-} {-| Module : Text.Pandoc.Filter.Pyplot.Types Copyright : (c) Laurent P René de Cotret, 2019 @@ -15,14 +16,41 @@ import Data.Char (toLower) import Data.Default.Class (Default, def) -import Data.Hashable (Hashable, hashWithSalt) +import Data.Hashable (Hashable) import Data.Semigroup as Sem -import Data.Text (Text) +import Data.Text (Text, pack) import Data.Yaml +import GHC.Generics (Generic) + 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 = "directory" +captionKey = "caption" +dpiKey = "dpi" +includePathKey = "include" +saveFormatKey = "format" +withLinksKey = "links" +isTightBboxKey = "tight_bbox" +isTransparentKey = "transparent" + +-- | list of all keys related to pandoc-pyplot that +-- can be specified in source material. +inclusionKeys :: [String] +inclusionKeys = [ directoryKey + , captionKey + , dpiKey + , includePathKey + , saveFormatKey + , withLinksKey + , isTightBboxKey + , isTransparentKey + ] + + -- | String representation of a Python script type PythonScript = Text @@ -73,8 +101,10 @@ | EPS | GIF | TIF - deriving (Bounded, Enum, Eq, Show) + deriving (Bounded, Enum, Eq, Show, Generic) +instance Hashable SaveFormat -- From Generic + -- | Parse an image save format string -- -- >>> saveFormatFromString ".png" @@ -125,6 +155,8 @@ , defaultWithLinks :: Bool -- ^ The default behavior of whether or not to include links to source code and high-res , defaultSaveFormat :: SaveFormat -- ^ The default save format of generated figures. , defaultDPI :: Int -- ^ The default dots-per-inch value for generated figures. + , isTightBbox :: Bool -- ^ Whether the figures should be saved with @bbox_inches="tight"@ or not. Useful for larger figures with subplots. + , isTransparent :: Bool -- ^ If True, figures will be saved with transparent background rather than solid color. , interpreter :: String -- ^ The name of the interpreter to use to render figures. , flags :: [String] -- ^ Command-line flags to be passed to the Python interpreger, e.g. ["-O", "-Wignore"] } @@ -137,22 +169,26 @@ , defaultWithLinks = True , defaultSaveFormat = PNG , defaultDPI = 80 + , isTightBbox = False + , isTransparent = False , interpreter = defaultPlatformInterpreter , flags = mempty } instance ToJSON Configuration where - toJSON (Configuration dir' _ withLinks' savefmt' dpi' interp' flags') = + 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 [ "directory" .= dir' - , "include" .= ("example.py" :: FilePath) - , "links" .= withLinks' - , "dpi" .= dpi' - , "format" .= (toLower <$> show savefmt') - , "interpreter" .= interp' - , "flags" .= flags' - ] + 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' + ] -- | Datatype containing all parameters required to run pandoc-pyplot. @@ -160,22 +196,15 @@ -- 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. - , 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 - , blockAttrs :: Attr -- ^ Attributes not related to @pandoc-pyplot@ will be propagated. - } + { caption :: String -- ^ 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. + , 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 -instance Hashable FigureSpec where - hashWithSalt salt spec = - -- Some things are not included in the hash because they do not affect the outcome - -- of running scripts, e.g. whether links should be shown or not. - hashWithSalt salt ( caption spec - , script spec - , fromEnum . saveFormat $ spec - , directory spec, dpi spec - , blockAttrs spec - ) +instance Hashable FigureSpec -- From Generic
test/Main.hs view
@@ -279,6 +279,8 @@ , defaultSaveFormat = JPG , defaultDPI = 150 , flags = ["-Wignore"] + , isTightBbox = True + , isTransparent = True } parsedConfig <- configuration "test/fixtures/.pandoc-pyplot.yml" assertEqual "" config parsedConfig