packages feed

pandoc-plot 0.5.0.0 → 0.6.0.0

raw patch · 23 files changed

+961/−311 lines, 23 filesdep ~pandocdep ~pandoc-typesPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: pandoc, pandoc-types

API changes (from Hackage documentation)

+ Text.Pandoc.Filter.Plot: [graphvizExe] :: Configuration -> !FilePath
+ Text.Pandoc.Filter.Plot: [graphvizPreamble] :: Configuration -> !Script
+ Text.Pandoc.Filter.Plot: [plotlyRExe] :: Configuration -> !FilePath
+ Text.Pandoc.Filter.Plot: [plotlyRPreamble] :: Configuration -> !Script
+ Text.Pandoc.Filter.Plot.Internal: Graphviz :: Toolkit
+ Text.Pandoc.Filter.Plot.Internal: OutputSpec :: Configuration -> FigureSpec -> FilePath -> FilePath -> OutputSpec
+ Text.Pandoc.Filter.Plot.Internal: PlotlyR :: Toolkit
+ Text.Pandoc.Filter.Plot.Internal: [graphvizExe] :: Configuration -> !FilePath
+ Text.Pandoc.Filter.Plot.Internal: [graphvizPreamble] :: Configuration -> !Script
+ Text.Pandoc.Filter.Plot.Internal: [oConfiguration] :: OutputSpec -> Configuration
+ Text.Pandoc.Filter.Plot.Internal: [oFigurePath] :: OutputSpec -> FilePath
+ Text.Pandoc.Filter.Plot.Internal: [oFigureSpec] :: OutputSpec -> FigureSpec
+ Text.Pandoc.Filter.Plot.Internal: [oScriptPath] :: OutputSpec -> FilePath
+ Text.Pandoc.Filter.Plot.Internal: [plotlyRExe] :: Configuration -> !FilePath
+ Text.Pandoc.Filter.Plot.Internal: [plotlyRPreamble] :: Configuration -> !Script
+ Text.Pandoc.Filter.Plot.Internal: configurationPathMeta :: Pandoc -> Maybe FilePath
+ Text.Pandoc.Filter.Plot.Internal: data OutputSpec
+ Text.Pandoc.Filter.Plot.Internal: executable :: Toolkit -> Configuration -> IO FilePath
+ Text.Pandoc.Filter.Plot.Internal: figureContentHash :: FigureSpec -> Int
- Text.Pandoc.Filter.Plot: Configuration :: !FilePath -> !Bool -> !Int -> !SaveFormat -> !Format -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !Bool -> !Bool -> Configuration
+ Text.Pandoc.Filter.Plot: Configuration :: !FilePath -> !Bool -> !Int -> !SaveFormat -> !Format -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !Bool -> !Bool -> Configuration
- Text.Pandoc.Filter.Plot.Internal: Configuration :: !FilePath -> !Bool -> !Int -> !SaveFormat -> !Format -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !Bool -> !Bool -> Configuration
+ Text.Pandoc.Filter.Plot.Internal: Configuration :: !FilePath -> !Bool -> !Int -> !SaveFormat -> !Format -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !Bool -> !Bool -> Configuration
- Text.Pandoc.Filter.Plot.Internal: command :: Toolkit -> Configuration -> FigureSpec -> FilePath -> Text
+ Text.Pandoc.Filter.Plot.Internal: command :: Toolkit -> OutputSpec -> IO Text

Files

CHANGELOG.md view
@@ -2,6 +2,36 @@ 
 pandoc-plot uses [Semantic Versioning](http://semver.org/spec/v2.0.0.html)
 
+Release 0.6.0.0
+--------------
+
+New toolkits:
+* Added support for the Plotly/R plotting library.
+* Added support for Graphviz.
+
+Other changes:
+* The determination of which figures to re-render or not has been improved. For example, changing the caption will not trigger a re-render of a figure anymore.
+* `pandoc-plot` will look for executables more thoroughly.
+* `pandoc-plot toolkits` will now show the exact executable that is being used, if possible.
+* Added a check when running the filter that the Pandoc version is at least 2.8. This is easier to understand that the default Pandoc warning on API incompatibility.
+* Added the ability to write the example configuration to an arbitrary file using `pandoc-plot write-example-config`.
+* Added the possibility to specify the configuration file via metadata. For example, in Markdown:
+
+    ```markdown
+    ---
+    title: My document
+    author: John Doe
+    plot-configuration: /path/to/file.yml
+    ---     
+    ```
+
+or on the command line:
+
+```bash
+pandoc --filter pandoc-plot -M plot-configuration=/path/to/file.yml ...
+```
+* Added the ability to specify configuration file to the `pandoc-plot clean` and `pandoc-plot toolkits` commands.
+
 Release 0.5.0.0
 ---------------
 
README.md view
@@ -1,39 +1,47 @@+<!--
+The file README.md is automatically generated by the tools/mkreadme script 
+Do not edit manually
+-->
+
 # pandoc-plot 
 
 ## A Pandoc filter to generate figures from code blocks in documents
 
-[![Hackage version](https://img.shields.io/hackage/v/pandoc-plot.svg)](http://hackage.haskell.org/package/pandoc-plot) [![Stackage version (nightly)](http://stackage.org/package/pandoc-plot/badge/nightly)](http://stackage.org/nightly/package/pandoc-plot) [![Build status](https://ci.appveyor.com/api/projects/status/mmgiuk52j356e6jp?svg=true)](https://ci.appveyor.com/project/LaurentRDC/pandoc-plot) [![Build Status](https://dev.azure.com/laurentdecotret/pandoc-plot/_apis/build/status/LaurentRDC.pandoc-plot?branchName=master)](https://dev.azure.com/laurentdecotret/pandoc-plot/_build/latest?definitionId=5&branchName=master) ![GitHub](https://img.shields.io/github/license/LaurentRDC/pandoc-plot) [![Conda Version](https://img.shields.io/conda/vn/conda-forge/pandoc-plot.svg)](https://anaconda.org/conda-forge/pandoc-plot)
+[![Hackage version](https://img.shields.io/hackage/v/pandoc-plot.svg)](http://hackage.haskell.org/package/pandoc-plot) [![Stackage version (nightly)](http://stackage.org/package/pandoc-plot/badge/nightly)](http://stackage.org/nightly/package/pandoc-plot) [![Build status](https://ci.appveyor.com/api/projects/status/mmgiuk52j356e6jp?svg=true)](https://ci.appveyor.com/project/LaurentRDC/pandoc-plot) [![Build Status](https://dev.azure.com/laurentdecotret/pandoc-plot/_apis/build/status/LaurentRDC.pandoc-plot?branchName=master)](https://dev.azure.com/laurentdecotret/pandoc-plot/_build/latest?definitionId=5&branchName=master) [![license](https://img.shields.io/badge/license-GPLv2+-lightgray.svg)](https://www.gnu.org/licenses/gpl.html) [![Conda Version](https://img.shields.io/conda/vn/conda-forge/pandoc-plot.svg)](https://anaconda.org/conda-forge/pandoc-plot)
 
 `pandoc-plot` turns code blocks present in your documents (Markdown, LaTeX, etc.) into embedded figures, using your plotting toolkit of choice, including Matplotlib, ggplot2, MATLAB, Mathematica, and more.
 
-## Table of content
-
-* [Overview](#overview)
-* [Supported toolkits](#supported-toolkits)
-* [Features](#features)
-    * [Captions](#captions)
-    * [Link to source code](#link-to-source-code)
-    * [Preamble scripts](#preamble-scripts)
-    * [Performance](#performance)
-    * [Compatibility with pandoc-crossref](#compatibility-with-pandoc-crossref)
-* [Configuration](#configuration)
-    * [Toolkit-specific options](#toolkit-specific-options)
-* [Detailed usage](#detailed-usage)
-    * [As a filter](#as-a-filter)
-    * [Cleaning output](#cleaning-output)
-    * [Configuration template](#configuration-template)
-    * [As a Haskell library](#as-a-haskell-library)
-        * [Usage with Hakyll](#usage-with-hakyll)
-* [Installation](#installation)
-* [Warning](#warning)
+  - [Overview](#overview)
+  - [Supported toolkits](#supported-toolkits)
+  - [Features](#features)
+      - [Captions](#captions)
+      - [Link to source code](#link-to-source-code)
+      - [Preamble scripts](#preamble-scripts)
+      - [Performance](#performance)
+      - [Compatibility with
+        pandoc-crossref](#compatibility-with-pandoc-crossref)
+  - [Detailed usage](#detailed-usage)
+      - [As a filter](#as-a-filter)
+      - [Parameters and options](#parameters-and-options)
+      - [Configuration](#configuration)
+      - [Other commands](#other-commands)
+  - [Installation](#installation)
+      - [Binaries and Installers](#binaries-and-installers)
+      - [conda](#conda)
+      - [From Hackage/Stackage](#from-hackagestackage)
+      - [From source](#from-source)
+  - [Warning](#warning)
 
 ## Overview
 
-This program is a [Pandoc](https://pandoc.org/) filter. It can therefore be used in the middle of conversion from input format to output format, replacing code blocks with figures.
+This program is a [Pandoc](https://pandoc.org/) filter. It can therefore
+be used in the middle of conversion from input format to output format,
+replacing code blocks with figures.
 
-The filter recognizes code blocks with classes that match plotting toolkits. For example, using the `matplotlib` toolkit:
+The filter recognizes code blocks with classes that match plotting
+toolkits. For example, using the `matplotlib` toolkit:
 
-~~~markdown
+```` markdown
 # My document
 
 This is a paragraph.
@@ -45,46 +53,60 @@ plt.plot([0,1,2,3,4], [1,2,3,4,5])
 plt.title('This is an example figure')
 ```
-~~~
+````
 
-Putting the above in `input.md`, we can then generate the plot and embed it in an HTML page:
+Putting the above in `input.md`, we can then generate the plot and embed
+it in an HTML page:
 
-```bash
+``` bash
 pandoc --filter pandoc-plot input.md --output output.html
 ```
 
-*Note that pandoc-plot only works with pandoc >= 2.8 because of some breaking changes in pandoc's API.*
+*Note that pandoc-plot only works with pandoc \>= 2.8 because of some
+breaking changes in pandoc’s API.*
 
 ## Supported toolkits
 
-`pandoc-plot` currently supports the following plotting toolkits (**installed separately**):
-
-* `matplotlib`: plots using the [matplotlib](https://matplotlib.org/) Python library;
-* `plotly_python` : plots using the [plotly](https://plot.ly/python/) Python library;
-* `matlabplot`: plots using [MATLAB](https://www.mathworks.com/);
-* `mathplot` : plots using [Mathematica](https://www.wolfram.com/mathematica/);
-* `octaveplot`: plots using [GNU Octave](https://www.gnu.org/software/octave/);
-* `ggplot2`: plots using [ggplot2](https://ggplot2.tidyverse.org/);
-* `gnuplot`: plots using [gnuplot](http://www.gnuplot.info/);
+`pandoc-plot` currently supports the following plotting toolkits
+(**installed separately**):
 
+  - `matplotlib`: plots using the [matplotlib](https://matplotlib.org/)
+    Python library;
+  - `plotly_python` : plots using the
+    [plotly](https://plotly.com/python/) Python library;
+  - `plotly_r`: plots using the [plotly](https://plotly.com/r/) R
+    library
+  - `matlabplot`: plots using [MATLAB](https://www.mathworks.com/);
+  - `mathplot` : plots using
+    [Mathematica](https://www.wolfram.com/mathematica/);
+  - `octaveplot`: plots using [GNU
+    Octave](https://www.gnu.org/software/octave/);
+  - `ggplot2`: plots using [ggplot2](https://ggplot2.tidyverse.org/);
+  - `gnuplot`: plots using [gnuplot](http://www.gnuplot.info/);
+  - `graphviz`: graphs using the [Graphviz](http://graphviz.org/)
 
-To know which toolkits are useable on *your machine* (and which ones are not available), you can check with the `--toolkits/-t` flag:
+To know which toolkits are useable on *your machine* (and which ones are
+not available), you can check with the `toolkits` command:
 
-```bash
-pandoc-plot --toolkits
+``` bash
+pandoc-plot toolkits
 ```
 
-**Wish your plotting toolkit of choice was available? Please [raise an issue](https://github.com/LaurentRDC/pandoc-plot/issues)!**
+The `toolkits` command is described in its own section below.
 
+**Wish your plotting toolkit of choice was available? Please [raise an
+issue](https://github.com/LaurentRDC/pandoc-plot/issues)\!**
+
 ## Features
 
 ### Captions
 
-You can also specify a caption for your image. This is done using the optional `caption` parameter.
+You can also specify a caption for your image. This is done using the
+optional `caption` parameter.
 
-__Markdown__:
+**Markdown**:
 
-~~~markdown
+```` markdown
 ```{.matlabplot caption="This is a simple figure with a **Markdown** caption"}
 x  = 0: .1 : 2*pi;
 y1 = cos(x);
@@ -93,11 +115,11 @@ figure
 plot(x, y1, 'b', x, y2, 'r-.', 'LineWidth', 2)
 ```
-~~~
+````
 
-__LaTex__:
+**LaTex**:
 
-```latex
+``` latex
 \begin{minted}[caption=This is a simple figure with a caption]{matlabplot}
 x  = 0: .1 : 2*pi;
 y1 = cos(x);
@@ -108,25 +130,30 @@ \end{minted}
 ```
 
-Caption formatting unfortunately cannot be determined automatically. To specify a caption format (e.g. "markdown", "LaTeX", etc.), see [Configuration](#configuration).
+Caption formatting unfortunately cannot be determined automatically. To
+specify a caption format (e.g. “markdown”, “LaTeX”, etc.), see
+[Configuration](#configuration).
 
 ### Link to source code
 
-In case of an output format that supports links (e.g. HTML), the embedded image generated by `pandoc-plot` can show a link to the source code which was used to generate the file. Therefore, other people can see what code was used to create your figures. 
+In case of an output format that supports links (e.g. HTML), the
+embedded image generated by `pandoc-plot` can show a link to the source
+code which was used to generate the file. Therefore, other people can
+see what code was used to create your figures.
 
 You can turn this on via the `source=true` key:
 
-__Markdown__:
+**Markdown**:
 
-~~~markdown
+```` markdown
 ```{.mathplot source=true}
 ...
 ```
-~~~
+````
 
-__LaTex__:
+**LaTex**:
 
-```latex
+``` latex
 \begin{minted}[source=true]{mathplot}
 ...
 \end{minted}
@@ -136,26 +163,30 @@ 
 ### Preamble scripts
 
-If you find yourself always repeating some steps, inclusion of scripts is possible using the `preamble` parameter. For example, if you want all Matplotlib plots to have the [`ggplot`](https://matplotlib.org/tutorials/introductory/customizing.html#sphx-glr-tutorials-introductory-customizing-py) style, you can write a very short preamble `style.py` like so:
+If you find yourself always repeating some steps, inclusion of scripts
+is possible using the `preamble` parameter. For example, if you want all
+Matplotlib plots to have the
+[`ggplot`](https://matplotlib.org/tutorials/introductory/customizing.html#sphx-glr-tutorials-introductory-customizing-py)
+style, you can write a very short preamble `style.py` like so:
 
-```python
+``` python
 import matplotlib.pyplot as plt
 plt.style.use('ggplot')
 ```
 
 and include it in your document as follows:
 
-~~~markdown
+```` markdown
 ```{.matplotlib preamble=style.py}
 plt.figure()
 plt.plot([0,1,2,3,4], [1,2,3,4,5])
 plt.title('This is an example figure')
 ```
-~~~
+````
 
 Which is equivalent to writing the following markdown:
 
-~~~markdown
+```` markdown
 ```{.matplotlib}
 import matplotlib.pyplot as plt
 plt.style.use('ggplot')
@@ -164,256 +195,464 @@ plt.plot([0,1,2,3,4], [1,2,3,4,5])
 plt.title('This is an example figure')
 ```
-~~~
+````
 
 The equivalent LaTeX usage is as follows:
 
-```latex
+``` latex
 \begin{minted}[include=style.py]{matplotlib}
 
 \end{minted}
 ```
 
-This `preamble` 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.
+This `preamble` 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.
 
 ### Performance
 
-`pandoc-plot` minimizes work, only generating figures if it absolutely must, i.e. if the content has changed. `pandoc-plot` will save the hash of the source code used to generate a figure in its filename. Before generating a figure, `pandoc-plot` will check it this figure already exists based on the hash of its source! This also means that there is no way to directly name figures.
+`pandoc-plot` minimizes work, only generating figures if it absolutely
+must, i.e. if the content has changed. `pandoc-plot` will save the hash
+of the source code used to generate a figure in its filename. Before
+generating a figure, `pandoc-plot` will check it this figure already
+exists based on the hash of its source\! This also means that there is
+no way to directly name figures.
 
-Moreover, starting with version 0.5.0.0, `pandoc-plot` takes advantage of multicore CPUs, rendering figures **in parallel**.
+Moreover, starting with version 0.5.0.0, `pandoc-plot` takes advantage
+of multicore CPUs, rendering figures **in parallel**.
 
-Therefore, you can confidently run the filter on very large documents containing hundreds of figures, like a book or a thesis.
+Therefore, you can confidently run the filter on very large documents
+containing hundreds of figures, like a book or a thesis.
 
 ### Compatibility with pandoc-crossref
 
-[`pandoc-crossref`](https://github.com/lierdakil/pandoc-crossref) is a pandoc filter that makes it effortless to cross-reference objects in Markdown documents. 
+[`pandoc-crossref`](https://github.com/lierdakil/pandoc-crossref) is a
+pandoc filter that makes it effortless to cross-reference objects in
+Markdown documents.
 
-You can use `pandoc-crossref` in conjunction with `pandoc-plot` for the ultimate figure-making pipeline. You can combine both in a figure like so:
+You can use `pandoc-crossref` in conjunction with `pandoc-plot` for the
+ultimate figure-making pipeline. You can combine both in a figure like
+so:
 
-~~~markdown
+```` markdown
 ```{#fig:myexample .plotly_python caption="This is a caption"}
 # Insert figure script here
 ```
 
 As you can see in @fig:myexample, ...
-~~~
+````
 
-If the above source is located in file `myfile.md`, you can render the figure and references by applying `pandoc-plot` **first**, and then `pandoc-crossref`. For example:
+If the above source is located in file `myfile.md`, you can render the
+figure and references by applying `pandoc-plot` **first**, and then
+`pandoc-crossref`. For example:
 
-```bash
+``` bash
 pandoc --filter pandoc-plot --filter pandoc-crossref -i myfile.md -o myfile.html
 ```
 
-## Configuration
+## Detailed usage
 
-To avoid repetition, `pandoc-plot` can be configured using simple YAML files. `pandoc-plot` will look for a `.pandoc-plot.yml` file in the current working directory. Here are **all** the possible parameters:
+`pandoc-plot` is a command line executable with a few functions. You can
+take a look at the help using the `-h`/`--help` flag:
 
-```yaml
+``` bash
+pandoc-plot - generate figures directly in documents using your plotting toolkit
+of choice.
+
+Usage: pandoc-plot.exe ([-v|--version] | [--full-version] | [-m|--manual])
+                       [COMMAND] [AST]
+  This pandoc filter generates plots from code blocks using a multitude of
+  possible renderers. This allows to keep documentation and figures in perfect
+  synchronicity.
+
+Available options:
+  -v,--version             Show version number and exit.
+  --full-version           Show full version information and exit.
+  -m,--manual              Open the manual page in the default web browser and
+                           exit.
+  -h,--help                Show this help text
+
+Available commands:
+  toolkits                 Show information on toolkits and exit.
+  clean                    Clean output directories where figures from FILE
+                           might be stored. WARNING: All files in those
+                           directories will be deleted.
+  write-example-config     Write example configuration to a file and exit.
+
+More information can be found via the manual (pandoc-plot --manual) or the repository README, located at
+    https://github.com/LaurentRDC/pandoc-plot
+
+```
+
+### As a filter
+
+The most common use for `pandoc-plot` is as a pandoc filter, in which
+case it should be called without arguments. For example:
+
+``` bash
+pandoc --filter pandoc-plot -i input.md -o output.html
+```
+
+If `pandoc-plot` fails to render a code block into a figure, the
+filtering will not stop. Your code blocks will stay unchanged.
+
+You can chain other filters with it (e.g.,
+[`pandoc-crossref`](https://github.com/lierdakil/pandoc-crossref)) like
+so:
+
+``` bash
+pandoc --filter pandoc-plot --filter pandoc-crossref -i input.md -o output.html
+```
+
+### Parameters and options
+
+`pandoc-plot` looks for code blocks with a specific class, depending on
+the toolkit you want to use. `pandoc-plot` will run the code and capture
+the figure output. There can only be **one** figure per code block.
+
+There are parameters that affect the figure that will be included in
+your document. Here are all the possible general parameters:
+
+```` markdown
+  ```{.cls directory=(path) caption=(text) format=(PNG|PDF|SVG|JPG|EPS|GIF|TIF|WEBP) source=(true|false) preamble=(path) dpi=(integer) executable=(path) caption_format=(text)}
+  # script content
+  ```
+````
+
+  - `cls` must be one of the following: `matplotlib`, `matlabplot`,
+    `plotly_python`, `plotly_r`, `mathplot`, `octaveplot`, `ggplot2`,
+    `gnuplot`
+
+All following parameters are optional, with their default values
+controlled by the [configuration](#configuration)
+
+  - `directory` is a path to the directory where the figure and source
+    code will be saved. You cannot control the file name. This path is
+    either absolute, or relative from the working directory where you
+    call `pandoc-plot`.
+  - `caption` is the caption text. The format of the caption is
+    specified in the `caption_format` parameter, described below.
+  - `format` is the desired filetype for the resulting figure. Possible
+    values for `format` are \[`PNG`, `PDF`, `SVG`, `JPG`, `EPS`, `GIF`,
+    `TIF`, `WEBP`\]. Not all toolkits support all formats. See
+    `pandoc-plot toolkits` for toolkit-specific information regarding
+    save formats.
+  - `source` is a boolean toggle that determines whether the source code
+    should be linked in the caption or not. Possible values are
+    \[`true`, `True`, `false`, `False`\].
+  - `preamble` is a path to a script that will be included as a preamble
+    to the content of the code block. This path is either absolute, or
+    relative from the working directory where you call `pandoc-plot`.
+  - `dpi` is the pixel density of the figure in dots-per-inch. Possible
+    values are positive integers. Not all toolkits respect this.
+  - `executable` is a path to the executable to use
+    (e.g. `C:\\python3.exe`) or the name of the executable
+    (e.g. `python3`).
+  - `caption_format` is the text format of the caption. Possible values
+    are exactly the same as `pandoc`’s format specification, usually
+    `FORMAT+EXTENSION-EXTENSION`. For example, captions in Markdown with
+    raw LaTeX would be parsed correctly provided that
+    `caption_format=markdown+raw_tex`. See Pandoc’s guide on [Specifying
+    formats](https://pandoc.org/MANUAL.html#specifying-formats).
+
+#### Code highlighting
+
+If your editor supports code highlighting in code blocks, you can also
+include the programming language. In Markdown:
+
+```` markdown
+  ```{.language .cls (options)}
+  # script content
+  ```
+````
+
+For example, for Matplotlib plots:
+
+```` markdown
+  ```{.python .matplotlib}
+  # script content
+  ```
+````
+
+or for GGPlot2 figures:
+
+```` markdown
+  ```{.r .ggplot2}
+  # script content
+  ```
+````
+
+This way, you benefit from code highlighting *and* `pandoc-plot`.
+
+### Configuration
+
+To avoid repetition, `pandoc-plot` can be configured using simple YAML
+files. Here are **all** the possible parameters:
+
+``` yaml
+
+# This is an example configuration. Everything in this file is optional.
+# Please refer to the documentation to know about the parameters herein.
+#
+# The `executable` parameter for all toolkits can be either the
+# executable name (if it is present on the PATH), or
+# the full path to the executable.
+# E.g.:
+#  executable: python3
+#  executable: "C:\Python37\Scripts\python.exe"
+#
+# Note that this file should be re-named to ".pandoc-plot.yml" before pandoc-plot 
+# notices it.
+
 # The following parameters affect all toolkits
+# Directory where to save the plots. The path can be relative to this file, or absolute.
 directory: plots/
+
+# Whether or not to include a link to the source script in the caption. 
+# Particularly useful for HTML output.
 source: false
+
+# Default density of figures in dots per inches (DPI). 
+# This can be changed in the document specifically as well.
 dpi: 80
+
+# Default format in which to save the figures. This can be specified individually as well.
 format: PNG
+
+# Text format for the captions. Unfortunately, there is no way to detect this automatically.
+# You can use the same notation as Pandoc's --from parameter, specified here:
+# https://pandoc.org/MANUAL.html#option--from
+# Example: markdown, rst+raw_tex
 caption_format: markdown+tex_math_dollars
 
 # The possible parameters for the Matplotlib toolkit
 matplotlib:
-  preamble: matplotlib.py
+  # preamble: matplotlib.py
   tight_bbox: false
   transparent: false
   executable: python
 
 # The possible parameters for the MATLAB toolkit
 matlabplot:
-  preamble: matlab.m
+  # preamble: matlab.m
   executable: matlab
 
 # The possible parameters for the Plotly/Python toolkit
 plotly_python:
-  preamble: plotly-python.py
+  # preamble: plotly-python.py
   executable: python
 
+# The possible parameters for the Plotly/R toolkit
+plotly_r:
+  # preamble: plotly-r.r
+  executable: Rscript
+
 # The possible parameters for the Mathematica toolkit
 mathplot:
-  preamble: mathematica.m
+  # preamble: mathematica.m
   executable: math
 
 # The possible parameters for the GNU Octave toolkit
 octaveplot:
-  preamble: octave.m
+  # preamble: octave.m
   executable: octave
 
 # The possible parameters for the ggplot2 toolkit
 ggplot2:
-  preamble: ggplot2.r
+  # preamble: ggplot2.r
   executable: Rscript
 
 # The possible parameters for the gnuplot toolkit
 gnuplot:
-  preamble: gnuplot.gp
+  # preamble: gnuplot.gp
   executable: gnuplot
 
+# The possible parameters for the graphviz toolkit
+graphviz:
+  # preamble: graphviz.dot
+  executable: dot
 ```
 
-A file like the above sets the **default** values; you can still override them in documents directly.
+A file like the above sets the **default** values; you can still
+override them in documents directly.
 
-Using `pandoc-plot write-example-config` will write the default configuration to a file which you can then customize.
+The easiest way to specify configuration for `pandoc-plot` is to place a
+`.pandoc-plot.yml` file in the current working directory. You can also
+specify a configuration file in document metadata, under the
+`plot-configuration` key. For example, in Markdown:
 
-### Executables
+``` markdown
+---
+title: My document
+author: John Doe
+plot-configuration: /path/to/file.yml
+---
+```
 
-The `executable` parameter for all toolkits can be either the executable name (if it is present on the PATH), or the full path to the executable.
+or on the command line, using pandoc’s `-M/--metadata` flag:
 
+``` bash
+pandoc --filter pandoc-plot -M plot-configuration=/path/to/file.yml ...
+```
+
+The hierarchy of configuration files is as follows:
+
+1.  A configuration file specified in the metadata under the
+    `plot-configuration` key;
+2.  Otherwise, a file in the current working directory named
+    `.pandoc-plot.yml`;
+3.  Finally, the default configuration is used.
+
+#### Executables
+
+The `executable` parameter for all toolkits can be either the executable
+name (if it is present on the PATH), or the full path to the executable.
+
 Examples:
 
-```yaml
+``` yaml
 matplotlib:
   executable: python3
 ```
 
-```yaml
+``` yaml
 matlabplot:
   executable: "C:\Program Files\Matlab\R2019b\bin\matlab.exe"
 ```
 
-### Toolkit-specific options
+#### Toolkit-specific options
 
-#### Matplotlib
+##### Matplotlib
 
-* `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 Matplotlib figure background transparent or not. This is useful, for example, for displaying a plot on top of a colored background on a web page. High-resolution figures are not affected. For example, `transparent: true`.
+  - `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
+    Matplotlib figure background transparent or not. This is useful, for
+    example, for displaying a plot on top of a colored background on a
+    web page. High-resolution figures are not affected. For example,
+    `transparent: true`.
 
-## Detailed usage
+### Other commands
 
-`pandoc-plot` is a command line executable with a few functions. You can take a look at the help using the `-h`/`--help` flag:
+#### Finding installed toolkits
 
-```bash
-$ pandoc-plot --help
-pandoc-plot - generate figures directly in documents using your plotting toolkit
-of choice.
+You can determine which toolkits are available on your current machine
+using the `pandoc-plot toolkits` command. Here is the full help text:
 
-Usage: pandoc-plot.exe ([-v|--version] | [--full-version] | [-m|--manual] |   
-                       [-t|--toolkits]) [COMMAND] [AST]
-  This pandoc filter generates plots from code blocks using a multitude of    
-  possible renderers. This allows to keep documentation and figures in perfect
-  synchronicity.
+``` bash
+Usage: pandoc-plot.exe toolkits [--config PATH]
+  Show information on toolkits and exit.
 
 Available options:
-  -v,--version             Show version number and exit.
-  --full-version           Show full version information and exit.
-  -m,--manual              Open the manual page in the default web browser and
-                           exit.
-  -t,--toolkits            Show information on toolkits and exit. Executables
-                           from the configuration file will be used, if a
-                           '.pandoc-plot.yml' file is in the current directory.
+  --config PATH            Path to optional configuration file.
   -h,--help                Show this help text
-
-Available commands:
-  clean                    Clean output directories where figures from FILE
-                           might be stored. WARNING: All files in those
-                           directories will be deleted.
-  write-example-config     Write example configuration to a file.
-
-More information can be found via the manual (pandoc-plot --manual) or the repository README, located at
-    https://github.com/LaurentRDC/pandoc-plot
 ```
 
-### As a filter
+#### Cleaning output
 
-The most common use for `pandoc-plot` is as a pandoc filter, in which case it should be called without arguments. For example:
+Figures produced by `pandoc-plot` can be placed in a few different
+locations. You can set a default location in the
+[Configuration](#configuration), but you can also re-direct specific
+figures in other directories if you use the `directory=...` argument in
+code blocks. These figures will build up over time. You can use the
+`clean` command to scan documents and delete the associated
+`pandoc-plot` output files. For example, to delete the figures generated
+from the `input.md` file:
 
-```bash
-pandoc --filter pandoc-plot -i input.md -o output.html
+``` bash
+pandoc-plot clean input.md
 ```
 
-If `pandoc-plot` fails to render a code block into a figure, the filtering will not stop. Your code blocks will stay unchanged.
-
-You can chain other filters with it (e.g., [`pandoc-crossref`](https://github.com/lierdakil/pandoc-crossref)) like so:
-
-```bash
-pandoc --filter pandoc-plot --filter pandoc-crossref -i input.md -o output.html
-```
+This sill remove all directories where a figure *could* have been
+placed. **WARNING**: all files will be removed.
 
-### Cleaning output
+Here is the full help text for the `clean` command:
 
-Figures produced by `pandoc-plot` can be placed in a few different locations. You can set a default location in the [Configuration](#configuration), but you can also re-direct specific figures in other directories if you use the `directory=...` argument in code blocks. These figures will build up over time. You can use the `clean` command to scan documents and delete the associated `pandoc-plot` output files. For example, to delete the figures generated from the `input.md` file:
+``` bash
+Usage: pandoc-plot.exe clean [--config PATH] FILE
+  Clean output directories where figures from FILE might be stored. WARNING: All
+  files in those directories will be deleted.
 
-```bash
-pandoc-plot clean input.md
+Available options:
+  --config PATH            Path to optional configuration file.
+  -h,--help                Show this help text
 ```
 
-This sill remove all directories where a figure *could* have been placed. **WARNING**: all files will be removed.
-
-### Configuration template
+#### Configuration template
 
-Because `pandoc-plot` supports a few toolkits, there are a lot of configuration options. Don't start from scratch! The `write-example-config` command will create a file for you, which you can then modify:
+Because `pandoc-plot` supports a few toolkits, there are a lot of
+configuration options. Don’t start from scratch\! The
+`write-example-config` command will create a file for you, which you can
+then modify:
 
-```bash
+``` bash
 pandoc-plot write-example-config
 ```
 
-You will need to re-name the file to `.pandoc-ploy.yml` to be able to use it, so don't worry about overwriting your own configuration.
-
-### As a Haskell library
-
-To include the functionality of `pandoc-plot` in a Haskell package, you can use the `makePlot` function (for single blocks) or `plotTransform` function (for entire documents). [Take a look at the documentation on Hackage](https://hackage.haskell.org/package/pandoc-plot).
-
-#### Usage with Hakyll
-
-In case you want to use the filter with your own Hakyll setup, you can use a transform function that works on entire documents:
+You will need to re-name the file to `.pandoc-ploy.yml` to be able to
+use it, so don’t worry about overwriting your own configuration.
 
-```haskell
-import Text.Pandoc.Filter.Plot (plotTransform)
+Here is the full help text for the `write-example-config` command:
 
-import Data.Default (def) -- From data-default package, for default configuration
-import Hakyll
+``` bash
+Usage: pandoc-plot.exe write-example-config [--path FILE]
+  Write example configuration to a file and exit.
 
--- Unsafe compiler is required because of the interaction
--- in IO (i.e. running an external script).
-makePlotPandocCompiler :: Compiler (Item String)
-makePlotPandocCompiler = 
-  pandocCompilerWithTransformM
-    defaultHakyllReaderOptions
-    defaultHakyllWriterOptions
-    (unsafeCompiler . plotTransform def) -- default configuration
+Available options:
+  --path FILE              Target location of the configuration file. Default is
+                           ".example-pandoc-plot.yml"
+  -h,--help                Show this help text
 ```
 
 ## Installation
 
 ### Binaries and Installers
 
-Windows, Linux, and Mac OS binaries are available on the [GitHub release page](https://github.com/LaurentRDC/pandoc-plot/releases). There are also Windows installers.
+Windows, Linux, and Mac OS binaries are available on the [GitHub release
+page](https://github.com/LaurentRDC/pandoc-plot/releases). There are
+also Windows installers.
 
 ### conda
 
-Like `pandoc`, `pandoc-plot` is available as a package installable with [`conda`](https://docs.conda.io/en/latest/). [Click here to see the package page](https://anaconda.org/conda-forge/pandoc-plot).
+Like `pandoc`, `pandoc-plot` is available as a package installable with
+[`conda`](https://docs.conda.io/en/latest/). [Click here to see the
+package page](https://anaconda.org/conda-forge/pandoc-plot).
 
 To install in the current environment:
 
-```sh
+``` sh
 conda install -c conda-forge pandoc-plot
 ```
 
 ### From Hackage/Stackage
 
-`pandoc-plot` is available on [Hackage](http://hackage.haskell.org/package/pandoc-plot) and [Stackage](https://www.stackage.org/nightly/package/pandoc-plot). Using the [`cabal-install`](https://www.haskell.org/cabal/) tool:
+`pandoc-plot` is available on
+[Hackage](http://hackage.haskell.org/package/pandoc-plot) and
+[Stackage](https://www.stackage.org/nightly/package/pandoc-plot). Using
+the [`cabal-install`](https://www.haskell.org/cabal/) tool:
 
-```bash
+``` bash
 cabal update
 cabal install pandoc-plot
 ```
 
 or
 
-```bash
+``` bash
 stack update
 stack install pandoc-plot
 ```
 
 ### From source
 
-Building from source can be done using [`stack`](https://docs.haskellstack.org/en/stable/README/) or [`cabal`](https://www.haskell.org/cabal/):
+Building from source can be done using
+[`stack`](https://docs.haskellstack.org/en/stable/README/) or
+[`cabal`](https://www.haskell.org/cabal/):
 
-```bash
+``` bash
 git clone https://github.com/LaurentRDC/pandoc-plot
 cd pandoc-plot
 stack install # Alternatively, `cabal install`
@@ -421,4 +660,5 @@ 
 ## Warning
 
-Do not run this filter on unknown documents. There is nothing in `pandoc-plot` that can stop a script from performing **evil actions**.
+Do not run this filter on unknown documents. There is nothing in
+`pandoc-plot` that can stop a script from performing **evil actions**.
example-config.yml view
@@ -50,6 +50,11 @@   # preamble: plotly-python.py
   executable: python
 
+# The possible parameters for the Plotly/R toolkit
+plotly_r:
+  # preamble: plotly-r.r
+  executable: Rscript
+
 # The possible parameters for the Mathematica toolkit
 mathplot:
   # preamble: mathematica.m
@@ -69,3 +74,8 @@ gnuplot:
   # preamble: gnuplot.gp
   executable: gnuplot
+
+# The possible parameters for the graphviz toolkit
+graphviz:
+  # preamble: graphviz.dot
+  executable: dot
executable/Main.hs view
@@ -6,11 +6,12 @@ module Main where
 
 import           Control.Applicative              ((<|>))
-import           Control.Monad                    (join, forM_)
+import           Control.Monad                    (join, forM_, when, msum)
 
 import           Data.List                        (intersperse, (\\))
 import           Data.Monoid                      ((<>))
 import           Data.Text                        (unpack)
+import           Data.Version                     (parseVersion, showVersion)
 
 import           GitHash                          as Git
 
@@ -18,6 +19,8 @@ import qualified Options.Applicative.Help.Pretty  as P
 
 import           System.Directory                 (doesFileExist)
+import           System.Environment               (lookupEnv)
+import           System.IO                        (hPutStrLn, stderr)
 import           System.IO.Temp                   (writeSystemTempFile)
 
 import           Text.Pandoc.Filter.Plot          (availableToolkits,
@@ -27,12 +30,16 @@                                                    Configuration(..))
 import           Text.Pandoc.Filter.Plot.Internal (cls, supportedSaveFormats, 
                                                    toolkits, readDoc, 
-                                                   cleanOutputDirs, )
+                                                   cleanOutputDirs, 
+                                                   configurationPathMeta,
+                                                   executable)
 
 import           Text.Pandoc                      (pandocVersion)
 import           Text.Pandoc.Definition           (pandocTypesVersion)
 import           Text.Pandoc.JSON                 (toJSONFilter)
 
+import           Text.ParserCombinators.ReadP     (readP_to_S)
+
 import           Web.Browser                      (openBrowser)
 
 import qualified Data.Version                     as V
@@ -41,22 +48,16 @@ import           ManPage                          (embedManualHtml)
 import           ExampleConfig                    (embedExampleConfig)
 
--- It is understood that Opts Nothing Nothing should be used for filtering
-data Opts = Opts
-    { optCommand :: Maybe Command
-    , optFlag :: Maybe Flag
-    }
-
--- The difference between commands and flags is that commands perform actions,
--- while flags only display information.
+-- The difference between commands and flags is that commands require knowledge of
+-- the configuration, while flags only display static information.
 
-data Command = Clean FilePath
-             | WriteConfig
+data Command = Clean (Maybe FilePath) FilePath
+             | WriteConfig FilePath
+             | Toolkits (Maybe FilePath)
 
 data Flag = Version
           | FullVersion
           | Manual
-          | Toolkits
     deriving (Eq)
 
 
@@ -81,16 +82,16 @@             return $ go flag_ command_ input
         
         go :: Maybe Flag -> Maybe Command -> Maybe String -> IO ()
-        go (Just Version)     _ _ = putStrLn (V.showVersion version)
-        go (Just FullVersion) _ _ = showFullVersion
-        go (Just Manual)      _ _ = showManPage
-        go (Just Toolkits)    _ _ = showAvailableToolkits
-        go _ (Just (Clean fp))  _ = clean fp
-        go _ (Just WriteConfig) _ = writeFile ".example-pandoc-plot.yml" $(embedExampleConfig)
-        go Nothing Nothing      _ = toJSONFilterWithConfig
+        go (Just Version)          _ _ = putStrLn (V.showVersion version)
+        go (Just FullVersion)      _ _ = showFullVersion
+        go (Just Manual)           _ _ = showManPage
+        go _ (Just (Toolkits mfp))   _ = showAvailableToolkits mfp
+        go _ (Just (Clean mfp fp))   _ = clean mfp fp
+        go _ (Just (WriteConfig fp)) _ = writeFile fp $(embedExampleConfig)
+        go Nothing Nothing           _ = toJSONFilterWithConfig
 
 flagParser :: Parser (Maybe Flag)
-flagParser = versionP <|> fullVersionP <|> manualP <|> toolkitsP
+flagParser = versionP <|> fullVersionP <|> manualP
     where
         versionP = flag Nothing (Just Version) (mconcat
             [ long "version"
@@ -109,41 +110,85 @@             , help "Open the manual page in the default web browser and exit."
             ])
 
-        toolkitsP = flag Nothing (Just Toolkits) (mconcat
-            [ long "toolkits"
-            , short 't'
-            , help "Show information on toolkits and exit. Executables from the configuration \
-                   \file will be used, if a '.pandoc-plot.yml' file is in the current directory."
-            ])
-
 commandParser :: Parser (Maybe Command)
-commandParser = optional $ subparser (
-            command "clean" (
+commandParser = optional $ subparser $ mconcat
+            [ command "toolkits" ( 
+                info (toolkitsP <**> helper) (progDesc "Show information on toolkits and exit.")
+                )  
+            , command "clean" (
                 info (cleanP <**> helper) ( 
                     progDesc "Clean output directories where figures from FILE might be stored.\
                               \ WARNING: All files in those directories will be deleted." 
                     )
                 )
-            <> command "write-example-config" (
-                    info (writeConfigP <**> helper) (progDesc "Write example configuration to a file.")
-                    )
+            , command "write-example-config" (
+                info (writeConfigP <**> helper) (progDesc "Write example configuration to a file and exit.")
                 )
+            ]
     where
-        cleanP = Clean <$> strArgument (metavar "FILE")
-        writeConfigP = pure WriteConfig
-
+        configP = optional $ strOption (mconcat [long "config", metavar "PATH", help "Path to optional configuration file."])
+        toolkitsP = Toolkits <$> configP
+        cleanP = Clean <$> configP <*> strArgument (metavar "FILE")
+        writeConfigP = WriteConfig <$> 
+                strOption ( 
+                    mconcat [ long "path"
+                            , metavar "FILE"
+                            , value ".example-pandoc-plot.yml"
+                            , help "Target location of the configuration file. Default is \".example-pandoc-plot.yml\""
+                            ] 
+                          )
 
+-- | Determine configuration and run filter.
+--
+-- Priority for configuration:
+-- 
+--     (1) Loaded from filepath stored in document metadata, under the key @plot-configuration@;
+--
+--     (2) Loaded from file @.pandoc-plot.yml@ in current work directory;
+--
+--     (3) Default configuration
+--
 toJSONFilterWithConfig :: IO ()
 toJSONFilterWithConfig = do
-    c <- config
-    toJSONFilter (plotTransform c)
+    upToDatePandoc <- checkRuntimePandocVersion 
+    when upToDatePandoc $ toJSONFilter $ \doc -> do
+        c <- maybe localConfig configuration (configurationPathMeta doc)
+        plotTransform c doc
 
 
-config :: IO Configuration
-config = do 
+-- | Check that the runtime version of Pandoc is at least 2.8. The return value
+-- indicates whether the Pandoc version is new enough or not.
+checkRuntimePandocVersion :: IO Bool
+checkRuntimePandocVersion = do
+    let minimumPandocVersion = V.Version [2,8,0,0] []
+    -- Pandoc runs filters in an environment with two variables:
+    -- PANDOV_VERSION and PANDOC_READER_OPTS
+    -- We can use the former to ensure that people are not using pandoc < 2.8
+    pandocV <- lookupEnv "PANDOC_VERSION"
+    case pandocV >>= readVersion of
+        Nothing -> return True
+        Just v -> if (v < minimumPandocVersion)
+            then do
+                hPutStrLn stderr $ mconcat 
+                    [ "ERROR (pandoc-plot) The pandoc-plot filter only "
+                    , "supports Pandoc 2.8 and newer. "
+                    , "but you are using Pandoc "
+                    , showVersion v
+                    ] 
+                return False 
+            else return True
+    where
+        readVersion = fmap fst . lastMaybe . readP_to_S parseVersion
+        lastMaybe xs = if length xs > 1 then Just (last xs) else Nothing
+
+
+-- | Load configuration from local file @.pandoc-plot.yml@. 
+-- If the file does not exist, the default configuration will be used.
+localConfig :: IO Configuration
+localConfig = do 
     configExists <- doesFileExist ".pandoc-plot.yml"
     if configExists
-        then configuration ".pandoc-plot.yml" 
+        then configuration ".pandoc-plot.yml"
         else return defaultConfiguration
 
 
@@ -162,32 +207,49 @@         gitrev = either (const "unknown") Git.giHash ($$tGitInfoCwdTry)
 
 
-showAvailableToolkits :: IO ()
-showAvailableToolkits = do
-    c <- config
+showAvailableToolkits :: Maybe FilePath -> IO ()
+showAvailableToolkits mfp = do
+    c <- case mfp of
+        Nothing -> localConfig
+        Just fp -> configuration fp
+
     putStrLn "\nAVAILABLE TOOLKITS\n"
     available <- availableToolkits c
-    return available >>= mapM_ toolkitInfo
+    return available >>= mapM_ (availToolkitInfo c)
     putStrLn "\nUNAVAILABLE TOOLKITS\n"
     -- We don't use unavailableToolkits because this would force
     -- more IO actions
     let unavailable = toolkits \\ available
-    return unavailable >>= mapM_ toolkitInfo
+    return unavailable >>= mapM_ (unavailToolkitInfo c)
     where
-        toolkitInfo tk = do
+        toolkitInfo avail conf tk = do
+            exe <- executable tk conf
             putStrLn $ "Toolkit: " <> show tk
+            when avail $ putStrLn $ "    Executable: " <> exe
             putStrLn $ "    Code block trigger: " <> (unpack . cls $ tk)
             putStrLn $ "    Supported save formats: " <> (mconcat . intersperse ", " . fmap show $ supportedSaveFormats tk)
             putStrLn mempty
+        availToolkitInfo = toolkitInfo True
+        unavailToolkitInfo = toolkitInfo False
 
 
-clean :: FilePath -> IO ()
-clean fp = do
-    conf <- config
+-- | Clean output directories associated with a file
+-- 
+-- Priority for configuration are the same as @toJSONFilterWithConfig@.
+clean :: Maybe FilePath -- Use configuration file?
+      -> FilePath       -- Document to clean
+      -> IO ()
+clean mfp fp = do
+    doc <- readDoc fp
+    -- Note the priority for configuration:
+    -- (1) path of argument --config (2) document metadata (3) local .pandoc-plot.yml (4) default config
+    conf <- maybe localConfig configuration $ firstJusts [configurationPathMeta doc, mfp]
     putStrLn $ "Cleaning output directories for " <> fp
-    cleanedDirs <- readDoc fp >>= cleanOutputDirs conf
+    cleanedDirs <- cleanOutputDirs conf doc
     forM_ cleanedDirs $ \d -> putStrLn $ "Removed directory " <> d
-
+    where
+        firstJusts :: [Maybe a] -> Maybe a
+        firstJusts = msum
 
 showManPage :: IO ()
 showManPage = 
pandoc-plot.cabal view
@@ -1,9 +1,10 @@ name:           pandoc-plot
-version:        0.5.0.0
+version:        0.6.0.0
 cabal-version:  >= 1.12
 synopsis:       A Pandoc filter to include figures generated from code blocks using your plotting toolkit of choice.
 description:    A Pandoc filter to include figures generated from code blocks. Keep the document and code in the same location. Output is captured and included as a figure.
-category:       Documentation
+category:       Text
+copyright:      (c) 2019-2020 Laurent P. René de Cotret
 homepage:       https://github.com/LaurentRDC/pandoc-plot#readme
 bug-reports:    https://github.com/LaurentRDC/pandoc-plot/issues
 author:         Laurent P. René de Cotret
@@ -36,12 +37,14 @@         Text.Pandoc.Filter.Plot.Renderers
         Text.Pandoc.Filter.Plot.Renderers.Prelude
         Text.Pandoc.Filter.Plot.Renderers.Matplotlib
-        Text.Pandoc.Filter.Plot.Renderers.Plotly
+        Text.Pandoc.Filter.Plot.Renderers.PlotlyPython
+        Text.Pandoc.Filter.Plot.Renderers.PlotlyR
         Text.Pandoc.Filter.Plot.Renderers.Matlab
         Text.Pandoc.Filter.Plot.Renderers.Mathematica
         Text.Pandoc.Filter.Plot.Renderers.Octave
         Text.Pandoc.Filter.Plot.Renderers.GGPlot2
         Text.Pandoc.Filter.Plot.Renderers.GNUPlot
+        Text.Pandoc.Filter.Plot.Renderers.Graphviz
     hs-source-dirs:
         src
     ghc-options: 
@@ -99,6 +102,7 @@     other-modules:
         Common
     build-depends:   base                 >= 4.11 && < 5
+                   , containers
                    , directory
                    , data-default-class   >= 0.1.2
                    , filepath
src/Text/Pandoc/Filter/Plot.hs view
@@ -49,7 +49,7 @@     * @preamble=...@: Path to a file to include before the code block. Ideal to avoid repetition over many figures.
 
 Default values for the above attributes are stored in the @Configuration@ datatype. These can be specified in a 
-YAML file which must be named ".pandoc-plot.yml".
+YAML file. 
 
 Here is an example code block which will render a figure using gnuplot, in Markdown:
 
src/Text/Pandoc/Filter/Plot/Configuration.hs view
@@ -15,24 +15,25 @@ 
 module Text.Pandoc.Filter.Plot.Configuration (
       configuration
+    , configurationPathMeta
     , defaultConfiguration
 ) where
 
 import           Data.Default.Class     (Default, def)
 import           Data.Maybe             (fromMaybe)
-import           Data.Text              (Text, pack)
+import           Data.Text              (Text, pack, unpack)
 import qualified Data.Text.IO           as TIO
 import           Data.Yaml
 import           Data.Yaml.Config       (ignoreEnv, loadYamlSettings)
 
-import           Text.Pandoc.Definition (Format(..))
+import           Text.Pandoc.Definition (Format(..), Pandoc(..), MetaValue(..), lookupMeta)
 
 import Text.Pandoc.Filter.Plot.Types
 
 -- | Read configuration from a YAML file. The
 -- keys are exactly the same as for code blocks.
 --
--- If a key is either not present, its value will be set
+-- If a key is not present, its value will be set
 -- to the default value. Parsing errors result in thrown exceptions.
 configuration :: FilePath -> IO Configuration
 configuration fp = (loadYamlSettings [fp] [] ignoreEnv) >>= renderConfig
@@ -45,6 +46,33 @@ defaultConfiguration = def
 
 
+-- | Extact path to configuration from the metadata in a Pandoc document.
+-- The path to the configuration file should be under the @plot-configuration@ key.
+-- In case there is no such metadata, return the default configuration.
+--
+-- For example, at the top of a markdown file:
+--
+-- @
+--     ---
+--     title: My document
+--     author: John Doe
+--     plot-configuration: /path/to/file.yml
+--     ---     
+-- @
+--
+-- The same can be specified via the command line using Pandoc's @-M@ flag:
+--
+-- > pandoc --filter pandoc-plot -M plot-configuration="path/to/file.yml" ...
+--
+-- @since 0.6.0.0
+configurationPathMeta :: Pandoc -> Maybe FilePath
+configurationPathMeta (Pandoc meta _) = 
+        lookupMeta "plot-configuration" meta >>= getPath
+    where
+        getPath (MetaString s) = Just (unpack s)
+        getPath _              = Nothing
+
+
 -- We define a precursor type because preambles are best specified as file paths,
 -- but we want to read those files before building a full
 -- @Configuration@ value.
@@ -58,10 +86,12 @@     , _matplotlibPrec    :: !MatplotlibPrecursor
     , _matlabPrec        :: !MatlabPrecursor
     , _plotlyPythonPrec  :: !PlotlyPythonPrecursor
+    , _plotlyRPrec       :: !PlotlyRPrecursor
     , _mathematicaPrec   :: !MathematicaPrecursor
     , _octavePrec        :: !OctavePrecursor
     , _ggplot2Prec       :: !GGPlot2Precursor
     , _gnuplotPrec       :: !GNUPlotPrecursor
+    , _graphvizPrec      :: !GraphvizPrecursor
     }
 
 instance Default ConfigPrecursor where
@@ -75,10 +105,12 @@         , _matplotlibPrec    = def
         , _matlabPrec        = def
         , _plotlyPythonPrec  = def
+        , _plotlyRPrec       = def
         , _mathematicaPrec   = def
         , _octavePrec        = def
         , _ggplot2Prec       = def
         , _gnuplotPrec       = def
+        , _graphvizPrec      = def
         }
 
 
@@ -91,10 +123,12 @@         }
 data MatlabPrecursor        = MatlabPrecursor       {_matlabPreamble       :: !(Maybe FilePath), _matlabExe       :: !FilePath}
 data PlotlyPythonPrecursor  = PlotlyPythonPrecursor {_plotlyPythonPreamble :: !(Maybe FilePath), _plotlyPythonExe :: !FilePath}
+data PlotlyRPrecursor       = PlotlyRPrecursor      {_plotlyRPreamble      :: !(Maybe FilePath), _plotlyRExe      :: !FilePath}
 data MathematicaPrecursor   = MathematicaPrecursor  {_mathematicaPreamble  :: !(Maybe FilePath), _mathematicaExe  :: !FilePath}
 data OctavePrecursor        = OctavePrecursor       {_octavePreamble       :: !(Maybe FilePath), _octaveExe       :: !FilePath}
 data GGPlot2Precursor       = GGPlot2Precursor      {_ggplot2Preamble      :: !(Maybe FilePath), _ggplot2Exe      :: !FilePath}
 data GNUPlotPrecursor       = GNUPlotPrecursor      {_gnuplotPreamble      :: !(Maybe FilePath), _gnuplotExe      :: !FilePath}
+data GraphvizPrecursor      = GraphvizPrecursor     {_graphvizPreamble     :: !(Maybe FilePath), _graphvizExe    :: !FilePath}
 
 
 instance Default MatplotlibPrecursor where
@@ -102,10 +136,12 @@ 
 instance Default MatlabPrecursor        where def = MatlabPrecursor       Nothing (matlabExe def)
 instance Default PlotlyPythonPrecursor  where def = PlotlyPythonPrecursor Nothing (plotlyPythonExe def)
+instance Default PlotlyRPrecursor       where def = PlotlyRPrecursor      Nothing (plotlyRExe def)
 instance Default MathematicaPrecursor   where def = MathematicaPrecursor  Nothing (mathematicaExe def)
 instance Default OctavePrecursor        where def = OctavePrecursor       Nothing (octaveExe def)
 instance Default GGPlot2Precursor       where def = GGPlot2Precursor      Nothing (ggplot2Exe def)
 instance Default GNUPlotPrecursor       where def = GNUPlotPrecursor      Nothing (gnuplotExe def)
+instance Default GraphvizPrecursor      where def = GraphvizPrecursor     Nothing (graphvizExe def)
 
 instance FromJSON MatplotlibPrecursor where
     parseJSON (Object v) = 
@@ -124,6 +160,10 @@     parseJSON (Object v) = PlotlyPythonPrecursor <$> v .:? (tshow PreambleK) <*> v .:? (tshow ExecutableK) .!= (plotlyPythonExe def)
     parseJSON _ = fail $ mconcat ["Could not parse ", show PlotlyPython, " configuration."]
 
+instance FromJSON PlotlyRPrecursor where
+    parseJSON (Object v) = PlotlyRPrecursor <$> v .:? (tshow PreambleK) <*> v .:? (tshow ExecutableK) .!= (plotlyRExe def)
+    parseJSON _ = fail $ mconcat ["Could not parse ", show PlotlyR, " configuration."]
+
 instance FromJSON MathematicaPrecursor where
     parseJSON (Object v) = MathematicaPrecursor <$> v .:? (tshow PreambleK) <*> v .:? (tshow ExecutableK) .!= (mathematicaExe def)
     parseJSON _ = fail $ mconcat ["Could not parse ", show Mathematica, " configuration."]
@@ -137,10 +177,14 @@     parseJSON _ = fail $ mconcat ["Could not parse ", show GGPlot2, " configuration."]
 
 instance FromJSON GNUPlotPrecursor where
-    parseJSON (Object v) = GNUPlotPrecursor <$> v .:? (tshow PreambleK) <*> v .:? (tshow ExecutableK) .!= (ggplot2Exe def)
+    parseJSON (Object v) = GNUPlotPrecursor <$> v .:? (tshow PreambleK) <*> v .:? (tshow ExecutableK) .!= (gnuplotExe def)
     parseJSON _ = fail $ mconcat ["Could not parse ", show GNUPlot, " configuration."]
 
+instance FromJSON GraphvizPrecursor where
+    parseJSON (Object v) = GraphvizPrecursor <$> v .:? (tshow PreambleK) <*> v .:? (tshow ExecutableK) .!= (graphvizExe def)
+    parseJSON _ = fail $ mconcat ["Could not parse ", show Graphviz, " configuration."]
 
+
 instance FromJSON ConfigPrecursor where
     parseJSON (Null) = return def -- In case of empty file
     parseJSON (Object v) = do
@@ -154,10 +198,12 @@         _matplotlibPrec    <- v .:? (cls Matplotlib)       .!= def
         _matlabPrec        <- v .:? (cls Matlab)           .!= def
         _plotlyPythonPrec  <- v .:? (cls PlotlyPython)     .!= def
+        _plotlyRPrec       <- v .:? (cls PlotlyR)          .!= def
         _mathematicaPrec   <- v .:? (cls Mathematica)      .!= def
         _octavePrec        <- v .:? (cls Octave)           .!= def
         _ggplot2Prec       <- v .:? (cls GGPlot2)          .!= def
         _gnuplotPrec       <- v .:? (cls GNUPlot)          .!= def
+        _graphvizPrec      <- v .:? (cls Graphviz)         .!= def
 
         return $ ConfigPrecursor{..}
     parseJSON _          = fail "Could not parse configuration."
@@ -177,18 +223,22 @@         matplotlibExe   = _matplotlibExe _matplotlibPrec
         matlabExe       = _matlabExe _matlabPrec
         plotlyPythonExe = _plotlyPythonExe _plotlyPythonPrec
+        plotlyRExe      = _plotlyRExe _plotlyRPrec
         mathematicaExe  = _mathematicaExe _mathematicaPrec
         octaveExe       = _octaveExe _octavePrec
         ggplot2Exe      = _ggplot2Exe _ggplot2Prec
         gnuplotExe      = _gnuplotExe _gnuplotPrec
+        graphvizExe     = _graphvizExe _graphvizPrec
     
     matplotlibPreamble   <- readPreamble (_matplotlibPreamble _matplotlibPrec)
     matlabPreamble       <- readPreamble (_matlabPreamble _matlabPrec)
     plotlyPythonPreamble <- readPreamble (_plotlyPythonPreamble _plotlyPythonPrec)
+    plotlyRPreamble      <- readPreamble (_plotlyRPreamble _plotlyRPrec)
     mathematicaPreamble  <- readPreamble (_mathematicaPreamble _mathematicaPrec)
     octavePreamble       <- readPreamble (_octavePreamble _octavePrec)
     ggplot2Preamble      <- readPreamble (_ggplot2Preamble _ggplot2Prec)
     gnuplotPreamble      <- readPreamble (_gnuplotPreamble _gnuplotPrec)
+    graphvizPreamble     <- readPreamble (_graphvizPreamble _graphvizPrec)
 
     return Configuration{..}
     where
src/Text/Pandoc/Filter/Plot/Renderers.hs view
@@ -21,9 +21,11 @@     , parseExtraAttrs
     , command
     , capture
+    , executable
     , toolkitAvailable
     , availableToolkits
     , unavailableToolkits
+    , OutputSpec(..)
 ) where
 
 import           Control.Concurrent.Async                      (mapConcurrently)
@@ -37,9 +39,12 @@ 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.PlotlyPython
+import           Text.Pandoc.Filter.Plot.Renderers.PlotlyR
 import           Text.Pandoc.Filter.Plot.Renderers.GGPlot2
 import           Text.Pandoc.Filter.Plot.Renderers.GNUPlot
+import           Text.Pandoc.Filter.Plot.Renderers.Graphviz
+import           Text.Pandoc.Filter.Plot.Renderers.Prelude     (executable, OutputSpec(..))
 
 import           Text.Pandoc.Filter.Plot.Types
 
@@ -48,44 +53,52 @@ scriptExtension :: Toolkit -> String
 scriptExtension Matplotlib   = ".py"
 scriptExtension PlotlyPython = ".py"
+scriptExtension PlotlyR      = ".r"
 scriptExtension Matlab       = ".m"
 scriptExtension Mathematica  = ".m"
 scriptExtension Octave       = ".m"
 scriptExtension GGPlot2      = ".r"
 scriptExtension GNUPlot      = ".gp"
+scriptExtension Graphviz     = ".dot"
 
 
 -- Make a string into a comment
 comment :: Toolkit -> (Text -> Text)
 comment Matplotlib   = mappend "# "
 comment PlotlyPython = mappend "# "
+comment PlotlyR      = mappend "# "
 comment Matlab       = mappend "% "
 comment Mathematica  = \t -> mconcat ["(*", t, "*)"]
 comment Octave       = mappend "% "
 comment GGPlot2      = mappend "# "
 comment GNUPlot      = mappend "# "
+comment Graphviz     = mappend "// "
 
 
 -- | The function that maps from configuration to the preamble.
 preambleSelector :: Toolkit -> (Configuration -> Script)
 preambleSelector Matplotlib   = matplotlibPreamble
 preambleSelector PlotlyPython = plotlyPythonPreamble
+preambleSelector PlotlyR      = plotlyRPreamble
 preambleSelector Matlab       = matlabPreamble
 preambleSelector Mathematica  = mathematicaPreamble
 preambleSelector Octave       = octavePreamble
 preambleSelector GGPlot2      = ggplot2Preamble
 preambleSelector GNUPlot      = gnuplotPreamble
+preambleSelector Graphviz     = graphvizPreamble
 
 
 -- | Save formats supported by this renderer.
 supportedSaveFormats :: Toolkit -> [SaveFormat]
 supportedSaveFormats Matplotlib   = matplotlibSupportedSaveFormats
 supportedSaveFormats PlotlyPython = plotlyPythonSupportedSaveFormats
+supportedSaveFormats PlotlyR      = plotlyRSupportedSaveFormats
 supportedSaveFormats Matlab       = matlabSupportedSaveFormats
 supportedSaveFormats Mathematica  = mathematicaSupportedSaveFormats
 supportedSaveFormats Octave       = octaveSupportedSaveFormats
 supportedSaveFormats GGPlot2      = ggplot2SupportedSaveFormats
 supportedSaveFormats GNUPlot      = gnuplotSupportedSaveFormats
+supportedSaveFormats Graphviz     = graphvizSupportedSaveFormats
 
 
 -- Checks to perform before running a script. If ANY check fails,
@@ -104,36 +117,45 @@ 
 
 -- | Generate the appropriate command-line command to generate a figure.
-command :: Toolkit -> (Configuration -> FigureSpec -> FilePath -> Text)
+-- The executable will need to be found first, hence the IO monad.
+command :: Toolkit 
+        -> OutputSpec
+        -> IO Text
 command Matplotlib   = matplotlibCommand
 command PlotlyPython = plotlyPythonCommand
+command PlotlyR      = plotlyRCommand
 command Matlab       = matlabCommand
 command Mathematica  = mathematicaCommand
 command Octave       = octaveCommand
 command GGPlot2      = ggplot2Command
 command GNUPlot      = gnuplotCommand
+command Graphviz     = graphvizCommand
 
 
 -- | Script fragment required to capture a figure.
 capture :: Toolkit -> (FigureSpec -> FilePath -> Script)
 capture Matplotlib   = matplotlibCapture
 capture PlotlyPython = plotlyPythonCapture
+capture PlotlyR      = plotlyRCapture
 capture Matlab       = matlabCapture
 capture Mathematica  = mathematicaCapture
 capture Octave       = octaveCapture
 capture GGPlot2      = ggplot2Capture
 capture GNUPlot      = gnuplotCapture
+capture Graphviz     = graphvizCapture 
 
 
 -- | Check if a toolkit is available, based on the current configuration
 toolkitAvailable :: Toolkit -> Configuration -> IO Bool
 toolkitAvailable Matplotlib   = matplotlibAvailable
 toolkitAvailable PlotlyPython = plotlyPythonAvailable
+toolkitAvailable PlotlyR      = plotlyRAvailable
 toolkitAvailable Matlab       = matlabAvailable
 toolkitAvailable Mathematica  = mathematicaAvailable
 toolkitAvailable Octave       = octaveAvailable
 toolkitAvailable GGPlot2      = ggplot2Available
 toolkitAvailable GNUPlot      = gnuplotAvailable
+toolkitAvailable Graphviz     = graphvizAvailable
 
 
 -- | List of toolkits available on this machine.
src/Text/Pandoc/Filter/Plot/Renderers/GGPlot2.hs view
@@ -10,7 +10,7 @@ Stability   : internal
 Portability : portable
 
-Rendering Mathematica plots code blocks
+Rendering GGPlot2 plots code blocks
 -}
 
 module Text.Pandoc.Filter.Plot.Renderers.GGPlot2 (
@@ -22,16 +22,21 @@ 
 import           Text.Pandoc.Filter.Plot.Renderers.Prelude
 
+
 ggplot2SupportedSaveFormats :: [SaveFormat]
 ggplot2SupportedSaveFormats = [PNG, PDF, SVG, JPG, EPS, TIF]
 
 
-ggplot2Command :: Configuration -> FigureSpec -> FilePath -> Text
-ggplot2Command Configuration{..} _ fp = [st|#{ggplot2Exe} "#{fp}"|]
+ggplot2Command :: OutputSpec -> IO Text
+ggplot2Command OutputSpec{..} = do
+    exe <- executable GGPlot2 oConfiguration
+    return [st|#{exe} "#{oScriptPath}"|]
 
 
 ggplot2Available :: Configuration -> IO Bool
-ggplot2Available Configuration{..} = commandSuccess [st|#{ggplot2Exe} -e 'library("ggplot2")'|]
+ggplot2Available conf = do
+    exe <- executable GGPlot2 conf
+    commandSuccess [st|#{exe} -e 'library("ggplot2")'|]
 
 
 ggplot2Capture :: FigureSpec -> FilePath -> Script
src/Text/Pandoc/Filter/Plot/Renderers/GNUPlot.hs view
@@ -26,12 +26,16 @@ gnuplotSupportedSaveFormats = [PNG, SVG, EPS, GIF, JPG, PDF]
 
 
-gnuplotCommand :: Configuration -> FigureSpec -> FilePath -> Text
-gnuplotCommand Configuration{..} _ fp = [st|#{gnuplotExe} -c "#{fp}"|]
+gnuplotCommand :: OutputSpec -> IO Text
+gnuplotCommand OutputSpec{..} = do
+    exe <- executable GNUPlot oConfiguration
+    return [st|#{exe} -c "#{oScriptPath}"|]
 
 
 gnuplotAvailable :: Configuration -> IO Bool
-gnuplotAvailable Configuration{..} = commandSuccess [st|#{gnuplotExe} -h|]
+gnuplotAvailable conf = do
+    exe <- executable GNUPlot conf
+    commandSuccess [st|#{exe} -h|]
 
 
 gnuplotCapture :: FigureSpec -> FilePath -> Script
+ src/Text/Pandoc/Filter/Plot/Renderers/Graphviz.hs view
@@ -0,0 +1,47 @@+{-# LANGUAGE NoImplicitPrelude #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE QuasiQuotes       #-}
+{-# 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
+
+Rendering Graphviz plots code blocks
+-}
+
+module Text.Pandoc.Filter.Plot.Renderers.Graphviz (
+      graphvizSupportedSaveFormats
+    , graphvizCommand
+    , graphvizCapture
+    , graphvizAvailable
+) where
+
+import           Data.Char
+import           Text.Pandoc.Filter.Plot.Renderers.Prelude
+
+
+graphvizSupportedSaveFormats :: [SaveFormat]
+graphvizSupportedSaveFormats = [PNG, PDF, SVG, JPG, EPS, WEBP, GIF]
+
+
+graphvizCommand :: OutputSpec -> IO Text
+graphvizCommand OutputSpec{..} = do
+    exe <- executable Graphviz oConfiguration
+    let fmt = fmap toLower . show . saveFormat $ oFigureSpec
+    return [st|#{exe} -T#{fmt} -o "#{oFigurePath}" "#{oScriptPath}"|]
+
+
+graphvizAvailable :: Configuration -> IO Bool
+graphvizAvailable conf = do
+    exe <- executable Graphviz conf
+    commandSuccess [st|#{exe} -?|]
+
+
+-- Graphviz export is entirely based on command-line arguments
+-- so there is no need to modify the script itself.
+graphvizCapture :: FigureSpec -> FilePath -> Script
+graphvizCapture _ _ = mempty
src/Text/Pandoc/Filter/Plot/Renderers/Mathematica.hs view
@@ -26,12 +26,16 @@ mathematicaSupportedSaveFormats = [PNG, PDF, SVG, JPG, EPS, GIF, TIF]
 
 
-mathematicaCommand :: Configuration -> FigureSpec -> FilePath -> Text
-mathematicaCommand Configuration{..} _ fp = [st|#{mathematicaExe} -script "#{fp}"|]
+mathematicaCommand :: OutputSpec -> IO Text
+mathematicaCommand OutputSpec{..} = do
+    exe <- executable Mathematica oConfiguration
+    return [st|#{exe} -script "#{oScriptPath}"|]
 
 
 mathematicaAvailable :: Configuration -> IO Bool
-mathematicaAvailable Configuration{..} = commandSuccess [st|#{mathematicaExe} -h|] -- TODO: test this
+mathematicaAvailable conf = do
+    exe <- executable Mathematica conf
+    commandSuccess [st|#{exe} -h|] -- TODO: test this
 
 
 mathematicaCapture :: FigureSpec -> FilePath -> Script
src/Text/Pandoc/Filter/Plot/Renderers/Matlab.hs view
@@ -20,6 +20,8 @@     , matlabAvailable
 ) where
 
+import           System.Directory                            (exeExtension)
+
 import           Text.Pandoc.Filter.Plot.Renderers.Prelude
 
 
@@ -27,8 +29,10 @@ matlabSupportedSaveFormats = [PNG, PDF, SVG, JPG, EPS, GIF, TIF]
 
 
-matlabCommand :: Configuration -> FigureSpec -> FilePath -> Text
-matlabCommand Configuration{..} _ fp = [st|#{matlabExe} -batch "run('#{fp}')"|]
+matlabCommand :: OutputSpec -> IO Text
+matlabCommand OutputSpec{..} = do
+    exe <- executable Matlab oConfiguration
+    return [st|#{exe} -batch "run('#{oScriptPath}')"|]
 
 
 -- On Windows at least, "matlab -help"  actually returns -1, even though the
@@ -36,11 +40,7 @@ -- Therefore, we cannot rely on this behavior to know if matlab is present, 
 -- like other toolkits.
 matlabAvailable :: Configuration -> IO Bool
-matlabAvailable Configuration{..} = existsOnPath (matlabExe <> ext)
-    where
-        -- The @which@ function from Turtle only works on
-        -- windows if the executable extension is included.
-        ext = if isWindows then ".exe" else mempty
+matlabAvailable Configuration{..} = existsOnPath (matlabExe <> exeExtension)
 
 
 matlabCapture :: FigureSpec -> FilePath -> Script
src/Text/Pandoc/Filter/Plot/Renderers/Matplotlib.hs view
@@ -36,8 +36,10 @@ matplotlibSupportedSaveFormats = [PNG, PDF, SVG, JPG, EPS, GIF, TIF]
 
 
-matplotlibCommand :: Configuration -> FigureSpec -> FilePath -> Text
-matplotlibCommand Configuration{..} _ fp = [st|#{matplotlibExe} "#{fp}"|]
+matplotlibCommand :: OutputSpec -> IO Text
+matplotlibCommand OutputSpec{..} = do
+    exe <- executable Matplotlib oConfiguration
+    return [st|#{exe} "#{oScriptPath}"|]
 
 
 matplotlibCapture :: FigureSpec -> FilePath -> Script
@@ -57,7 +59,9 @@ 
 
 matplotlibAvailable :: Configuration -> IO Bool
-matplotlibAvailable Configuration{..} = commandSuccess [st|#{matplotlibExe} -c "import matplotlib"|]
+matplotlibAvailable conf = do
+    exe <- executable Matplotlib conf
+    commandSuccess [st|#{exe} -c "import matplotlib"|]
 
 
 -- | Check if `matplotlib.pyplot.show()` calls are present in the script,
src/Text/Pandoc/Filter/Plot/Renderers/Octave.hs view
@@ -27,12 +27,16 @@ octaveSupportedSaveFormats = [PNG, PDF, SVG, JPG, EPS, GIF, TIF]
 
 
-octaveCommand :: Configuration -> FigureSpec -> FilePath -> Text
-octaveCommand Configuration{..} _ fp = [st|#{octaveExe} --no-gui --no-window-system "#{fp}"|]
+octaveCommand :: OutputSpec -> IO Text
+octaveCommand OutputSpec{..} = do
+    exe <- executable Octave oConfiguration
+    return [st|#{exe} --no-gui --no-window-system "#{oScriptPath}"|]
 
 
 octaveAvailable :: Configuration -> IO Bool
-octaveAvailable Configuration{..} = commandSuccess [st|#{octaveExe} -h|]
+octaveAvailable conf = do
+    exe <- executable Octave conf
+    commandSuccess [st|#{exe} -h|]
 
 
 octaveCapture :: FigureSpec -> FilePath -> Script
− src/Text/Pandoc/Filter/Plot/Renderers/Plotly.hs
@@ -1,43 +0,0 @@-{-# LANGUAGE NoImplicitPrelude #-}
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE QuasiQuotes       #-}
-{-# 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
-
-Rendering Plotly code blocks
--}
-
-module Text.Pandoc.Filter.Plot.Renderers.Plotly (
-      plotlyPythonSupportedSaveFormats
-    , plotlyPythonCommand
-    , plotlyPythonCapture
-    , plotlyPythonAvailable
-) where
-
-import           Text.Pandoc.Filter.Plot.Renderers.Prelude
-
-
-plotlyPythonSupportedSaveFormats :: [SaveFormat]
-plotlyPythonSupportedSaveFormats = [PNG, JPG, WEBP, PDF, SVG, EPS]
-
-
-plotlyPythonCommand :: Configuration -> FigureSpec -> FilePath -> Text
-plotlyPythonCommand Configuration{..} _ fp = [st|#{plotlyPythonExe} "#{fp}"|]
-
-
-plotlyPythonAvailable :: Configuration -> IO Bool
-plotlyPythonAvailable Configuration{..} = commandSuccess [st|#{plotlyPythonExe} -c "import plotly.graph_objects"|]
-
-
-plotlyPythonCapture :: FigureSpec -> FilePath -> Script
-plotlyPythonCapture _ fname = [st|
-import plotly.graph_objects as go
-__current_plotly_figure = next(obj for obj in globals().values() if type(obj) == go.Figure)
-__current_plotly_figure.write_image(r"#{fname}")
-|]
+ src/Text/Pandoc/Filter/Plot/Renderers/PlotlyPython.hs view
@@ -0,0 +1,47 @@+{-# LANGUAGE NoImplicitPrelude #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE QuasiQuotes       #-}
+{-# 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
+
+Rendering Plotly-python code blocks
+-}
+
+module Text.Pandoc.Filter.Plot.Renderers.PlotlyPython (
+      plotlyPythonSupportedSaveFormats
+    , plotlyPythonCommand
+    , plotlyPythonCapture
+    , plotlyPythonAvailable
+) where
+
+import           Text.Pandoc.Filter.Plot.Renderers.Prelude
+
+
+plotlyPythonSupportedSaveFormats :: [SaveFormat]
+plotlyPythonSupportedSaveFormats = [PNG, JPG, WEBP, PDF, SVG, EPS]
+
+
+plotlyPythonCommand :: OutputSpec -> IO Text
+plotlyPythonCommand OutputSpec{..} = do
+    exe <- executable PlotlyPython oConfiguration
+    return [st|#{exe} "#{oScriptPath}"|]
+
+
+plotlyPythonAvailable :: Configuration -> IO Bool
+plotlyPythonAvailable conf = do
+    exe <- executable PlotlyPython conf
+    commandSuccess [st|#{exe} -c "import plotly.graph_objects"|]
+
+
+plotlyPythonCapture :: FigureSpec -> FilePath -> Script
+plotlyPythonCapture _ fname = [st|
+import plotly.graph_objects as go
+__current_plotly_figure = next(obj for obj in globals().values() if type(obj) == go.Figure)
+__current_plotly_figure.write_image(r"#{fname}")
+|]
+ src/Text/Pandoc/Filter/Plot/Renderers/PlotlyR.hs view
@@ -0,0 +1,49 @@+{-# LANGUAGE NoImplicitPrelude #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE QuasiQuotes       #-}
+{-# 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
+
+Rendering Plotly/R plots code blocks
+-}
+
+module Text.Pandoc.Filter.Plot.Renderers.PlotlyR (
+      plotlyRSupportedSaveFormats
+    , plotlyRCommand
+    , plotlyRCapture
+    , plotlyRAvailable
+) where
+
+import           Text.Pandoc.Filter.Plot.Renderers.Prelude
+
+
+plotlyRSupportedSaveFormats :: [SaveFormat]
+plotlyRSupportedSaveFormats = [PNG, PDF, SVG, JPG, EPS]
+
+
+plotlyRCommand :: OutputSpec -> IO Text
+plotlyRCommand OutputSpec{..} = do
+    exe <- executable PlotlyR oConfiguration
+    return [st|#{exe} "#{oScriptPath}"|]
+
+
+plotlyRAvailable :: Configuration -> IO Bool
+plotlyRAvailable conf = do
+    exe <- executable GGPlot2 conf
+    commandSuccess [st|#{exe} -e 'library("plotly")'|]
+
+
+-- Based on the following documentation:
+--    https://plotly.com/r/static-image-export/
+plotlyRCapture :: FigureSpec -> FilePath -> Script
+plotlyRCapture FigureSpec{..} fname = [st|
+library(plotly) # just in case
+if (!require("processx")) install.packages("processx")
+orca(last_plot(), file = "#{fname}")
+|]
src/Text/Pandoc/Filter/Plot/Renderers/Prelude.hs view
@@ -19,10 +19,14 @@     , unpack
     , commandSuccess
     , existsOnPath
+    , executable
+    , OutputSpec(..)
 ) where
 
 import           Data.Maybe                    (isJust)
 import           Data.Text                     (Text, unpack)
+
+import           System.Directory              (findExecutable)
 import           System.Exit                   (ExitCode(..))
 import           System.Process.Typed          (runProcess, shell, 
                                                 setStdout, setStderr, 
@@ -47,4 +51,34 @@ 
 -- | Checks that an executable is available on path, at all.
 existsOnPath :: FilePath -> IO Bool
-existsOnPath fp = Sh.which (Sh.fromString fp) >>= fmap isJust . return+existsOnPath fp = Sh.which (Sh.fromString fp) >>= fmap isJust . return
+
+
+-- | Try to find the executable and normalise its path.
+-- If it cannot be found, it is left unchanged - just in case.
+tryToFindExe :: String -> IO FilePath
+tryToFindExe fp = findExecutable fp >>= maybe (return fp) return
+
+
+-- | Path to the executable of a toolkit. If the executable can
+-- be found, then it will be the full path to it.
+executable :: Toolkit -> Configuration -> IO FilePath
+executable Matplotlib   = tryToFindExe . matplotlibExe
+executable PlotlyPython = tryToFindExe . plotlyPythonExe
+executable PlotlyR      = tryToFindExe . plotlyRExe
+executable Matlab       = tryToFindExe . matlabExe
+executable Mathematica  = tryToFindExe . mathematicaExe
+executable Octave       = tryToFindExe . octaveExe
+executable GGPlot2      = tryToFindExe . ggplot2Exe
+executable GNUPlot      = tryToFindExe . gnuplotExe
+executable Graphviz     = tryToFindExe . graphvizExe
+
+
+-- | Internal description of all information 
+-- needed to output a figure.
+data OutputSpec = OutputSpec 
+    { oConfiguration :: Configuration -- ^ Pandoc-plot configuration
+    , oFigureSpec    :: FigureSpec    -- ^ Figure spec
+    , oScriptPath    :: FilePath      -- ^ Path to the script to render
+    , oFigurePath    :: FilePath      -- ^ Figure output path
+    } 
src/Text/Pandoc/Filter/Plot/Scripting.hs view
@@ -52,6 +52,7 @@     | ScriptFailure String Int    -- Command and exit code
     | ToolkitNotInstalled Toolkit -- Script failed because toolkit is not installed
 
+
 -- Run script as described by the spec, only if necessary
 runScriptIfNecessary :: FigureSpec -> PlotM ScriptResult
 runScriptIfNecessary spec = do
@@ -80,8 +81,6 @@     case checkResult of
         CheckFailed msg -> return $ ScriptChecksFailed msg
         CheckPassed -> do
-            -- We involve the script hash as a temporary filename
-            -- so that there is never any collision
             scriptPath <- tempScriptPath spec
             let captureFragment = (capture tk) spec (figurePath spec)
                 -- Note: for gnuplot, the capture string must be placed
@@ -91,7 +90,12 @@                                         then mconcat [captureFragment, "\n", script]
                                         else mconcat [script, "\n", captureFragment]
             liftIO $ T.writeFile scriptPath scriptWithCapture
-            let command_ = T.unpack $ command tk conf spec scriptPath
+            let outputSpec = OutputSpec { oConfiguration = conf
+                                        , oFigureSpec = spec
+                                        , oScriptPath = scriptPath
+                                        , oFigurePath = figurePath spec
+                                        }
+            command_ <- T.unpack <$> (liftIO $ command tk outputSpec)
 
             ec <- liftIO 
                     $ runProcess 
@@ -134,11 +138,13 @@ -- is important.
 tempScriptPath :: FigureSpec -> PlotM FilePath
 tempScriptPath FigureSpec{..} = do
-    tk <- asks toolkit
+    ext <- scriptExtension <$> asks toolkit
     -- Note that matlab will refuse to process files that don't start with
     -- a letter... so we append the renderer name
-    let ext = scriptExtension tk
-        hashedPath = "pandocplot" <> (show . abs . hash $ script) <> ext
+    -- Note that this hash is only so that we are running scripts from unique
+    -- file names; it does NOT determine whether this figure should
+    -- be rendered or not.
+    let hashedPath = "pandocplot" <> (show . abs . hash $ script) <> ext
     liftIO $ (</> hashedPath) <$> getCanonicalTemporaryDirectory
 
 
@@ -148,8 +154,11 @@ 
 
 -- | Determine the path a figure should have.
+-- The path for this file is unique to the content of the figure,
+-- so that @figurePath@ can be used to determine whether a figure should
+-- be rendered again or not.
 figurePath :: FigureSpec -> FilePath
 figurePath spec = normalise $ directory spec </> stem spec
   where
-    stem = flip addExtension ext . show . hash
+    stem = flip addExtension ext . show . figureContentHash
     ext  = extension . saveFormat $ spec
src/Text/Pandoc/Filter/Plot/Types.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE DeriveGeneric     #-}
 {-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards   #-}
 
 
 {-|
@@ -23,6 +24,7 @@     , InclusionKey(..)
     , FigureSpec(..)
     , SaveFormat(..)
+    , figureContentHash
     , cls
     , extension
     , toolkits
@@ -35,7 +37,7 @@ 
 import           Data.Char              (toLower)
 import           Data.Default.Class     (Default, def)
-import           Data.Hashable          (Hashable (..))
+import           Data.Hashable          (hash)
 import           Data.List              (intersperse)
 import           Data.Semigroup         (Semigroup (..))
 import           Data.String            (IsString (..))
@@ -47,43 +49,54 @@ 
 import           Text.Pandoc.Definition (Attr, Format(..))
 
+
 toolkits :: [Toolkit]
 toolkits = enumFromTo minBound maxBound
 
+
 -- | Enumeration of supported toolkits
 data Toolkit
     = Matplotlib
     | Matlab
     | PlotlyPython
+    | PlotlyR
     | Mathematica
     | Octave
     | GGPlot2
     | GNUPlot
+    | Graphviz
     deriving (Bounded, Eq, Enum, Generic)
 
+
 -- | This instance should only be used to display toolkit names
 instance Show Toolkit where
     show Matplotlib   = "Python/Matplotlib"
     show Matlab       = "MATLAB"
     show PlotlyPython = "Python/Plotly"
+    show PlotlyR      = "R/Plotly"
     show Mathematica  = "Mathematica"
     show Octave       = "GNU Octave"
     show GGPlot2      = "ggplot2"
     show GNUPlot      = "gnuplot"
+    show Graphviz     = "graphviz"
 
+
 -- | Class name which will trigger the filter
 cls :: Toolkit -> Text
 cls Matplotlib   = "matplotlib"
 cls Matlab       = "matlabplot"
 cls PlotlyPython = "plotly_python"
+cls PlotlyR      = "plotly_r"
 cls Mathematica  = "mathplot"
 cls Octave       = "octaveplot"
 cls GGPlot2      = "ggplot2"
 cls GNUPlot      = "gnuplot"
+cls Graphviz     = "graphviz"
 
 
 type PlotM a = ReaderT PlotEnv IO a
 
+
 data PlotEnv
     = PlotEnv { toolkit   :: !Toolkit
               , config    :: !Configuration
@@ -91,6 +104,24 @@ 
 -- | The @Configuration@ type holds the default values to use
 -- when running pandoc-plot. These values can be overridden in code blocks.
+--
+-- You can create an instance of the @Configuration@ type from file using the @configuration@ function.
+--
+-- You can store the path to a configuration file in metadata under the key @plot-configuration@. For example, in Markdown:
+--
+-- @
+--     ---
+--     title: My document
+--     author: John Doe
+--     plot-configuration: /path/to/file.yml
+--     ---     
+-- @
+--
+-- The same can be specified via the command line using Pandoc's @-M@ flag:
+--
+-- > pandoc --filter pandoc-plot -M plot-configuration="path/to/file.yml" ...
+--
+-- In this case, use @configurationPathMeta@ to extact the path from @Pandoc@ documents.
 data Configuration = Configuration
     { defaultDirectory      :: !FilePath   -- ^ The default directory where figures will be saved.
     , defaultWithSource     :: !Bool       -- ^ The default behavior of whether or not to include links to source code and high-res
@@ -100,19 +131,23 @@ 
     , matplotlibPreamble    :: !Script     -- ^ The default preamble script for the matplotlib toolkit.
     , plotlyPythonPreamble  :: !Script     -- ^ The default preamble script for the Plotly/Python toolkit.
+    , plotlyRPreamble       :: !Script     -- ^ The default preamble script for the Plotly/R toolkit.
     , matlabPreamble        :: !Script     -- ^ The default preamble script for the MATLAB toolkit.
     , mathematicaPreamble   :: !Script     -- ^ The default preamble script for the Mathematica toolkit.
     , octavePreamble        :: !Script     -- ^ The default preamble script for the GNU Octave toolkit.
     , ggplot2Preamble       :: !Script     -- ^ The default preamble script for the GGPlot2 toolkit.
     , gnuplotPreamble       :: !Script     -- ^ The default preamble script for the gnuplot toolkit.
+    , graphvizPreamble      :: !Script     -- ^ The default preamble script for the Graphviz toolkit.
     
     , matplotlibExe         :: !FilePath   -- ^ The executable to use to generate figures using the matplotlib toolkit.
     , matlabExe             :: !FilePath   -- ^ The executable to use to generate figures using the MATLAB toolkit.
     , plotlyPythonExe       :: !FilePath   -- ^ The executable to use to generate figures using the Plotly/Python toolkit.
+    , plotlyRExe            :: !FilePath   -- ^ The executable to use to generate figures using the Plotly/R toolkit.
     , mathematicaExe        :: !FilePath   -- ^ The executable to use to generate figures using the Mathematica toolkit.
     , octaveExe             :: !FilePath   -- ^ The executable to use to generate figures using the GNU Octave toolkit.
     , ggplot2Exe            :: !FilePath   -- ^ The executable to use to generate figures using the GGPlot2 toolkit.
     , gnuplotExe            :: !FilePath   -- ^ The executable to use to generate figures using the gnuplot toolkit.
+    , graphvizExe           :: !FilePath   -- ^ The executable to use to generate figures using the Graphviz toolkit.
     
     , matplotlibTightBBox   :: !Bool       -- ^ Whether or not to make Matplotlib figures tight by default.
     , matplotlibTransparent :: !Bool       -- ^ Whether or not to make Matplotlib figures transparent by default.
@@ -128,19 +163,23 @@           
           , matplotlibPreamble  = mempty
           , plotlyPythonPreamble= mempty
+          , plotlyRPreamble     = mempty
           , matlabPreamble      = mempty
           , mathematicaPreamble = mempty
           , octavePreamble      = mempty
           , ggplot2Preamble     = mempty
           , gnuplotPreamble     = mempty
+          , graphvizPreamble    = mempty
 
           , matplotlibExe       = if isWindows then "python" else "python3"
           , matlabExe           = "matlab"
           , plotlyPythonExe     = if isWindows then "python" else "python3"
+          , plotlyRExe          = "Rscript"
           , mathematicaExe      = "math"
           , octaveExe           = "octave"
           , ggplot2Exe          = "Rscript"
           , gnuplotExe          = "gnuplot"
+          , graphvizExe         = "dot"
           
           , matplotlibTightBBox   = False
           , matplotlibTransparent = False
@@ -148,6 +187,7 @@ 
 type Script = Text
 
+
 -- | Result of checking scripts for problems
 data CheckResult
     = CheckPassed
@@ -162,6 +202,7 @@ instance Monoid CheckResult where
     mempty = CheckPassed
 
+
 -- | Description of any possible inclusion key, both in documents
 -- and in configuration files.
 data InclusionKey
@@ -211,10 +252,19 @@     , dpi        :: !Int            -- ^ Dots-per-inch of figure.
     , extraAttrs :: ![(Text, Text)] -- ^ Renderer-specific extra attributes.
     , blockAttrs :: !Attr           -- ^ Attributes not related to @pandoc-plot@ will be propagated.
-    } deriving Generic
+    }
 
-instance Hashable FigureSpec -- From Generic
 
+-- | Hash of the content of a @FigureSpec@. Note that unlike usual hashes,
+-- two @FigureSpec@ with the same @figureContentHash@ does not mean that they are equal!
+--
+-- Not all parts of a FigureSpec are related to running code.
+-- For example, changing the caption should not require running the figure again.
+figureContentHash :: FigureSpec -> Int
+figureContentHash FigureSpec{..} = 
+    hash (script, fromEnum saveFormat, directory, dpi, extraAttrs)
+
+
 -- | Generated figure file format supported by pandoc-plot.
 -- Note that not all formats are supported by all toolkits.
 data SaveFormat
@@ -228,8 +278,6 @@     | WEBP
     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. That's OK
     -- since pandoc-plot is a command-line tool and isn't expected to run
@@ -255,6 +303,7 @@ 
 instance ToJSON SaveFormat where
     toJSON = toJSON . extension
+
 
 -- | Save format file extension
 extension :: SaveFormat -> String
tests/Common.hs view
@@ -69,6 +69,7 @@         include Octave       = "tests/includes/octave.m"
         include GGPlot2      = "tests/includes/ggplot2.r"
         include GNUPlot      = "tests/includes/gnuplot.gp"
+        include Graphviz     = "tests/includes/graphviz.dot"
 
 -------------------------------------------------------------------------------
 -- Test that the files are saved in the appropriate format
@@ -261,6 +262,7 @@ trivialContent Octave       = "figure('visible', 'off')\nplot (-10:0.1:10);"
 trivialContent GGPlot2      = "library(ggplot2)\nggplot()\n"
 trivialContent GNUPlot      = "plot sin(x)"
+trivialContent Graphviz     = "digraph {A -> B [label=\"test\"];}"
 
 
 addCaption :: String -> Block -> Block
@@ -324,6 +326,7 @@   where
     msg = mconcat ["Expected ", show xs, " to be an infix of ", show ys]
 
+
 -- Ensure a directory is empty but exists.
 ensureDirectoryExistsAndEmpty :: FilePath -> IO ()
 ensureDirectoryExistsAndEmpty dir = do
@@ -332,6 +335,7 @@         then removePathForcibly dir
         else return ()
     createDirectory dir
+
 
 tshow :: Show a => a -> Text
 tshow = pack . show
tests/Main.hs view
@@ -4,6 +4,7 @@ import           Control.Monad                    (forM_)
 
 import           Data.Default.Class               (Default, def)
+import qualified Data.Map.Strict                  as Map
 import           Data.Text                        (Text, unpack)
 
 import           Test.Tasty
@@ -28,6 +29,7 @@             "Configuration tests"
             [ testEmptyConfiguration
             , testExampleConfiguration
+            , testConfigurationPathMeta
             ]        
         , testGroup
             "Parsing tests"
@@ -59,11 +61,11 @@     testCase "empty configuration is correctly parsed to default values" $ do
         let config = def
 
-        parsedConfig <- configuration "tests/fixtures/.pandoc-plot.yml"
+        parsedConfig <- configuration "tests/fixtures/.empty-config.yml"
         assertEqual "" config parsedConfig
 
 
--- The exampel configuration is build by hand (to add comments)
+-- The example configuration is build by hand (to add comments)
 -- and it is embedded into the executable. Therefore, we must make sure it 
 -- is correctly parsed (and is therefore valid.)
 testExampleConfiguration :: TestTree
@@ -78,6 +80,19 @@ 
         parsedConfig <- configuration "example-config.yml"
         assertEqual "" config parsedConfig
+
+
+-- Test that the path to configuration in metadata is found correctly
+testConfigurationPathMeta :: TestTree
+testConfigurationPathMeta = 
+    testCase "Configuration path stored in metadata is correctly parsed" $ do
+    let configPath = "tests/fixtures/.config-meta.yml"
+        meta = B.Meta $ Map.fromList [("plot-configuration", B.MetaString configPath)]
+
+    parsedConfig <- maybe (return defaultConfiguration) configuration $ configurationPathMeta (B.Pandoc meta mempty)
+    expected <- configuration (unpack configPath)
+    assertEqual "" expected parsedConfig
+
 
 testCaptionReader :: TestTree
 testCaptionReader =