pandoc-plot 0.8.1.0 → 0.9.0.0
raw patch · 18 files changed
+378/−216 lines, 18 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Text.Pandoc.Filter.Plot: DependenciesK :: InclusionKey
+ Text.Pandoc.Filter.Plot: [defaultDependencies] :: Configuration -> ![FilePath]
- Text.Pandoc.Filter.Plot: Configuration :: !FilePath -> !Bool -> !Int -> !SaveFormat -> !Format -> !Verbosity -> !LogSink -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !Bool -> !Bool -> Configuration
+ Text.Pandoc.Filter.Plot: Configuration :: !FilePath -> !Bool -> !Int -> !SaveFormat -> ![FilePath] -> !Format -> !Verbosity -> !LogSink -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !Bool -> !Bool -> Configuration
- Text.Pandoc.Filter.Plot: type PlotM a = ReaderT RuntimeEnv IO a
+ Text.Pandoc.Filter.Plot: type PlotM a = StateT PlotState (ReaderT RuntimeEnv IO) a
Files
- CHANGELOG.md +10/−0
- MANUAL.md +12/−3
- README.md +8/−0
- docs/MANUAL.html +84/−75
- example-config.yml +7/−0
- pandoc-plot.cabal +1/−1
- src/Text/Pandoc/Filter/Plot.hs +1/−0
- src/Text/Pandoc/Filter/Plot/Configuration.hs +72/−67
- src/Text/Pandoc/Filter/Plot/Embed.hs +44/−14
- src/Text/Pandoc/Filter/Plot/Monad.hs +56/−7
- src/Text/Pandoc/Filter/Plot/Monad/Types.hs +12/−21
- src/Text/Pandoc/Filter/Plot/Parse.hs +19/−3
- src/Text/Pandoc/Filter/Plot/Renderers/Bokeh.hs +11/−7
- src/Text/Pandoc/Filter/Plot/Renderers/Graphviz.hs +2/−1
- src/Text/Pandoc/Filter/Plot/Renderers/Mathematica.hs +1/−1
- src/Text/Pandoc/Filter/Plot/Renderers/Matlab.hs +6/−2
- src/Text/Pandoc/Filter/Plot/Scripting.hs +28/−12
- tests/Main.hs +4/−2
CHANGELOG.md view
@@ -2,6 +2,16 @@ pandoc-plot uses [Semantic Versioning](http://semver.org/spec/v2.0.0.html) +Release 0.9.0.0 +--------------- + +* The `bokeh` toolkit now supports exporting plots as SVGs (#8). +* Interactive plots relying on javascript scripts will now defer loading the scripts (#9). +* Added the `dependencies` argument, which tells `pandoc-plot` what files are important to a particular figure (#10). If a file listed in `dependencies` changes (for example, a data file), `pandoc-plot` will re-render the associated figure. +* Better heuristic to determine what `bokeh` plot to save. This allows the user to export plots like the [`bokeh.layouts` module](https://docs.bokeh.org/en/latest/docs/user_guide/layout.html#creating-layouts). +* Added support for the `dpi` parameter in `graphviz` and `mathematica`. +* Added support for MATLAB's new `exportgraphics` function introduced in MATLAB 2020a. Older versions fallback to using `saveas`. + Release 0.8.1.0 ---------------
MANUAL.md view
@@ -2,7 +2,7 @@ The file MANUAL.md is automatically generated by the tools/mkmanual.ps1 script. Do not edit manually. --> -# pandoc-plot 0.8.1.0 +# pandoc-plot 0.9.0.0 ## A Pandoc filter to generate figures from code blocks in documents @@ -161,7 +161,7 @@ `pandoc-plot` is a command line executable with a few functions. You can take a look at the help using the `-h`/`--help` flag: ``` bash -pandoc-plot 0.8.1.0 - generate figures directly in documents +pandoc-plot 0.9.0.0 - generate figures directly in documents Usage: pandoc-plot.EXE [(-v|--version) | --full-version | (-m|--manual)] [COMMAND] [AST] @@ -239,7 +239,8 @@ format=(PNG|PDF|SVG|JPG|EPS|GIF|TIF|WEBP|HTML) source=(true|false) preamble=(path) - dpi=(integer) + dpi=(integer) + dependencies=[...] executable=(path) caption_format=(text) } @@ -258,6 +259,7 @@ - `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. + - `dependencies` is a list of files/directories that affect the figure, for example data files. If one of those files/directories changes, `pandoc-plot` will re-render the associated figure. Format is a comma-separated list, e.g. `dependencies=[data.txt, foo.bar, ~/wtv]`. Values for `dependencies` in the configuration file will be appended to the values in a code block. - `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). @@ -344,6 +346,13 @@ # Default format in which to save the figures. This can be specified # individually as well. format: PNG + +# Default files/directories on which all figures depend. If any of these files/directories +# changes, all figures will be re-rendered. +# Dependencies specified in code blocks will be appended to this list. +dependencies: + - file1.txt + - file2.txt # Text format for the captions. Unfortunately, there is no way to detect # this automatically. You can use the same notation as Pandoc's --from
README.md view
@@ -108,6 +108,14 @@ winget install pandoc-plot ``` +### Arch Linux + +You can install `pandoc-plot` from the [archlinux user repository](https://aur.archlinux.org/packages/pandoc-plot-bin/) as `pandoc-plot-bin`. You can install using e.g. `yay`: + +```sh +yay -S pandoc-plot-bin +``` + ### From Hackage/Stackage `pandoc-plot` is available on
docs/MANUAL.html view
@@ -85,7 +85,7 @@ <!-- The file MANUAL.md is automatically generated by the tools/mkmanual.ps1 script. Do not edit manually. --> -<h1 id="pandoc-plot-0.8.1.0">pandoc-plot 0.8.1.0</h1> +<h1 id="pandoc-plot-0.9.0.0">pandoc-plot 0.9.0.0</h1> <h2 id="a-pandoc-filter-to-generate-figures-from-code-blocks-in-documents">A Pandoc filter to generate figures from code blocks in documents</h2> <p><code>pandoc-plot</code> 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.</p> <ul> @@ -185,7 +185,7 @@ <div class="sourceCode" id="cb10"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true"></a><span class="ex">pandoc</span> --filter pandoc-plot --filter pandoc-crossref -i myfile.md -o myfile.html</span></code></pre></div> <h2 id="detailed-usage">Detailed usage</h2> <p><code>pandoc-plot</code> is a command line executable with a few functions. You can take a look at the help using the <code>-h</code>/<code>--help</code> flag:</p> -<div class="sourceCode" id="cb11"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true"></a><span class="ex">pandoc-plot</span> 0.8.1.0 - generate figures directly in documents</span> +<div class="sourceCode" id="cb11"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true"></a><span class="ex">pandoc-plot</span> 0.9.0.0 - generate figures directly in documents</span> <span id="cb11-2"><a href="#cb11-2" aria-hidden="true"></a></span> <span id="cb11-3"><a href="#cb11-3" aria-hidden="true"></a><span class="ex">Usage</span>: pandoc-plot.EXE [(-v<span class="kw">|</span><span class="ex">--version</span>) <span class="kw">|</span> <span class="ex">--full-version</span> <span class="kw">|</span> <span class="kw">(</span><span class="ex">-m</span><span class="kw">|</span><span class="ex">--manual</span><span class="kw">)</span>] </span> <span id="cb11-4"><a href="#cb11-4" aria-hidden="true"></a> [<span class="ex">COMMAND</span>] [AST]</span> @@ -236,12 +236,13 @@ <span id="cb16-5"><a href="#cb16-5" aria-hidden="true"></a><span class="in"> format=(PNG|PDF|SVG|JPG|EPS|GIF|TIF|WEBP|HTML) </span></span> <span id="cb16-6"><a href="#cb16-6" aria-hidden="true"></a><span class="in"> source=(true|false) </span></span> <span id="cb16-7"><a href="#cb16-7" aria-hidden="true"></a><span class="in"> preamble=(path) </span></span> -<span id="cb16-8"><a href="#cb16-8" aria-hidden="true"></a><span class="in"> dpi=(integer) </span></span> -<span id="cb16-9"><a href="#cb16-9" aria-hidden="true"></a><span class="in"> executable=(path) </span></span> -<span id="cb16-10"><a href="#cb16-10" aria-hidden="true"></a><span class="in"> caption_format=(text)</span></span> -<span id="cb16-11"><a href="#cb16-11" aria-hidden="true"></a><span class="in"> }</span></span> -<span id="cb16-12"><a href="#cb16-12" aria-hidden="true"></a><span class="in"> # script content</span></span> -<span id="cb16-13"><a href="#cb16-13" aria-hidden="true"></a><span class="in"> ```</span></span></code></pre></div> +<span id="cb16-8"><a href="#cb16-8" aria-hidden="true"></a><span class="in"> dpi=(integer)</span></span> +<span id="cb16-9"><a href="#cb16-9" aria-hidden="true"></a><span class="in"> dependencies=[...] </span></span> +<span id="cb16-10"><a href="#cb16-10" aria-hidden="true"></a><span class="in"> executable=(path) </span></span> +<span id="cb16-11"><a href="#cb16-11" aria-hidden="true"></a><span class="in"> caption_format=(text)</span></span> +<span id="cb16-12"><a href="#cb16-12" aria-hidden="true"></a><span class="in"> }</span></span> +<span id="cb16-13"><a href="#cb16-13" aria-hidden="true"></a><span class="in"> # script content</span></span> +<span id="cb16-14"><a href="#cb16-14" aria-hidden="true"></a><span class="in"> ```</span></span></code></pre></div> <ul> <li><code>cls</code> must be one of the following: <code>matplotlib</code>, <code>matlabplot</code>, <code>plotly_python</code>, <code>plotly_r</code>, <code>mathplot</code>, <code>octaveplot</code>, <code>ggplot2</code>, <code>gnuplot</code>, <code>graphviz</code>, <code>bokeh</code>, <code>plotsjl</code>.</li> </ul> @@ -254,6 +255,7 @@ <li><code>source</code> is a boolean toggle that determines whether the source code should be linked in the caption or not. Possible values are [<code>true</code>, <code>True</code>, <code>false</code>, <code>False</code>].</li> <li><code>preamble</code> 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 <code>pandoc-plot</code>.</li> <li><code>dpi</code> is the pixel density of the figure in dots-per-inch. Possible values are positive integers. Not all toolkits respect this.</li> +<li><code>dependencies</code> is a list of files/directories that affect the figure, for example data files. If one of those files/directories changes, <code>pandoc-plot</code> will re-render the associated figure. Format is a comma-separated list, e.g. <code>dependencies=[data.txt, foo.bar, ~/wtv]</code>. Values for <code>dependencies</code> in the configuration file will be appended to the values in a code block.</li> <li><code>executable</code> is a path to the executable to use (e.g. <code>C:\\python3.exe</code>) or the name of the executable (e.g. <code>python3</code>).</li> <li><code>caption_format</code> is the text format of the caption. Possible values are exactly the same as <code>pandoc</code>’s format specification, usually <code>FORMAT+EXTENSION-EXTENSION</code>. For example, captions in Markdown with raw LaTeX would be parsed correctly provided that <code>caption_format=markdown+raw_tex</code>. See Pandoc’s guide on <a href="https://pandoc.org/MANUAL.html#specifying-formats">Specifying formats</a>.</li> </ul> @@ -312,75 +314,82 @@ <span id="cb22-26"><a href="#cb22-26" aria-hidden="true"></a><span class="co"># individually as well.</span></span> <span id="cb22-27"><a href="#cb22-27" aria-hidden="true"></a><span class="fu">format</span><span class="kw">:</span><span class="at"> PNG</span></span> <span id="cb22-28"><a href="#cb22-28" aria-hidden="true"></a></span> -<span id="cb22-29"><a href="#cb22-29" aria-hidden="true"></a><span class="co"># Text format for the captions. Unfortunately, there is no way to detect</span></span> -<span id="cb22-30"><a href="#cb22-30" aria-hidden="true"></a><span class="co"># this automatically. You can use the same notation as Pandoc's --from </span></span> -<span id="cb22-31"><a href="#cb22-31" aria-hidden="true"></a><span class="co"># parameter, specified here: </span></span> -<span id="cb22-32"><a href="#cb22-32" aria-hidden="true"></a><span class="co"># https://pandoc.org/MANUAL.html#option--from</span></span> -<span id="cb22-33"><a href="#cb22-33" aria-hidden="true"></a><span class="co"># Example: markdown, rst+raw_tex</span></span> -<span id="cb22-34"><a href="#cb22-34" aria-hidden="true"></a><span class="fu">caption_format</span><span class="kw">:</span><span class="at"> markdown+tex_math_dollars</span></span> +<span id="cb22-29"><a href="#cb22-29" aria-hidden="true"></a><span class="co"># Default files/directories on which all figures depend. If any of these files/directories</span></span> +<span id="cb22-30"><a href="#cb22-30" aria-hidden="true"></a><span class="co"># changes, all figures will be re-rendered.</span></span> +<span id="cb22-31"><a href="#cb22-31" aria-hidden="true"></a><span class="co"># Dependencies specified in code blocks will be appended to this list.</span></span> +<span id="cb22-32"><a href="#cb22-32" aria-hidden="true"></a><span class="fu">dependencies</span><span class="kw">:</span></span> +<span id="cb22-33"><a href="#cb22-33" aria-hidden="true"></a><span class="at"> </span><span class="kw">-</span><span class="at"> file1.txt</span></span> +<span id="cb22-34"><a href="#cb22-34" aria-hidden="true"></a><span class="at"> </span><span class="kw">-</span><span class="at"> file2.txt</span></span> <span id="cb22-35"><a href="#cb22-35" aria-hidden="true"></a></span> -<span id="cb22-36"><a href="#cb22-36" aria-hidden="true"></a><span class="co"># Logging configuration</span></span> -<span id="cb22-37"><a href="#cb22-37" aria-hidden="true"></a><span class="fu">logging</span><span class="kw">:</span></span> -<span id="cb22-38"><a href="#cb22-38" aria-hidden="true"></a><span class="co"> # Possible verbosity values: debug, error, warning, info, silent</span></span> -<span id="cb22-39"><a href="#cb22-39" aria-hidden="true"></a><span class="at"> </span><span class="fu">verbosity</span><span class="kw">:</span><span class="at"> warning</span></span> -<span id="cb22-40"><a href="#cb22-40" aria-hidden="true"></a><span class="co"> # If the filepath below is not present, then pandoc-plot will log to stderr</span></span> -<span id="cb22-41"><a href="#cb22-41" aria-hidden="true"></a><span class="co"> # Otherwise, log messages will be appended to the filepath.</span></span> -<span id="cb22-42"><a href="#cb22-42" aria-hidden="true"></a><span class="co"> # filepath: path/to/file.txt</span></span> -<span id="cb22-43"><a href="#cb22-43" aria-hidden="true"></a></span> -<span id="cb22-44"><a href="#cb22-44" aria-hidden="true"></a><span class="co"># The possible parameters for the Matplotlib toolkit</span></span> -<span id="cb22-45"><a href="#cb22-45" aria-hidden="true"></a><span class="fu">matplotlib</span><span class="kw">:</span></span> -<span id="cb22-46"><a href="#cb22-46" aria-hidden="true"></a><span class="co"> # preamble: matplotlib.py</span></span> -<span id="cb22-47"><a href="#cb22-47" aria-hidden="true"></a><span class="at"> </span><span class="fu">tight_bbox</span><span class="kw">:</span><span class="at"> </span><span class="ch">false</span></span> -<span id="cb22-48"><a href="#cb22-48" aria-hidden="true"></a><span class="at"> </span><span class="fu">transparent</span><span class="kw">:</span><span class="at"> </span><span class="ch">false</span></span> -<span id="cb22-49"><a href="#cb22-49" aria-hidden="true"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> python</span></span> +<span id="cb22-36"><a href="#cb22-36" aria-hidden="true"></a><span class="co"># Text format for the captions. Unfortunately, there is no way to detect</span></span> +<span id="cb22-37"><a href="#cb22-37" aria-hidden="true"></a><span class="co"># this automatically. You can use the same notation as Pandoc's --from </span></span> +<span id="cb22-38"><a href="#cb22-38" aria-hidden="true"></a><span class="co"># parameter, specified here: </span></span> +<span id="cb22-39"><a href="#cb22-39" aria-hidden="true"></a><span class="co"># https://pandoc.org/MANUAL.html#option--from</span></span> +<span id="cb22-40"><a href="#cb22-40" aria-hidden="true"></a><span class="co"># Example: markdown, rst+raw_tex</span></span> +<span id="cb22-41"><a href="#cb22-41" aria-hidden="true"></a><span class="fu">caption_format</span><span class="kw">:</span><span class="at"> markdown+tex_math_dollars</span></span> +<span id="cb22-42"><a href="#cb22-42" aria-hidden="true"></a></span> +<span id="cb22-43"><a href="#cb22-43" aria-hidden="true"></a><span class="co"># Logging configuration</span></span> +<span id="cb22-44"><a href="#cb22-44" aria-hidden="true"></a><span class="fu">logging</span><span class="kw">:</span></span> +<span id="cb22-45"><a href="#cb22-45" aria-hidden="true"></a><span class="co"> # Possible verbosity values: debug, error, warning, info, silent</span></span> +<span id="cb22-46"><a href="#cb22-46" aria-hidden="true"></a><span class="at"> </span><span class="fu">verbosity</span><span class="kw">:</span><span class="at"> warning</span></span> +<span id="cb22-47"><a href="#cb22-47" aria-hidden="true"></a><span class="co"> # If the filepath below is not present, then pandoc-plot will log to stderr</span></span> +<span id="cb22-48"><a href="#cb22-48" aria-hidden="true"></a><span class="co"> # Otherwise, log messages will be appended to the filepath.</span></span> +<span id="cb22-49"><a href="#cb22-49" aria-hidden="true"></a><span class="co"> # filepath: path/to/file.txt</span></span> <span id="cb22-50"><a href="#cb22-50" aria-hidden="true"></a></span> -<span id="cb22-51"><a href="#cb22-51" aria-hidden="true"></a><span class="co"># The possible parameters for the MATLAB toolkit</span></span> -<span id="cb22-52"><a href="#cb22-52" aria-hidden="true"></a><span class="fu">matlabplot</span><span class="kw">:</span></span> -<span id="cb22-53"><a href="#cb22-53" aria-hidden="true"></a><span class="co"> # preamble: matlab.m</span></span> -<span id="cb22-54"><a href="#cb22-54" aria-hidden="true"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> matlab</span></span> -<span id="cb22-55"><a href="#cb22-55" aria-hidden="true"></a></span> -<span id="cb22-56"><a href="#cb22-56" aria-hidden="true"></a><span class="co"># The possible parameters for the Plotly/Python toolkit</span></span> -<span id="cb22-57"><a href="#cb22-57" aria-hidden="true"></a><span class="fu">plotly_python</span><span class="kw">:</span></span> -<span id="cb22-58"><a href="#cb22-58" aria-hidden="true"></a><span class="co"> # preamble: plotly-python.py</span></span> -<span id="cb22-59"><a href="#cb22-59" aria-hidden="true"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> python</span></span> -<span id="cb22-60"><a href="#cb22-60" aria-hidden="true"></a></span> -<span id="cb22-61"><a href="#cb22-61" aria-hidden="true"></a><span class="co"># The possible parameters for the Plotly/R toolkit</span></span> -<span id="cb22-62"><a href="#cb22-62" aria-hidden="true"></a><span class="fu">plotly_r</span><span class="kw">:</span></span> -<span id="cb22-63"><a href="#cb22-63" aria-hidden="true"></a><span class="co"> # preamble: plotly-r.r</span></span> -<span id="cb22-64"><a href="#cb22-64" aria-hidden="true"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> Rscript</span></span> -<span id="cb22-65"><a href="#cb22-65" aria-hidden="true"></a></span> -<span id="cb22-66"><a href="#cb22-66" aria-hidden="true"></a><span class="co"># The possible parameters for the Mathematica toolkit</span></span> -<span id="cb22-67"><a href="#cb22-67" aria-hidden="true"></a><span class="fu">mathplot</span><span class="kw">:</span></span> -<span id="cb22-68"><a href="#cb22-68" aria-hidden="true"></a><span class="co"> # preamble: mathematica.m</span></span> -<span id="cb22-69"><a href="#cb22-69" aria-hidden="true"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> math</span></span> -<span id="cb22-70"><a href="#cb22-70" aria-hidden="true"></a></span> -<span id="cb22-71"><a href="#cb22-71" aria-hidden="true"></a><span class="co"># The possible parameters for the GNU Octave toolkit</span></span> -<span id="cb22-72"><a href="#cb22-72" aria-hidden="true"></a><span class="fu">octaveplot</span><span class="kw">:</span></span> -<span id="cb22-73"><a href="#cb22-73" aria-hidden="true"></a><span class="co"> # preamble: octave.m</span></span> -<span id="cb22-74"><a href="#cb22-74" aria-hidden="true"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> octave</span></span> -<span id="cb22-75"><a href="#cb22-75" aria-hidden="true"></a></span> -<span id="cb22-76"><a href="#cb22-76" aria-hidden="true"></a><span class="co"># The possible parameters for the ggplot2 toolkit</span></span> -<span id="cb22-77"><a href="#cb22-77" aria-hidden="true"></a><span class="fu">ggplot2</span><span class="kw">:</span></span> -<span id="cb22-78"><a href="#cb22-78" aria-hidden="true"></a><span class="co"> # preamble: ggplot2.r</span></span> -<span id="cb22-79"><a href="#cb22-79" aria-hidden="true"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> Rscript</span></span> -<span id="cb22-80"><a href="#cb22-80" aria-hidden="true"></a></span> -<span id="cb22-81"><a href="#cb22-81" aria-hidden="true"></a><span class="co"># The possible parameters for the gnuplot toolkit</span></span> -<span id="cb22-82"><a href="#cb22-82" aria-hidden="true"></a><span class="fu">gnuplot</span><span class="kw">:</span></span> -<span id="cb22-83"><a href="#cb22-83" aria-hidden="true"></a><span class="co"> # preamble: gnuplot.gp</span></span> -<span id="cb22-84"><a href="#cb22-84" aria-hidden="true"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> gnuplot</span></span> -<span id="cb22-85"><a href="#cb22-85" aria-hidden="true"></a></span> -<span id="cb22-86"><a href="#cb22-86" aria-hidden="true"></a><span class="co"># The possible parameters for the graphviz toolkit</span></span> -<span id="cb22-87"><a href="#cb22-87" aria-hidden="true"></a><span class="fu">graphviz</span><span class="kw">:</span></span> -<span id="cb22-88"><a href="#cb22-88" aria-hidden="true"></a><span class="co"> # preamble: graphviz.dot</span></span> -<span id="cb22-89"><a href="#cb22-89" aria-hidden="true"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> dot</span></span> -<span id="cb22-90"><a href="#cb22-90" aria-hidden="true"></a></span> -<span id="cb22-91"><a href="#cb22-91" aria-hidden="true"></a><span class="fu">bokeh</span><span class="kw">:</span></span> -<span id="cb22-92"><a href="#cb22-92" aria-hidden="true"></a><span class="co"> # preamble: bokeh.py</span></span> -<span id="cb22-93"><a href="#cb22-93" aria-hidden="true"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> python</span></span> -<span id="cb22-94"><a href="#cb22-94" aria-hidden="true"></a></span> -<span id="cb22-95"><a href="#cb22-95" aria-hidden="true"></a><span class="fu">plotsjl</span><span class="kw">:</span></span> -<span id="cb22-96"><a href="#cb22-96" aria-hidden="true"></a><span class="co"> # preamble: plotsjl.jl</span></span> -<span id="cb22-97"><a href="#cb22-97" aria-hidden="true"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> julia</span></span></code></pre></div> +<span id="cb22-51"><a href="#cb22-51" aria-hidden="true"></a><span class="co"># The possible parameters for the Matplotlib toolkit</span></span> +<span id="cb22-52"><a href="#cb22-52" aria-hidden="true"></a><span class="fu">matplotlib</span><span class="kw">:</span></span> +<span id="cb22-53"><a href="#cb22-53" aria-hidden="true"></a><span class="co"> # preamble: matplotlib.py</span></span> +<span id="cb22-54"><a href="#cb22-54" aria-hidden="true"></a><span class="at"> </span><span class="fu">tight_bbox</span><span class="kw">:</span><span class="at"> </span><span class="ch">false</span></span> +<span id="cb22-55"><a href="#cb22-55" aria-hidden="true"></a><span class="at"> </span><span class="fu">transparent</span><span class="kw">:</span><span class="at"> </span><span class="ch">false</span></span> +<span id="cb22-56"><a href="#cb22-56" aria-hidden="true"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> python</span></span> +<span id="cb22-57"><a href="#cb22-57" aria-hidden="true"></a></span> +<span id="cb22-58"><a href="#cb22-58" aria-hidden="true"></a><span class="co"># The possible parameters for the MATLAB toolkit</span></span> +<span id="cb22-59"><a href="#cb22-59" aria-hidden="true"></a><span class="fu">matlabplot</span><span class="kw">:</span></span> +<span id="cb22-60"><a href="#cb22-60" aria-hidden="true"></a><span class="co"> # preamble: matlab.m</span></span> +<span id="cb22-61"><a href="#cb22-61" aria-hidden="true"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> matlab</span></span> +<span id="cb22-62"><a href="#cb22-62" aria-hidden="true"></a></span> +<span id="cb22-63"><a href="#cb22-63" aria-hidden="true"></a><span class="co"># The possible parameters for the Plotly/Python toolkit</span></span> +<span id="cb22-64"><a href="#cb22-64" aria-hidden="true"></a><span class="fu">plotly_python</span><span class="kw">:</span></span> +<span id="cb22-65"><a href="#cb22-65" aria-hidden="true"></a><span class="co"> # preamble: plotly-python.py</span></span> +<span id="cb22-66"><a href="#cb22-66" aria-hidden="true"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> python</span></span> +<span id="cb22-67"><a href="#cb22-67" aria-hidden="true"></a></span> +<span id="cb22-68"><a href="#cb22-68" aria-hidden="true"></a><span class="co"># The possible parameters for the Plotly/R toolkit</span></span> +<span id="cb22-69"><a href="#cb22-69" aria-hidden="true"></a><span class="fu">plotly_r</span><span class="kw">:</span></span> +<span id="cb22-70"><a href="#cb22-70" aria-hidden="true"></a><span class="co"> # preamble: plotly-r.r</span></span> +<span id="cb22-71"><a href="#cb22-71" aria-hidden="true"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> Rscript</span></span> +<span id="cb22-72"><a href="#cb22-72" aria-hidden="true"></a></span> +<span id="cb22-73"><a href="#cb22-73" aria-hidden="true"></a><span class="co"># The possible parameters for the Mathematica toolkit</span></span> +<span id="cb22-74"><a href="#cb22-74" aria-hidden="true"></a><span class="fu">mathplot</span><span class="kw">:</span></span> +<span id="cb22-75"><a href="#cb22-75" aria-hidden="true"></a><span class="co"> # preamble: mathematica.m</span></span> +<span id="cb22-76"><a href="#cb22-76" aria-hidden="true"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> math</span></span> +<span id="cb22-77"><a href="#cb22-77" aria-hidden="true"></a></span> +<span id="cb22-78"><a href="#cb22-78" aria-hidden="true"></a><span class="co"># The possible parameters for the GNU Octave toolkit</span></span> +<span id="cb22-79"><a href="#cb22-79" aria-hidden="true"></a><span class="fu">octaveplot</span><span class="kw">:</span></span> +<span id="cb22-80"><a href="#cb22-80" aria-hidden="true"></a><span class="co"> # preamble: octave.m</span></span> +<span id="cb22-81"><a href="#cb22-81" aria-hidden="true"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> octave</span></span> +<span id="cb22-82"><a href="#cb22-82" aria-hidden="true"></a></span> +<span id="cb22-83"><a href="#cb22-83" aria-hidden="true"></a><span class="co"># The possible parameters for the ggplot2 toolkit</span></span> +<span id="cb22-84"><a href="#cb22-84" aria-hidden="true"></a><span class="fu">ggplot2</span><span class="kw">:</span></span> +<span id="cb22-85"><a href="#cb22-85" aria-hidden="true"></a><span class="co"> # preamble: ggplot2.r</span></span> +<span id="cb22-86"><a href="#cb22-86" aria-hidden="true"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> Rscript</span></span> +<span id="cb22-87"><a href="#cb22-87" aria-hidden="true"></a></span> +<span id="cb22-88"><a href="#cb22-88" aria-hidden="true"></a><span class="co"># The possible parameters for the gnuplot toolkit</span></span> +<span id="cb22-89"><a href="#cb22-89" aria-hidden="true"></a><span class="fu">gnuplot</span><span class="kw">:</span></span> +<span id="cb22-90"><a href="#cb22-90" aria-hidden="true"></a><span class="co"> # preamble: gnuplot.gp</span></span> +<span id="cb22-91"><a href="#cb22-91" aria-hidden="true"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> gnuplot</span></span> +<span id="cb22-92"><a href="#cb22-92" aria-hidden="true"></a></span> +<span id="cb22-93"><a href="#cb22-93" aria-hidden="true"></a><span class="co"># The possible parameters for the graphviz toolkit</span></span> +<span id="cb22-94"><a href="#cb22-94" aria-hidden="true"></a><span class="fu">graphviz</span><span class="kw">:</span></span> +<span id="cb22-95"><a href="#cb22-95" aria-hidden="true"></a><span class="co"> # preamble: graphviz.dot</span></span> +<span id="cb22-96"><a href="#cb22-96" aria-hidden="true"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> dot</span></span> +<span id="cb22-97"><a href="#cb22-97" aria-hidden="true"></a></span> +<span id="cb22-98"><a href="#cb22-98" aria-hidden="true"></a><span class="fu">bokeh</span><span class="kw">:</span></span> +<span id="cb22-99"><a href="#cb22-99" aria-hidden="true"></a><span class="co"> # preamble: bokeh.py</span></span> +<span id="cb22-100"><a href="#cb22-100" aria-hidden="true"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> python</span></span> +<span id="cb22-101"><a href="#cb22-101" aria-hidden="true"></a></span> +<span id="cb22-102"><a href="#cb22-102" aria-hidden="true"></a><span class="fu">plotsjl</span><span class="kw">:</span></span> +<span id="cb22-103"><a href="#cb22-103" aria-hidden="true"></a><span class="co"> # preamble: plotsjl.jl</span></span> +<span id="cb22-104"><a href="#cb22-104" aria-hidden="true"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> julia</span></span></code></pre></div> <p>A file like the above sets the <strong>default</strong> values; you can still override them in documents directly.</p> <p>The easiest way to specify configuration for <code>pandoc-plot</code> is to place a <code>.pandoc-plot.yml</code> file in the current working directory. You can also specify a configuration file in document metadata, under the <code>plot-configuration</code> key. For example, in Markdown:</p> <div class="sourceCode" id="cb23"><pre class="sourceCode markdown"><code class="sourceCode markdown"><span id="cb23-1"><a href="#cb23-1" aria-hidden="true"></a><span class="co">---</span></span>
example-config.yml view
@@ -26,6 +26,13 @@ # individually as well. format: PNG +# Default files/directories on which all figures depend. If any of these files/directories +# changes, all figures will be re-rendered. +# Dependencies specified in code blocks will be appended to this list. +dependencies: + - file1.txt + - file2.txt + # 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:
pandoc-plot.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.2 name: pandoc-plot -version: 0.8.1.0 +version: 0.9.0.0 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: Text
src/Text/Pandoc/Filter/Plot.hs view
@@ -50,6 +50,7 @@ * @format=...@: Format of the generated figure. This can be an extension or an acronym, e.g. @format=PNG@. * @caption="..."@: Specify a plot caption (or alternate text). Format for captions is specified in the documentation for the @Configuration@ type. * @dpi=...@: Specify a value for figure resolution, or dots-per-inch. Certain toolkits ignore this. + * @dependencies=[...]@: Specify files/directories on which a figure depends, e.g. data file. Figures will be re-rendered if one of those file/directory changes. * @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
src/Text/Pandoc/Filter/Plot/Configuration.hs view
@@ -44,14 +44,15 @@ defaultConfiguration :: Configuration defaultConfiguration = Configuration - { defaultDirectory = "plots/" - , defaultWithSource = False - , defaultDPI = 80 - , defaultSaveFormat = PNG - , captionFormat = Format "markdown+tex_math_dollars" + { defaultDirectory = "plots/" + , defaultWithSource = False + , defaultDPI = 80 + , defaultSaveFormat = PNG + , defaultDependencies = mempty + , captionFormat = Format "markdown+tex_math_dollars" - , logVerbosity = Warning - , logSink = StdErr + , logVerbosity = Warning + , logSink = StdErr , matplotlibPreamble = mempty , plotlyPythonPreamble= mempty @@ -63,7 +64,7 @@ , gnuplotPreamble = mempty , graphvizPreamble = mempty , bokehPreamble = mempty - , plotsjlPreamble = mempty + , plotsjlPreamble = mempty , matplotlibExe = python , matlabExe = "matlab" @@ -75,7 +76,7 @@ , gnuplotExe = "gnuplot" , graphvizExe = "dot" , bokehExe = python - , plotsjlExe = "julia" + , plotsjlExe = "julia" , matplotlibTightBBox = False , matplotlibTransparent = False @@ -114,49 +115,51 @@ -- but we want to read those files before building a full -- @Configuration@ value. data ConfigPrecursor = ConfigPrecursor - { _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 - , _defaultDPI :: !Int -- ^ The default dots-per-inch value for generated figures. Renderers might ignore this. - , _defaultSaveFormat :: !SaveFormat -- ^ The default save format of generated figures. - , _captionFormat :: !Format -- ^ Caption format in Pandoc notation, e.g. "markdown+tex_math_dollars". + { _defaultDirectory :: !FilePath + , _defaultWithSource :: !Bool + , _defaultDPI :: !Int + , _defaultSaveFormat :: !SaveFormat + , _defaultDependencies :: ![FilePath] + , _captionFormat :: !Format - , _logPrec :: !LoggingPrecursor + , _logPrec :: !LoggingPrecursor - , _matplotlibPrec :: !MatplotlibPrecursor - , _matlabPrec :: !MatlabPrecursor - , _plotlyPythonPrec :: !PlotlyPythonPrecursor - , _plotlyRPrec :: !PlotlyRPrecursor - , _mathematicaPrec :: !MathematicaPrecursor - , _octavePrec :: !OctavePrecursor - , _ggplot2Prec :: !GGPlot2Precursor - , _gnuplotPrec :: !GNUPlotPrecursor - , _graphvizPrec :: !GraphvizPrecursor - , _bokehPrec :: !BokehPrecursor - , _plotsjlPrec :: !PlotsjlPrecursor + , _matplotlibPrec :: !MatplotlibPrecursor + , _matlabPrec :: !MatlabPrecursor + , _plotlyPythonPrec :: !PlotlyPythonPrecursor + , _plotlyRPrec :: !PlotlyRPrecursor + , _mathematicaPrec :: !MathematicaPrecursor + , _octavePrec :: !OctavePrecursor + , _ggplot2Prec :: !GGPlot2Precursor + , _gnuplotPrec :: !GNUPlotPrecursor + , _graphvizPrec :: !GraphvizPrecursor + , _bokehPrec :: !BokehPrecursor + , _plotsjlPrec :: !PlotsjlPrecursor } defaultConfigPrecursor :: ConfigPrecursor defaultConfigPrecursor = ConfigPrecursor - { _defaultDirectory = defaultDirectory defaultConfiguration - , _defaultWithSource = defaultWithSource defaultConfiguration - , _defaultDPI = defaultDPI defaultConfiguration - , _defaultSaveFormat = defaultSaveFormat defaultConfiguration - , _captionFormat = captionFormat defaultConfiguration + { _defaultDirectory = defaultDirectory defaultConfiguration + , _defaultWithSource = defaultWithSource defaultConfiguration + , _defaultDPI = defaultDPI defaultConfiguration + , _defaultSaveFormat = defaultSaveFormat defaultConfiguration + , _defaultDependencies = defaultDependencies defaultConfiguration + , _captionFormat = captionFormat defaultConfiguration - , _logPrec = LoggingPrecursor (logVerbosity defaultConfiguration) Nothing -- _logFilePath=Nothing implies log to stderr + , _logPrec = LoggingPrecursor (logVerbosity defaultConfiguration) Nothing -- _logFilePath=Nothing implies log to stderr - , _matplotlibPrec = MatplotlibPrecursor Nothing (matplotlibTightBBox defaultConfiguration) (matplotlibTransparent defaultConfiguration) (matplotlibExe defaultConfiguration) - , _matlabPrec = MatlabPrecursor Nothing (matlabExe defaultConfiguration) - , _plotlyPythonPrec = PlotlyPythonPrecursor Nothing (plotlyPythonExe defaultConfiguration) - , _plotlyRPrec = PlotlyRPrecursor Nothing (plotlyRExe defaultConfiguration) - , _mathematicaPrec = MathematicaPrecursor Nothing (mathematicaExe defaultConfiguration) - , _octavePrec = OctavePrecursor Nothing (octaveExe defaultConfiguration) - , _ggplot2Prec = GGPlot2Precursor Nothing (ggplot2Exe defaultConfiguration) - , _gnuplotPrec = GNUPlotPrecursor Nothing (gnuplotExe defaultConfiguration) - , _graphvizPrec = GraphvizPrecursor Nothing (graphvizExe defaultConfiguration) - , _bokehPrec = BokehPrecursor Nothing (bokehExe defaultConfiguration) - , _plotsjlPrec = PlotsjlPrecursor Nothing (plotsjlExe defaultConfiguration) + , _matplotlibPrec = MatplotlibPrecursor Nothing (matplotlibTightBBox defaultConfiguration) (matplotlibTransparent defaultConfiguration) (matplotlibExe defaultConfiguration) + , _matlabPrec = MatlabPrecursor Nothing (matlabExe defaultConfiguration) + , _plotlyPythonPrec = PlotlyPythonPrecursor Nothing (plotlyPythonExe defaultConfiguration) + , _plotlyRPrec = PlotlyRPrecursor Nothing (plotlyRExe defaultConfiguration) + , _mathematicaPrec = MathematicaPrecursor Nothing (mathematicaExe defaultConfiguration) + , _octavePrec = OctavePrecursor Nothing (octaveExe defaultConfiguration) + , _ggplot2Prec = GGPlot2Precursor Nothing (ggplot2Exe defaultConfiguration) + , _gnuplotPrec = GNUPlotPrecursor Nothing (gnuplotExe defaultConfiguration) + , _graphvizPrec = GraphvizPrecursor Nothing (graphvizExe defaultConfiguration) + , _bokehPrec = BokehPrecursor Nothing (bokehExe defaultConfiguration) + , _plotsjlPrec = PlotsjlPrecursor Nothing (plotsjlExe defaultConfiguration) } @@ -242,25 +245,26 @@ parseJSON (Null) = return defaultConfigPrecursor -- In case of empty file parseJSON (Object v) = do - _defaultDirectory <- v .:? (tshow DirectoryK) .!= (_defaultDirectory defaultConfigPrecursor) - _defaultWithSource <- v .:? (tshow WithSourceK) .!= (_defaultWithSource defaultConfigPrecursor) - _defaultDPI <- v .:? (tshow DpiK) .!= (_defaultDPI defaultConfigPrecursor) - _defaultSaveFormat <- v .:? (tshow SaveFormatK) .!= (_defaultSaveFormat defaultConfigPrecursor) - _captionFormat <- v .:? (tshow CaptionFormatK) .!= (_captionFormat defaultConfigPrecursor) + _defaultDirectory <- v .:? (tshow DirectoryK) .!= (_defaultDirectory defaultConfigPrecursor) + _defaultWithSource <- v .:? (tshow WithSourceK) .!= (_defaultWithSource defaultConfigPrecursor) + _defaultDPI <- v .:? (tshow DpiK) .!= (_defaultDPI defaultConfigPrecursor) + _defaultSaveFormat <- v .:? (tshow SaveFormatK) .!= (_defaultSaveFormat defaultConfigPrecursor) + _defaultDependencies <- v .:? (tshow DependenciesK) .!= (_defaultDependencies defaultConfigPrecursor) + _captionFormat <- v .:? (tshow CaptionFormatK) .!= (_captionFormat defaultConfigPrecursor) - _logPrec <- v .:? "logging" .!= _logPrec defaultConfigPrecursor + _logPrec <- v .:? "logging" .!= _logPrec defaultConfigPrecursor - _matplotlibPrec <- v .:? (cls Matplotlib) .!= _matplotlibPrec defaultConfigPrecursor - _matlabPrec <- v .:? (cls Matlab) .!= _matlabPrec defaultConfigPrecursor - _plotlyPythonPrec <- v .:? (cls PlotlyPython) .!= _plotlyPythonPrec defaultConfigPrecursor - _plotlyRPrec <- v .:? (cls PlotlyR) .!= _plotlyRPrec defaultConfigPrecursor - _mathematicaPrec <- v .:? (cls Mathematica) .!= _mathematicaPrec defaultConfigPrecursor - _octavePrec <- v .:? (cls Octave) .!= _octavePrec defaultConfigPrecursor - _ggplot2Prec <- v .:? (cls GGPlot2) .!= _ggplot2Prec defaultConfigPrecursor - _gnuplotPrec <- v .:? (cls GNUPlot) .!= _gnuplotPrec defaultConfigPrecursor - _graphvizPrec <- v .:? (cls Graphviz) .!= _graphvizPrec defaultConfigPrecursor - _bokehPrec <- v .:? (cls Bokeh) .!= _bokehPrec defaultConfigPrecursor - _plotsjlPrec <- v .:? (cls Plotsjl) .!= _plotsjlPrec defaultConfigPrecursor + _matplotlibPrec <- v .:? (cls Matplotlib) .!= _matplotlibPrec defaultConfigPrecursor + _matlabPrec <- v .:? (cls Matlab) .!= _matlabPrec defaultConfigPrecursor + _plotlyPythonPrec <- v .:? (cls PlotlyPython) .!= _plotlyPythonPrec defaultConfigPrecursor + _plotlyRPrec <- v .:? (cls PlotlyR) .!= _plotlyRPrec defaultConfigPrecursor + _mathematicaPrec <- v .:? (cls Mathematica) .!= _mathematicaPrec defaultConfigPrecursor + _octavePrec <- v .:? (cls Octave) .!= _octavePrec defaultConfigPrecursor + _ggplot2Prec <- v .:? (cls GGPlot2) .!= _ggplot2Prec defaultConfigPrecursor + _gnuplotPrec <- v .:? (cls GNUPlot) .!= _gnuplotPrec defaultConfigPrecursor + _graphvizPrec <- v .:? (cls Graphviz) .!= _graphvizPrec defaultConfigPrecursor + _bokehPrec <- v .:? (cls Bokeh) .!= _bokehPrec defaultConfigPrecursor + _plotsjlPrec <- v .:? (cls Plotsjl) .!= _plotsjlPrec defaultConfigPrecursor return $ ConfigPrecursor{..} parseJSON _ = fail "Could not parse configuration." @@ -268,14 +272,15 @@ renderConfig :: ConfigPrecursor -> IO Configuration renderConfig ConfigPrecursor{..} = do - let defaultDirectory = _defaultDirectory - defaultWithSource = _defaultWithSource - defaultDPI = _defaultDPI - defaultSaveFormat = _defaultSaveFormat - captionFormat = _captionFormat + let defaultDirectory = _defaultDirectory + defaultWithSource = _defaultWithSource + defaultDPI = _defaultDPI + defaultSaveFormat = _defaultSaveFormat + defaultDependencies = _defaultDependencies + captionFormat = _captionFormat - logVerbosity = _logVerbosity _logPrec - logSink = maybe StdErr LogFile (_logFilePath _logPrec) + logVerbosity = _logVerbosity _logPrec + logSink = maybe StdErr LogFile (_logFilePath _logPrec) matplotlibTightBBox = _matplotlibTightBBox _matplotlibPrec matplotlibTransparent = _matplotlibTransparent _matplotlibPrec
src/Text/Pandoc/Filter/Plot/Embed.hs view
@@ -17,6 +17,7 @@ ) where import Data.Default (def) +import Data.List (nub) import Data.Maybe (fromMaybe) import Data.Text (Text, pack) import qualified Data.Text.IO as T @@ -45,18 +46,19 @@ toFigure :: Format -- ^ text format of the caption -> FigureSpec -> PlotM Block -toFigure fmt spec = builder attrs' target' caption' - where - builder = if saveFormat spec == HTML - then interactiveBlock - else figure +toFigure fmt spec = do + target <- figurePath spec + let srcLink = link (pack $ replaceExtension target ".txt") mempty "Source code" attrs' = blockAttrs spec - target' = figurePath spec withSource' = withSource spec - srcLink = link (pack $ replaceExtension target' ".txt") mempty "Source code" captionText = fromList $ fromMaybe mempty (captionReader fmt $ caption spec) captionLinks = mconcat [" (", srcLink, ")"] caption' = if withSource' then captionText <> captionLinks else captionText + builder attrs' target caption' + where + builder = if saveFormat spec == HTML + then interactiveBlock + else figure figure :: Attr @@ -67,7 +69,7 @@ -- must be "fig:" -- Janky? yes figure as fp caption' = return . head . toList . para $ - imageWith as (pack fp) "fig:" caption' + imageWith as (pack fp) "fig:" caption' interactiveBlock :: Attr @@ -75,6 +77,9 @@ -> Inlines -> PlotM Block interactiveBlock _ fp caption' = do + -- TODO: should we instead include the scripts in the "include-after" + -- template variable? + -- See https://github.com/jgm/pandoc/issues/6582 htmlpage <- liftIO $ T.readFile fp renderedCaption <- writeHtml caption' return $ RawBlock "html5" [st| @@ -99,19 +104,44 @@ -- <body> tag. extractPlot :: Text -> Text extractPlot t = let tags = canonicalizeTags $ parseTagsOptions parseOptionsFast t - in mconcat $ renderTags <$> (headScripts tags <> [htmlBody tags]) + extracted = headScripts tags <> [inside "body" $ tags] + in mconcat $ renderTags <$> (deferScripts <$> extracted) where - headScripts = partitions (~== ("<script>"::String)) . htmlHead + headScripts = partitions (~== ("<script>"::String)) . inside "head" + +-- | Get content inside a tag, e.g. /inside "body"/ returns all tags +-- between /<body>/ and /</body>/ inside :: Text -> [Tag Text] -> [Tag Text] inside t = init . tail . tgs where tgs = takeWhile (~/= TagClose t) . dropWhile (~/= TagOpen t []) -htmlHead :: [Tag Text] -> [Tag Text] -htmlHead = inside "head" +data ScriptTag = InlineScript [Attribute Text] + | ExternalScript [Attribute Text] -htmlBody :: [Tag Text] -> [Tag Text] -htmlBody = inside "body"+fromTag :: Tag Text -> Maybe ScriptTag +fromTag (TagOpen "script" attrs) = + Just $ if "src" `elem` (fst . unzip $ attrs) + then ExternalScript attrs + else InlineScript attrs +fromTag _ = Nothing + + +toTag :: ScriptTag -> Tag Text +toTag (InlineScript t) = TagOpen "script" t +toTag (ExternalScript t) = TagOpen "script" t + + +deferScript :: ScriptTag -> ScriptTag +deferScript (InlineScript attrs) = InlineScript $ nub $ attrs <> [("type", "module")] +deferScript (ExternalScript attrs) = ExternalScript $ nub $ attrs <> [("defer", mempty)] + + +-- | Replace /<script src=...>/ tags with /<script src=... defer>/, +-- and inline scripts as /<script type="module">/. +-- This makes scripts execute only after HTML parsing has finished. +deferScripts :: [Tag Text] -> [Tag Text] +deferScripts = fmap (\t -> maybe t (toTag . deferScript) (fromTag t))
src/Text/Pandoc/Filter/Plot/Monad.hs view
@@ -17,6 +17,8 @@ , runPlotM -- * Running external commands , runCommand + -- * Getting file hashes + , fileHash -- * Logging , Verbosity(..) , LogSink(..) @@ -36,20 +38,26 @@ import Control.Concurrent.Chan (writeChan) +import Control.Concurrent.MVar import Control.Monad.Reader +import Control.Monad.State.Strict import Data.ByteString.Lazy (toStrict) +import Data.Hashable (hash) +import Data.Map.Strict (Map) +import qualified Data.Map.Strict as M + import Data.Text (Text, pack, unpack) import qualified Data.Text as T import Data.Text.Encoding (decodeUtf8With) import Data.Text.Encoding.Error (lenientDecode) +import System.Directory (doesFileExist, getModificationTime) import System.Exit (ExitCode (..)) import System.Process.Typed ( readProcessStderr, shell, nullStream , setStdout, setStderr, byteStringOutput ) - import Text.Pandoc.Definition (Format(..)) import Prelude hiding (log, fst, snd) @@ -59,7 +67,7 @@ -- | pandoc-plot monad -type PlotM a = ReaderT RuntimeEnv IO a +type PlotM a = StateT PlotState (ReaderT RuntimeEnv IO) a data RuntimeEnv = @@ -80,11 +88,12 @@ -- | Evaluate a @PlotM@ action. runPlotM :: Configuration -> PlotM a -> IO a -runPlotM conf v = +runPlotM conf v = do + st <- newMVar mempty let verbosity = logVerbosity conf sink = logSink conf - in withLogger verbosity sink $ - \logger -> runReaderT v (RuntimeEnv conf logger) + withLogger verbosity sink $ + \logger -> runReaderT (evalStateT v st) (RuntimeEnv conf logger) debug, err, warning, info :: Text -> PlotM () @@ -137,6 +146,45 @@ return (ec, processOutput) +-- Plot state consists of a map of filepaths to hashes +-- This allows multiple plots to depend on the same file/directory, and the file hashes +-- will only be calculated once. This is OK because pandoc-plot will not run for long. +-- We note that because figures are rendered possibly in parallel, access to +-- the state must be synchronized; otherwise, each thread might compute its own +-- hashes. +type FileHash = Word +type PlotState = MVar (Map FilePath FileHash) + + +-- | Get a filehash. If the file hash has been computed before, +-- it is reused. Otherwise, the filehash is calculated and stored. +fileHash :: FilePath -> PlotM FileHash +fileHash path = do + var <- get + hashes <- liftIO $ takeMVar var + (fh, hashes') <- case M.lookup path hashes of + Nothing -> do + debug $ mconcat ["Calculating hash of dependency ", pack path] + fh <- fileHash' path + let hashes' = M.insert path fh hashes + return (fh, hashes') + Just h -> do + debug $ mconcat ["Hash of dependency ", pack path, " already calculated."] + return (h, hashes) + liftIO $ putMVar var hashes' + put var + return fh + where + -- As a proxy for the state of a file dependency, we use the modification time + -- This is much faster than actual file hashing + fileHash' :: FilePath -> PlotM FileHash + fileHash' fp = do + fileExists <- liftIO $ doesFileExist fp + if fileExists + then liftIO . fmap (fromIntegral . hash . show) . getModificationTime $ fp + else err (mconcat ["Dependency ", pack fp, " does not exist."]) >> return 0 + + -- | The @Configuration@ type holds the default values to use -- when running pandoc-plot. These values can be overridden in code blocks. -- @@ -162,6 +210,7 @@ , defaultWithSource :: !Bool -- ^ The default behavior of whether or not to include links to source code and high-res , defaultDPI :: !Int -- ^ The default dots-per-inch value for generated figures. Renderers might ignore this. , defaultSaveFormat :: !SaveFormat -- ^ The default save format of generated figures. + , defaultDependencies :: ![FilePath] -- ^ List of files/directories on which all figures depend. , captionFormat :: !Format -- ^ Caption format, in the same notation as Pandoc format, e.g. "markdown+tex_math_dollars" , logVerbosity :: !Verbosity -- ^ Level of logging verbosity. @@ -177,7 +226,7 @@ , gnuplotPreamble :: !Script -- ^ The default preamble script for the gnuplot toolkit. , graphvizPreamble :: !Script -- ^ The default preamble script for the Graphviz toolkit. , bokehPreamble :: !Script -- ^ The default preamble script for the Python/Bokeh toolkit. - , plotsjlPreamble :: !Script -- ^ The default preamble script for the Julia/Plots.jl toolkit. + , plotsjlPreamble :: !Script -- ^ The default preamble script for the Julia/Plots.jl 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. @@ -189,7 +238,7 @@ , 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. , bokehExe :: !FilePath -- ^ The executable to use to generate figures using the Python/Bokeh toolkit. - , plotsjlExe :: !FilePath -- ^ The executable to use to generate figures using the Julia/Plots.jl toolkit. + , plotsjlExe :: !FilePath -- ^ The executable to use to generate figures using the Julia/Plots.jl 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.
src/Text/Pandoc/Filter/Plot/Monad/Types.hs view
@@ -21,7 +21,6 @@ , InclusionKey(..) , FigureSpec(..) , SaveFormat(..) - , figureContentHash , cls , extension , toolkits @@ -31,7 +30,6 @@ ) where import Data.Char (toLower) -import Data.Hashable (hash) import Data.List (intersperse) import Data.String (IsString (..)) import Data.Text (Text) @@ -123,6 +121,7 @@ | PreambleK | DpiK | ExecutableK + | DependenciesK | MatplotlibTightBBoxK | MatplotlibTransparentK deriving (Bounded, Eq, Enum) @@ -138,6 +137,7 @@ show PreambleK = "preamble" show DpiK = "dpi" show ExecutableK = "executable" + show DependenciesK = "dependencies" show MatplotlibTightBBoxK = "tight_bbox" show MatplotlibTransparentK = "transparent" @@ -153,26 +153,17 @@ -- It is assumed that once a @FigureSpec@ has been created, no configuration -- can overload it; hence, a @FigureSpec@ completely encodes a particular figure. data FigureSpec = FigureSpec - { toolkit :: !Toolkit -- ^ Plotting toolkit to use for this figure. - , caption :: !Text -- ^ Figure caption. - , withSource :: !Bool -- ^ Append link to source code in caption. - , script :: !Script -- ^ Source code for the figure. - , saveFormat :: !SaveFormat -- ^ Save format of the figure. - , directory :: !FilePath -- ^ Directory where to save the file. - , dpi :: !Int -- ^ Dots-per-inch of figure. - , extraAttrs :: ![(Text, Text)] -- ^ Renderer-specific extra attributes. - , blockAttrs :: !Attr -- ^ Attributes not related to @pandoc-plot@ will be propagated. + { toolkit :: !Toolkit -- ^ Plotting toolkit to use for this figure. + , caption :: !Text -- ^ Figure caption. + , withSource :: !Bool -- ^ Append link to source code in caption. + , script :: !Script -- ^ Source code for the figure. + , saveFormat :: !SaveFormat -- ^ Save format of the figure. + , directory :: !FilePath -- ^ Directory where to save the file. + , dpi :: !Int -- ^ Dots-per-inch of figure. + , dependencies :: ![FilePath] -- ^ Files/directories on which this figure depends, e.g. data files. + , extraAttrs :: ![(Text, Text)] -- ^ Renderer-specific extra attributes. + , blockAttrs :: !Attr -- ^ Attributes not related to @pandoc-plot@ will be propagated. } - - --- | 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 (fromEnum toolkit, script, fromEnum saveFormat, directory, dpi, extraAttrs) -- | Generated figure file format supported by pandoc-plot.
src/Text/Pandoc/Filter/Plot/Parse.hs view
@@ -23,18 +23,20 @@ import Control.Monad (join, when) +import Data.Char (isSpace) import Data.Default (def) import Data.List (intersperse) import qualified Data.Map.Strict as Map import Data.Maybe (fromMaybe, listToMaybe) import Data.String (fromString) import Data.Text (Text, pack, unpack) +import qualified Data.Text as T import qualified Data.Text.IO as TIO import Data.Version (showVersion) import Paths_pandoc_plot (version) -import System.FilePath (makeValid) +import System.FilePath (makeValid, normalise) import Text.Pandoc.Definition (Block (..), Inline, Pandoc (..), Format(..)) @@ -90,8 +92,11 @@ directory = makeValid $ unpack $ Map.findWithDefault (pack $ defaultDirectory conf) (tshow DirectoryK) attrs' dpi = fromMaybe defDPI $ (read . unpack) <$> Map.lookup (tshow DpiK) attrs' extraAttrs = Map.toList extraAttrs' - blockAttrs = (id', classes, filteredAttrs) + blockAttrs = (id', filter (/= cls toolkit) classes, filteredAttrs) + let blockDependencies = parseFileDependencies $ fromMaybe mempty $ Map.lookup (tshow DependenciesK) attrs' + dependencies = (defaultDependencies conf) <> blockDependencies + -- This is the first opportunity to check save format compatibility let saveFormatSupported = saveFormat `elem` (supportedSaveFormats toolkit) when (not saveFormatSupported) $ do @@ -132,4 +137,15 @@ readBool :: Text -> Bool readBool s | s `elem` ["True", "true", "'True'", "'true'", "1"] = True | s `elem` ["False", "false", "'False'", "'false'", "0"] = False - | otherwise = error $ unpack $ mconcat ["Could not parse '", s, "' into a boolean. Please use 'True' or 'False'"]+ | otherwise = error $ unpack $ mconcat ["Could not parse '", s, "' into a boolean. Please use 'True' or 'False'"] + + +-- | Parse a list of file dependencies such as /[foo.bar, hello.txt]/. +parseFileDependencies :: Text -> [FilePath] +parseFileDependencies t + | t == mempty = mempty + | otherwise = fmap normalise + . fmap unpack + . fmap (T.dropAround isSpace) -- Remove leading/trailing whitespace on filenames + . T.splitOn "," + . T.dropAround (\c -> c `elem` ['[', ']']) $ t
src/Text/Pandoc/Filter/Plot/Renderers/Bokeh.hs view
@@ -27,7 +27,7 @@ bokehSupportedSaveFormats :: [SaveFormat] -bokehSupportedSaveFormats = [PNG, HTML] +bokehSupportedSaveFormats = [PNG, SVG, HTML] bokehCommand :: OutputSpec -> PlotM Text @@ -56,18 +56,22 @@ ] --- TODO: should we capture a Document instead of a Plot? --- For some reason, saving the current document was not working bokehCapture :: FigureSpec -> FilePath -> Script bokehCapture FigureSpec{..} fname = [st| from bokeh.io import export_png, export_svgs, save -from bokeh.models import Plot +from bokeh.models import Model from bokeh.resources import CDN -__current_plot = next(obj for obj in globals().values() if isinstance(obj, Plot)) + +# The heuristic to determine the current Model is to find all objects which are +# at least subclasses of bokeh.models.Model, and then find the one which was +# created last. This is a dirty hack, so if you're reading this, don't hesitate to +# suggest something else. +__current_model = [obj for obj in globals().values() if isinstance(obj, Model)][-1] #{write} |] where write = case saveFormat of - HTML -> [st|save(__current_plot, filename=r"#{fname}", resources=CDN)|] - PNG -> [st|export_png(obj = __current_plot, filename=r"#{fname}")|] + HTML -> [st|save(__current_model, filename=r"#{fname}", resources=CDN)|] + SVG -> [st|__current_model.output_backend="svg"; export_svgs(__current_model, filename=r"#{fname}")|] + PNG -> [st|export_png(obj = __current_model, filename=r"#{fname}")|] fmt -> error $ "Save format not supported: " <> show fmt
src/Text/Pandoc/Filter/Plot/Renderers/Graphviz.hs view
@@ -32,7 +32,8 @@ graphvizCommand OutputSpec{..} = do exe <- executable Graphviz let fmt = fmap toLower . show . saveFormat $ oFigureSpec - return [st|#{exe} -T#{fmt} -o "#{oFigurePath}" "#{oScriptPath}"|] + dpi' = dpi oFigureSpec + return [st|#{exe} -T#{fmt} -Gdpi=#{dpi'} -o "#{oFigurePath}" "#{oScriptPath}"|] graphvizAvailable :: PlotM Bool
src/Text/Pandoc/Filter/Plot/Renderers/Mathematica.hs view
@@ -40,5 +40,5 @@ mathematicaCapture :: FigureSpec -> FilePath -> Script mathematicaCapture FigureSpec{..} fname = [st| -Export["#{fname}", %, #{show saveFormat}] +Export["#{fname}", %, #{show saveFormat}, ImageResolution -> #{dpi}] |]
src/Text/Pandoc/Filter/Plot/Renderers/Matlab.hs view
@@ -44,6 +44,10 @@ matlabCapture :: FigureSpec -> FilePath -> Script -matlabCapture _ fname = [st| -saveas(gcf, '#{fname}') +matlabCapture FigureSpec{..} fname = [st| +if exist("exportgraphics")>0 + exportgraphics(gcf, '#{fname}', 'Resolution', #{dpi}); +else + saveas(gcf, '#{fname}'); +end |]
src/Text/Pandoc/Filter/Plot/Scripting.hs view
@@ -39,9 +39,10 @@ -- Run script as described by the spec, only if necessary runScriptIfNecessary :: FigureSpec -> PlotM ScriptResult runScriptIfNecessary spec = do - liftIO $ createDirectoryIfMissing True . takeDirectory $ figurePath spec + target <- figurePath spec + liftIO $ createDirectoryIfMissing True . takeDirectory $ target - fileAlreadyExists <- liftIO . doesFileExist $ figurePath spec + fileAlreadyExists <- liftIO . doesFileExist $ target result <- if fileAlreadyExists then return ScriptSuccess else runTempScript spec @@ -49,7 +50,9 @@ logScriptResult result case result of - ScriptSuccess -> liftIO $ T.writeFile (sourceCodePath spec) (script spec) >> return ScriptSuccess + ScriptSuccess -> do + scp <- sourceCodePath spec + liftIO $ T.writeFile scp (script spec) >> return ScriptSuccess other -> return other where @@ -83,7 +86,8 @@ CheckFailed msg -> return $ ScriptChecksFailed msg CheckPassed -> do scriptPath <- tempScriptPath spec - let captureFragment = (capture toolkit) spec (figurePath spec) + target <- figurePath spec + let captureFragment = (capture toolkit) spec target -- Note: for gnuplot, the capture string must be placed -- BEFORE plotting happens. Since this is only really an -- issue for gnuplot, we have a special case. @@ -93,7 +97,7 @@ liftIO $ T.writeFile scriptPath scriptWithCapture let outputSpec = OutputSpec { oFigureSpec = spec , oScriptPath = scriptPath - , oFigurePath = figurePath spec + , oFigurePath = target } command_ <- command toolkit outputSpec (ec, _) <- runCommand command_ @@ -125,16 +129,28 @@ -- | Determine the path to the source code that generated the figure. -sourceCodePath :: FigureSpec -> FilePath -sourceCodePath = normalise . flip replaceExtension ".txt" . figurePath +sourceCodePath :: FigureSpec -> PlotM FilePath +sourceCodePath = fmap normalise . fmap (flip replaceExtension ".txt") . figurePath +-- | 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 -> PlotM Word +figureContentHash FigureSpec{..} = do + dependenciesHash <- sequence $ fileHash <$> dependencies + return $ fromIntegral $ hash (fromEnum toolkit, script, fromEnum saveFormat, directory, dpi, dependenciesHash, extraAttrs) + + -- | 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 . figureContentHash - ext = extension . saveFormat $ spec +figurePath :: FigureSpec -> PlotM FilePath +figurePath spec = do + fh <- figureContentHash spec + let ext = extension . saveFormat $ spec + stem = flip addExtension ext . show $ fh + return $ normalise $ directory spec </> stem
tests/Main.hs view
@@ -22,7 +22,7 @@ available <- availableToolkits defaultTestConfig unavailable <- unavailableToolkits defaultTestConfig forM_ unavailable $ \tk -> do - putStrLn $ show tk <> " is not availble. Its tests will be skipped." + putStrLn $ show tk <> " is not available. Its tests will be skipped." defaultMain $ testGroup "All tests" [ testGroup @@ -82,6 +82,7 @@ let config = defaultConfiguration { matplotlibExe = "python" , plotlyPythonExe = "python" , bokehExe = "python" + , defaultDependencies = ["file1.txt", "file2.txt"] } parsedConfig <- configuration "example-config.yml" @@ -133,6 +134,7 @@ assertEqual "" expected extracted + testHtmlEmbedding :: TestTree testHtmlEmbedding = testCase "HTML body and head scripts can be extracted from file" $ do @@ -150,6 +152,6 @@ ,"</html>" ] extracted = extractPlot html - expected = "<script src=\"script.js\"></script>\n <p>Hello</p>" + expected = "<script src=\"script.js\" defer></script>\n <p>Hello</p>" assertEqual "" expected extracted