Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 1ed5cbf

Browse files
SommerEngineeringtarleb
authored andcommittedApr 14, 2019
Added a caption for PlantUML diagrams (#39)
* Added a caption for PlantUML diagrams * Updated the README to use captions * Added diagram generator & fig. numbering * Removed the figure numbering * Added env. var. for java, pdflatex, and dot * Replaced imagemagic+ghostscript with Inkscape * Re-implement the Python generator * Added check if images were generated * Improved the Tikz i.e. LaTeX implementation * Updated the introductions * Fixed JAVA_HOME handling * Removed default Python value * Rewrote most of the readme * Added new dependencies * Updated the command * Captions can now use markdown as well * Added texlive-latex-extra * Added pgf package i.e. Tikz for LaTeX * Define the Python path * Bugfix for SVG convertion of PDF * Changed to Python3 * Added Python tk dependency * Bugfix: Headless matplotlib * Updated output file * Added note for the Java path * Updated output file * Renamed filter * Removed the output * Fixed spelling of TikZ * Replaced TikZ example with a shorter version * Tried to cut lines to max. 80 chars * Fixed TikZ figure; LaTeX/TikZ can now use additional packages * Using common Lua idiom to check nil * Replaced snail with camel case * Added note why Inkscape is preferred to ImageMagick * Removed unnecessary parameter of function * Using Lua's table feature instead of repeating blocks * Renamed filter * Fixed formatting of pandoc command * Bugfix: All code blocks where converted in images * Added a sample * Bugfix: Link to Matplotlib * Ensures that the code blocks are code blocks * Adjusted regarding recent changes
1 parent 51eda86 commit 1ed5cbf

File tree

9 files changed

+782
-136
lines changed

9 files changed

+782
-136
lines changed
 

Diff for: ‎.travis.yml

+11-2
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,20 @@ addons:
1212
- aspell-en
1313
- imagemagick
1414
- default-jre
15+
- graphviz
16+
- inkscape
17+
- python3
18+
- python3-pip
19+
- python3-tk
20+
- python3-numpy
21+
- python3-matplotlib
1522
- latex-xcolor
1623
- lmodern
1724
- texlive-bibtex-extra
1825
- texlive-fonts-recommended
1926
- texlive-latex-recommended
27+
- texlive-latex-extra
28+
- pgf
2029

2130
before_install:
2231
- unset CC
@@ -32,10 +41,10 @@ before_install:
3241
# Download plantuml.jar for plantuml filter
3342
- |
3443
pushd $HOME
35-
wget http://sourceforge.net/projects/plantuml/files/plantuml.1.2018.9.jar
44+
wget http://sourceforge.net/projects/plantuml/files/plantuml.jar
3645
popd
3746
- export PATH=$HOME/pandoc-$PANDOCVERSION/bin:$PATH
38-
- export PLANTUML=$HOME/plantuml.1.2018.9.jar
47+
- export PLANTUML=$HOME/plantuml.jar
3948

4049
install: []
4150

Diff for: ‎diagram-generator/Makefile

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
test:
2+
@pandoc --self-contained --lua-filter=diagram-generator.lua --metadata=activatePythonPath:"python3 --version" --metadata=pythonPath:"python3" --metadata=title:"README" sample.md -o output.html
3+

Diff for: ‎diagram-generator/README.md

+252
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
# Diagram Generator Lua Filter
2+
3+
## Introduction
4+
This Lua filter is used to create images with or without captions from code
5+
blocks. Currently PlantUML, Graphviz, Ti*k*Z and Python can be processed.
6+
This document also serves as a test document, which is why the subsequent
7+
test diagrams are integrated in every supported language.
8+
9+
## Prerequisites
10+
To be able to use this Lua filter, the respective external tools must be
11+
installed. However, it is sufficient if the tools to be used are installed.
12+
If you only want to use PlantUML, you don't need LaTeX or Python, etc.
13+
14+
### PlantUML
15+
To use PlantUML, you must install PlantUML itself. See the
16+
[PlantUML website](http://plantuml.com/) for more details. It should be
17+
noted that PlantUML is a Java program and therefore Java must also
18+
be installed.
19+
20+
By default, this filter expects the plantuml.jar file to be in the
21+
working directory. Alternatively, the environment variable
22+
`PLANTUML` can be set with a path. If, for example, a specific
23+
PlantUML version is to be used per pandoc document, the
24+
`plantumlPath` meta variable can be set.
25+
26+
Furthermore, this filter assumes that Java is located in the
27+
system or user path. This means that from any place of the system
28+
the `java` command is understood. Alternatively, the `JAVA_HOME`
29+
environment variable gets used. To use a specific Java version per
30+
pandoc document, use the `javaPath` meta variable. Please notice
31+
that `JAVA_HOME` must be set to the java's home directory e.g.
32+
`c:\Program Files\Java\jre1.8.0_201\` whereas `javaPath` must be
33+
set to the absolute path of `java.exe` e.g.
34+
`c:\Program Files\Java\jre1.8.0_201\bin\java.exe`.
35+
36+
Example usage:
37+
38+
~~~~~~~~~~~~~~~~
39+
```{.plantuml caption="This is an image, created by **PlantUML**."}
40+
@startuml
41+
Alice -> Bob: Authentication Request Bob --> Alice: Authentication Response
42+
Alice -> Bob: Another authentication Request Alice <-- Bob: another Response
43+
@enduml
44+
```
45+
~~~~~~~~~~~~~~~~
46+
47+
### Graphviz
48+
To use Graphviz you only need to install Graphviz, as you can read
49+
on its [website](http://www.graphviz.org/). There are no other
50+
dependencies.
51+
52+
This filter assumes that the `dot` command is located in the path
53+
and therefore can be used from any location. Alternatively, you can
54+
set the environment variable `DOT` or use the pandoc's meta variable
55+
`dotPath`.
56+
57+
Example usage from [the Graphviz
58+
gallery](https://graphviz.gitlab.io/_pages/Gallery/directed/fsm.html):
59+
60+
~~~~~~~~~~~~~~~~
61+
```{.graphviz caption="This is an image, created by **Graphviz**'s dot."}
62+
digraph finite_state_machine {
63+
rankdir=LR;
64+
size="8,5"
65+
node [shape = doublecircle]; LR_0 LR_3 LR_4 LR_8;
66+
node [shape = circle];
67+
LR_0 -> LR_2 [ label = "SS(B)" ];
68+
LR_0 -> LR_1 [ label = "SS(S)" ];
69+
LR_1 -> LR_3 [ label = "S($end)" ];
70+
LR_2 -> LR_6 [ label = "SS(b)" ];
71+
LR_2 -> LR_5 [ label = "SS(a)" ];
72+
LR_2 -> LR_4 [ label = "S(A)" ];
73+
LR_5 -> LR_7 [ label = "S(b)" ];
74+
LR_5 -> LR_5 [ label = "S(a)" ];
75+
LR_6 -> LR_6 [ label = "S(b)" ];
76+
LR_6 -> LR_5 [ label = "S(a)" ];
77+
LR_7 -> LR_8 [ label = "S(b)" ];
78+
LR_7 -> LR_5 [ label = "S(a)" ];
79+
LR_8 -> LR_6 [ label = "S(b)" ];
80+
LR_8 -> LR_5 [ label = "S(a)" ];
81+
}
82+
```
83+
~~~~~~~~~~~~~~~~
84+
85+
### Ti*k*Z
86+
Ti*k*Z (cf. [Wikipedia](https://en.wikipedia.org/wiki/PGF/TikZ)) is a
87+
description language for graphics of any kind that can be used within
88+
LaTeX (cf. [Wikipedia](https://en.wikipedia.org/wiki/LaTeX)).
89+
90+
Therefore a LaTeX system must be installed on the system. The Ti*k*Z code is
91+
embedded into a dynamic LaTeX document. This temporary document gets
92+
translated into a PDF document using LaTeX (`pdflatex`). Finally,
93+
Inkscape is used to convert the PDF file to the desired format.
94+
95+
Note: We are using Inkscape here to use a stable solution for the
96+
convertion. Formerly ImageMagick was used instead. ImageMagick is
97+
not able to convert PDF files. Hence, it uses Ghostscript to do
98+
so, cf. [1](https://stackoverflow.com/a/6599718/2258393).
99+
Unfortunately, Ghostscript behaves unpredictable during Windows and
100+
Linux tests cases, cf. [2](https://stackoverflow.com/questions/21774561/some-pdfs-are-converted-improperly-using-imagemagick),
101+
[3](https://stackoverflow.com/questions/9064706/imagemagic-convert-command-pdf-convertion-with-bad-size-orientation), [4](https://stackoverflow.com/questions/18837093/imagemagic-renders-image-with-black-background),
102+
[5](https://stackoverflow.com/questions/37392798/pdf-to-svg-is-not-perfect),
103+
[6](https://stackoverflow.com/q/10288065/2258393), etc. By using Inkscape,
104+
we need one dependency less and get rid of unexpected Ghostscript issues.
105+
106+
Due to this more complicated process, the use of Ti*k*Z is also more
107+
complicated overall. The process is error-prone: An insufficiently
108+
configured LaTeX installation or an insufficiently configured
109+
Inkscape installation can lead to errors. Overall, this results in
110+
the following dependencies:
111+
112+
- Any LaTeX installation. This should be configured so that
113+
missing packages are installed automatically. This filter uses the
114+
`pdflatex` command which is available by the system's path. Alternatively,
115+
you can set the `PDFLATEX` environment variable. In case you have to use
116+
a specific LaTeX version on a pandoc document basis, you might set the
117+
`pdflatexPath` meta variable.
118+
119+
- An installation of [Inkscape](https://inkscape.org/).
120+
It is assumed that the `inkscape` command is in the path and can be
121+
executed from any location. Alternatively, the environment
122+
variable `INKSCAPE` can be set with a path. If a specific
123+
version per pandoc document is to be used, the `inkscapePath`
124+
meta-variable can be set.
125+
126+
In order to use additional LaTeX packages, use the optional
127+
`additionalPackages` attribute in your document, as in the
128+
example below.
129+
130+
Example usage from [TikZ
131+
examples](http://www.texample.net/tikz/examples/parallelepiped/) by
132+
[Kjell Magne Fauske](http://www.texample.net/tikz/examples/nav1d/):
133+
134+
~~~~~~~~~~~~~~~~
135+
```{.tikz caption="This is an image, created by **TikZ i.e. LaTeX**."
136+
additionalPackages="\usepackage{adjustbox}"}
137+
\usetikzlibrary{arrows}
138+
\tikzstyle{int}=[draw, fill=blue!20, minimum size=2em]
139+
\tikzstyle{init} = [pin edge={to-,thin,black}]
140+
141+
\resizebox{16cm}{!}{%
142+
\trimbox{3.5cm 0cm 0cm 0cm}{
143+
\begin{tikzpicture}[node distance=2.5cm,auto,>=latex']
144+
\node [int, pin={[init]above:$v_0$}] (a) {$\frac{1}{s}$};
145+
\node (b) [left of=a,node distance=2cm, coordinate] {a};
146+
\node [int, pin={[init]above:$p_0$}] at (0,0) (c)
147+
[right of=a] {$\frac{1}{s}$};
148+
\node [coordinate] (end) [right of=c, node distance=2cm]{};
149+
\path[->] (b) edge node {$a$} (a);
150+
\path[->] (a) edge node {$v$} (c);
151+
\draw[->] (c) edge node {$p$} (end) ;
152+
\end{tikzpicture}
153+
}
154+
}
155+
```
156+
~~~~~~~~~~~~~~~~
157+
158+
### Python
159+
In order to use Python to generate an diagram, your Python code must store the
160+
final image data in a temporary file with the correct format. In case you use
161+
matplotlib for a diagram, add the following line to do so:
162+
163+
```python
164+
plt.savefig("$DESTINATION$", dpi=300, fomat="$FORMAT$")
165+
```
166+
167+
The placeholder `$FORMAT$` gets replace by the necessary format. Most of the
168+
time, this will be `png` or `svg`. The second placeholder, `$DESTINATION$`
169+
gets replaced by the path and file name of the destination. Both placeholders
170+
can be used as many times as you want. Example usage from the [Matplotlib
171+
examples](https://matplotlib.org/gallery/lines_bars_and_markers/cohere.html#sphx-glr-gallery-lines-bars-and-markers-cohere-py):
172+
173+
~~~~~~~~~~~~~~~~
174+
```{.py2image caption="This is an image, created by **Python**."}
175+
import matplotlib
176+
matplotlib.use('Agg')
177+
178+
import sys
179+
import numpy as np
180+
import matplotlib.pyplot as plt
181+
182+
# Fixing random state for reproducibility
183+
np.random.seed(19680801)
184+
185+
dt = 0.01
186+
t = np.arange(0, 30, dt)
187+
nse1 = np.random.randn(len(t)) # white noise 1
188+
nse2 = np.random.randn(len(t)) # white noise 2
189+
190+
# Two signals with a coherent part at 10Hz and a random part
191+
s1 = np.sin(2 * np.pi * 10 * t) + nse1
192+
s2 = np.sin(2 * np.pi * 10 * t) + nse2
193+
194+
fig, axs = plt.subplots(2, 1)
195+
axs[0].plot(t, s1, t, s2)
196+
axs[0].set_xlim(0, 2)
197+
axs[0].set_xlabel('time')
198+
axs[0].set_ylabel('s1 and s2')
199+
axs[0].grid(True)
200+
201+
cxy, f = axs[1].cohere(s1, s2, 256, 1. / dt)
202+
axs[1].set_ylabel('coherence')
203+
204+
fig.tight_layout()
205+
plt.savefig("$DESTINATION$", dpi=300, fomat="$FORMAT$")
206+
```
207+
~~~~~~~~~~~~~~~~
208+
209+
Precondition to use Python is a Python environment which contains all
210+
necessary libraries you want to use. To use, for example, the standard
211+
[Anaconda Python](https://www.anaconda.com/distribution/) environment
212+
on a Microsoft Windows system ...
213+
214+
- set the environment variable `PYTHON` or the meta key `pythonPath`
215+
to `c:\ProgramData\Anaconda3\python.exe`
216+
217+
- set the environment variable `PYTHON_ACTIVATE` or the meta
218+
key `activatePythonPath` to `c:\ProgramData\Anaconda3\Scripts\activate.bat`.
219+
220+
Pandoc will activate this Python environment and starts Python with your code.
221+
222+
## How to run pandoc
223+
This section will show, how to call Pandoc in order to use this filter with
224+
meta keys. The following command assume, that the filters are stored in the
225+
subdirectory `filters`. Further, this is a example for a Microsoft Windows
226+
system.
227+
228+
Command to use PlantUML (a single line):
229+
230+
```
231+
pandoc.exe README.md -f markdown -t docx --self-contained --standalone --lua-filter=filters\diagram-generator.lua --metadata=plantumlPath:"c:\ProgramData\chocolatey\lib\plantuml\tools\plantuml.jar" --metadata=javaPath:"c:\Program Files\Java\jre1.8.0_201\bin\java.exe" -o README.docx
232+
```
233+
234+
All available environment variables:
235+
236+
- `PLANTUML` e.g. `c:\ProgramData\chocolatey\lib\plantuml\tools\plantuml.jar`; Default: `plantuml.jar`
237+
- `INKSCAPE` e.g. `c:\Program Files\Inkscape\inkscape.exe`; Default: `inkscape`
238+
- `PYTHON` e.g. `c:\ProgramData\Anaconda3\python.exe`; Default: n/a
239+
- `PYTHON_ACTIVATE` e.g. `c:\ProgramData\Anaconda3\Scripts\activate.bat`; Default: n/a
240+
- `JAVA_HOME` e.g. `c:\Program Files\Java\jre1.8.0_201`; Default: n/a
241+
- `DOT` e.g. `c:\ProgramData\chocolatey\bin\dot.exe`; Default: `dot`
242+
- `PDFLATEX` e.g. `c:\Program Files\MiKTeX 2.9\miktex\bin\x64\pdflatex.exe`; Default: `pdflatex`
243+
244+
All available meta keys:
245+
246+
- `plantumlPath`
247+
- `inkscapePath`
248+
- `pythonPath`
249+
- `activatePythonPath`
250+
- `javaPath`
251+
- `dotPath`
252+
- `pdflatexPath`

Diff for: ‎diagram-generator/diagram-generator.lua

+272
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
--[[
2+
This Lua filter is used to create images with or without captions from
3+
code blocks. Currently PlantUML, GraphViz, Tikz, and Python can be
4+
processed. For further details, see README.md.
5+
6+
Thanks to @floriandd2ba and @jgm for the initial implementation of
7+
the PlantUML filter, which I used as a template. Thanks also @muxueqz
8+
for the code to generate a GraphViz image.
9+
]]
10+
11+
-- The PlantUML path. If set, uses the environment variable PLANTUML or the value "plantuml.jar" (local PlantUML version).
12+
-- In order to define a PlantUML version per pandoc document, use the meta data to define the key "plantumlPath".
13+
local plantumlPath = os.getenv("PLANTUML") or "plantuml.jar"
14+
15+
-- The Inkscape path. In order to define an Inkscape version
16+
-- per pandoc document, use the meta data to define the key "inkscapePath".
17+
local inkscapePath = os.getenv("INKSCAPE") or "inkscape"
18+
19+
-- The Python path. In order to define a Python version per pandoc document,
20+
-- use the meta data to define the key "pythonPath".
21+
local pythonPath = os.getenv("PYTHON")
22+
23+
-- The Python environment's activate script. Can be set on a per document basis
24+
-- by using the meta data key "activatePythonPath".
25+
local pythonActivatePath = os.getenv("PYTHON_ACTIVATE")
26+
27+
-- The Java path. In order to define a Java version per pandoc document,
28+
-- use the meta data to define the key "javaPath".
29+
local javaPath = os.getenv("JAVA_HOME")
30+
if javaPath then
31+
javaPath = javaPath .. package.config:sub(1,1) .. "bin" .. package.config:sub(1,1) .. "java"
32+
else
33+
javaPath = "java"
34+
end
35+
36+
-- The dot (Graphviz) path. In order to define a dot version per pandoc document,
37+
-- use the meta data to define the key "dotPath".
38+
local dotPath = os.getenv("DOT") or "dot"
39+
40+
-- The pdflatex path. In order to define a pdflatex version per pandoc document,
41+
-- use the meta data to define the key "pdflatexPath".
42+
local pdflatexPath = os.getenv("PDFLATEX") or "pdflatex"
43+
44+
-- The default format is SVG i.e. vector graphics:
45+
local filetype = "svg"
46+
local mimetype = "image/svg+xml"
47+
48+
-- Check for output formats that potentially cannot use SVG
49+
-- vector graphics. In these cases, we use a different format
50+
-- such as PNG:
51+
if FORMAT == "docx" then
52+
filetype = "png"
53+
mimetype = "image/png"
54+
elseif FORMAT == "pptx" then
55+
filetype = "png"
56+
mimetype = "image/png"
57+
elseif FORMAT == "rtf" then
58+
filetype = "png"
59+
mimetype = "image/png"
60+
end
61+
62+
-- Execute the meta data table to determine the paths. This function
63+
-- must be called first to get the desired path. If one of these
64+
-- meta options was set, it gets used instead of the corresponding
65+
-- environment variable:
66+
function Meta(meta)
67+
plantumlPath = meta.plantumlPath or plantumlPath
68+
inkscapePath = meta.inkscapePath or inkscapePath
69+
pythonPath = meta.pythonPath or pythonPath
70+
pythonActivatePath = meta.activatePythonPath or pythonActivatePath
71+
javaPath = meta.javaPath or javaPath
72+
dotPath = meta.dotPath or dotPath
73+
pdflatexPath = meta.pdflatexPath or pdflatexPath
74+
end
75+
76+
-- Call plantuml.jar with some parameters (cf. PlantUML help):
77+
local function plantuml(puml, filetype)
78+
local final = pandoc.pipe(javaPath, {"-jar", plantumlPath, "-t" .. filetype, "-pipe", "-charset", "UTF8"}, puml)
79+
return final
80+
end
81+
82+
-- Call dot (GraphViz) in order to generate the image
83+
-- (thanks @muxueqz for this code):
84+
local function graphviz(code, filetype)
85+
local final = pandoc.pipe(dotPath, {"-T" .. filetype}, code)
86+
return final
87+
end
88+
89+
-- Compile LaTeX with Tikz code to an image:
90+
local function tikz2image(src, filetype, additionalPackages)
91+
92+
-- Define file names:
93+
local outfile = string.format("./tmp-latex/file.%s", filetype)
94+
local tmp = "./tmp-latex/file"
95+
local tmpDir = "./tmp-latex/"
96+
97+
-- Ensure, that the tmp directory exists:
98+
os.execute("mkdir tmp-latex")
99+
100+
-- Build and write the LaTeX document:
101+
local f = io.open(tmp .. ".tex", 'w')
102+
f:write("\\documentclass{standalone}\n\\usepackage{tikz}\n")
103+
104+
-- Any additional package(s) are desired?
105+
if additionalPackages then
106+
f:write(additionalPackages)
107+
end
108+
109+
f:write("\\begin{document}\n")
110+
f:write(src)
111+
f:write("\n\\end{document}\n")
112+
f:close()
113+
114+
-- Execute the LaTeX compiler:
115+
os.execute(pdflatexPath .. " -output-directory " .. tmpDir .. " " .. tmp)
116+
117+
-- Build the basic Inkscape command for the conversion:
118+
local baseCommand = " --without-gui --file=" .. tmp .. ".pdf"
119+
local knownFormat = false
120+
121+
if filetype == "png" then
122+
123+
-- Append the subcommands to convert into a PNG file:
124+
baseCommand = baseCommand .. " --export-png=" .. tmp .. ".png --export-dpi=300"
125+
knownFormat = true
126+
127+
elseif filetype == "svg" then
128+
129+
-- Append the subcommands to convert into a SVG file:
130+
baseCommand = baseCommand .. " --export-plain-svg=" .. tmp .. ".svg"
131+
knownFormat = true
132+
133+
end
134+
135+
-- Unfortunately, continuation is only possible, if we know the actual format:
136+
local imgData = nil
137+
if knownFormat then
138+
139+
-- We know the desired format. Thus, execute Inkscape:
140+
os.execute("\"" .. inkscapePath .. "\"" .. baseCommand)
141+
142+
-- Try to open the image:
143+
local r = io.open(tmp .. "." .. filetype, 'rb')
144+
145+
-- Read the image, if available:
146+
if r then
147+
imgData = r:read("*all")
148+
r:close()
149+
end
150+
151+
-- Delete the image tmp file:
152+
os.remove(outfile)
153+
end
154+
155+
-- Remove the temporary files:
156+
os.remove(tmp .. ".tex")
157+
os.remove(tmp .. ".pdf")
158+
os.remove(tmp .. ".log")
159+
os.remove(tmp .. ".aux")
160+
161+
return imgData
162+
end
163+
164+
-- Run Python to generate an image:
165+
local function py2image(code, filetype)
166+
167+
-- Define the temp files:
168+
local outfile = string.format("./tmp-python/file.%s", filetype)
169+
local tmp = "./tmp-python/file"
170+
local tmpDir = "./tmp-python/"
171+
172+
-- Replace the desired destination's file type in the Python code:
173+
local extendedCode = string.gsub(code, "%$FORMAT%$", filetype)
174+
175+
-- Replace the desired destination's path in the Python code:
176+
extendedCode = string.gsub(extendedCode, "%$DESTINATION%$", outfile)
177+
178+
-- Ensure, that the tmp directory exists:
179+
os.execute("mkdir tmp-python")
180+
181+
-- Write the Python code:
182+
local f = io.open(tmp .. ".py", 'w')
183+
f:write(extendedCode)
184+
f:close()
185+
186+
-- Execute Python in the desired environment:
187+
os.execute(pythonActivatePath .. " && " .. pythonPath .. " " .. tmp .. ".py")
188+
189+
-- Try to open the written image:
190+
local r = io.open(outfile, 'rb')
191+
local imgData = nil
192+
193+
-- When the image exist, read it:
194+
if r then
195+
imgData = r:read("*all")
196+
r:close()
197+
end
198+
199+
-- Delete the tmp files:
200+
os.remove(tmp .. ".py")
201+
os.remove(outfile)
202+
203+
return imgData
204+
end
205+
206+
-- Executes each document's code block to find matching code blocks:
207+
function CodeBlock(block)
208+
209+
-- Predefine a potential image:
210+
local fname = nil
211+
212+
-- Using a table with all known generators i.e. converters:
213+
local converters = {
214+
plantuml = plantuml,
215+
graphviz = graphviz,
216+
tikz = tikz2image,
217+
py2image = py2image,
218+
}
219+
220+
-- Call the correct converter which belongs to the used class:
221+
local success, img = pcall(converters[block.classes[1]], block.text,
222+
filetype, block.attributes["additionalPackages"] or nil)
223+
224+
-- Was ok?
225+
if success and img then
226+
227+
-- Hash the figure name and content:
228+
fname = pandoc.sha1(img) .. "." .. filetype
229+
230+
-- Store the data in the media bag:
231+
pandoc.mediabag.insert(fname, mimetype, img)
232+
end
233+
234+
-- Case: This code block was an image e.g. PlantUML or dot/Graphviz, etc.:
235+
if fname then
236+
237+
-- Define the default caption:
238+
local caption = {}
239+
local enableCaption = nil
240+
241+
-- If the user defines a caption, use it:
242+
if block.attributes["caption"] then
243+
caption = pandoc.read(block.attributes.caption).blocks[1].content
244+
245+
-- This is pandoc's current hack to enforce a caption:
246+
enableCaption = "fig:"
247+
end
248+
249+
-- Create a new image for the document's structure. Attach the user's caption.
250+
-- Also use a hack (fig:) to enforce pandoc to create a figure i.e. attach
251+
-- a caption to the image.
252+
local imgObj = pandoc.Image(caption, fname, enableCaption)
253+
254+
-- Now, transfer the attribute "name" from the code block to the new image block.
255+
-- It might gets used by the figure numbering lua filter. If the figure numbering
256+
-- gets not used, this additional attribute gets ignored as well.
257+
if block.attributes["name"] then
258+
imgObj.attributes["name"] = block.attributes["name"]
259+
end
260+
261+
-- Finally, put the image inside an empty paragraph. By returning the resulting
262+
-- paragraph object, the source code block gets replaced by the image:
263+
return pandoc.Para{ imgObj }
264+
end
265+
end
266+
267+
-- Normally, pandoc will run the function in the built-in order Inlines -> Blocks -> Meta -> Pandoc.
268+
-- We instead want Meta -> Blocks. Thus, we must define our custom order:
269+
return {
270+
{Meta = Meta},
271+
{CodeBlock = CodeBlock},
272+
}

Diff for: ‎diagram-generator/sample.md

+244
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
# Diagram Generator Lua Filter
2+
3+
## Introduction
4+
This Lua filter is used to create images with or without captions from code
5+
blocks. Currently PlantUML, Graphviz, Ti*k*Z and Python can be processed.
6+
This document also serves as a test document, which is why the subsequent
7+
test diagrams are integrated in every supported language.
8+
9+
## Prerequisites
10+
To be able to use this Lua filter, the respective external tools must be
11+
installed. However, it is sufficient if the tools to be used are installed.
12+
If you only want to use PlantUML, you don't need LaTeX or Python, etc.
13+
14+
### PlantUML
15+
To use PlantUML, you must install PlantUML itself. See the
16+
[PlantUML website](http://plantuml.com/) for more details. It should be
17+
noted that PlantUML is a Java program and therefore Java must also
18+
be installed.
19+
20+
By default, this filter expects the plantuml.jar file to be in the
21+
working directory. Alternatively, the environment variable
22+
`PLANTUML` can be set with a path. If, for example, a specific
23+
PlantUML version is to be used per pandoc document, the
24+
`plantumlPath` meta variable can be set.
25+
26+
Furthermore, this filter assumes that Java is located in the
27+
system or user path. This means that from any place of the system
28+
the `java` command is understood. Alternatively, the `JAVA_HOME`
29+
environment variable gets used. To use a specific Java version per
30+
pandoc document, use the `javaPath` meta variable. Please notice
31+
that `JAVA_HOME` must be set to the java's home directory e.g.
32+
`c:\Program Files\Java\jre1.8.0_201\` whereas `javaPath` must be
33+
set to the absolute path of `java.exe` e.g.
34+
`c:\Program Files\Java\jre1.8.0_201\bin\java.exe`.
35+
36+
Example usage:
37+
38+
```{.plantuml caption="This is an image, created by **PlantUML**."}
39+
@startuml
40+
Alice -> Bob: Authentication Request Bob --> Alice: Authentication Response
41+
Alice -> Bob: Another authentication Request Alice <-- Bob: another Response
42+
@enduml
43+
```
44+
45+
### Graphviz
46+
To use Graphviz you only need to install Graphviz, as you can read
47+
on its [website](http://www.graphviz.org/). There are no other
48+
dependencies.
49+
50+
This filter assumes that the `dot` command is located in the path
51+
and therefore can be used from any location. Alternatively, you can
52+
set the environment variable `DOT` or use the pandoc's meta variable
53+
`dotPath`.
54+
55+
Example usage from [the Graphviz
56+
gallery](https://graphviz.gitlab.io/_pages/Gallery/directed/fsm.html):
57+
58+
```{.graphviz caption="This is an image, created by **Graphviz**'s dot."}
59+
digraph finite_state_machine {
60+
rankdir=LR;
61+
size="8,5"
62+
node [shape = doublecircle]; LR_0 LR_3 LR_4 LR_8;
63+
node [shape = circle];
64+
LR_0 -> LR_2 [ label = "SS(B)" ];
65+
LR_0 -> LR_1 [ label = "SS(S)" ];
66+
LR_1 -> LR_3 [ label = "S($end)" ];
67+
LR_2 -> LR_6 [ label = "SS(b)" ];
68+
LR_2 -> LR_5 [ label = "SS(a)" ];
69+
LR_2 -> LR_4 [ label = "S(A)" ];
70+
LR_5 -> LR_7 [ label = "S(b)" ];
71+
LR_5 -> LR_5 [ label = "S(a)" ];
72+
LR_6 -> LR_6 [ label = "S(b)" ];
73+
LR_6 -> LR_5 [ label = "S(a)" ];
74+
LR_7 -> LR_8 [ label = "S(b)" ];
75+
LR_7 -> LR_5 [ label = "S(a)" ];
76+
LR_8 -> LR_6 [ label = "S(b)" ];
77+
LR_8 -> LR_5 [ label = "S(a)" ];
78+
}
79+
```
80+
81+
### Ti*k*Z
82+
Ti*k*Z (cf. [Wikipedia](https://en.wikipedia.org/wiki/PGF/TikZ)) is a
83+
description language for graphics of any kind that can be used within
84+
LaTeX (cf. [Wikipedia](https://en.wikipedia.org/wiki/LaTeX)).
85+
86+
Therefore a LaTeX system must be installed on the system. The Ti*k*Z code is
87+
embedded into a dynamic LaTeX document. This temporary document gets
88+
translated into a PDF document using LaTeX (`pdflatex`). Finally,
89+
Inkscape is used to convert the PDF file to the desired format.
90+
91+
Note: We are using Inkscape here to use a stable solution for the
92+
convertion. Formerly ImageMagick was used instead. ImageMagick is
93+
not able to convert PDF files. Hence, it uses Ghostscript to do
94+
so, cf. [1](https://stackoverflow.com/a/6599718/2258393).
95+
Unfortunately, Ghostscript behaves unpredictable during Windows and
96+
Linux tests cases, cf. [2](https://stackoverflow.com/questions/21774561/some-pdfs-are-converted-improperly-using-imagemagick),
97+
[3](https://stackoverflow.com/questions/9064706/imagemagic-convert-command-pdf-convertion-with-bad-size-orientation), [4](https://stackoverflow.com/questions/18837093/imagemagic-renders-image-with-black-background),
98+
[5](https://stackoverflow.com/questions/37392798/pdf-to-svg-is-not-perfect),
99+
[6](https://stackoverflow.com/q/10288065/2258393), etc. By using Inkscape,
100+
we need one dependency less and get rid of unexpected Ghostscript issues.
101+
102+
Due to this more complicated process, the use of Ti*k*Z is also more
103+
complicated overall. The process is error-prone: An insufficiently
104+
configured LaTeX installation or an insufficiently configured
105+
Inkscape installation can lead to errors. Overall, this results in
106+
the following dependencies:
107+
108+
- Any LaTeX installation. This should be configured so that
109+
missing packages are installed automatically. This filter uses the
110+
`pdflatex` command which is available by the system's path. Alternatively,
111+
you can set the `PDFLATEX` environment variable. In case you have to use
112+
a specific LaTeX version on a pandoc document basis, you might set the
113+
`pdflatexPath` meta variable.
114+
115+
- An installation of [Inkscape](https://inkscape.org/).
116+
It is assumed that the `inkscape` command is in the path and can be
117+
executed from any location. Alternatively, the environment
118+
variable `INKSCAPE` can be set with a path. If a specific
119+
version per pandoc document is to be used, the `inkscapePath`
120+
meta-variable can be set.
121+
122+
In order to use additional LaTeX packages, use the optional
123+
`additionalPackages` attribute in your document, as in the
124+
example below.
125+
126+
Example usage from [TikZ
127+
examples](http://www.texample.net/tikz/examples/parallelepiped/) by
128+
[Kjell Magne Fauske](http://www.texample.net/tikz/examples/nav1d/):
129+
130+
```{.tikz caption="This is an image, created by **TikZ i.e. LaTeX**."
131+
additionalPackages="\usepackage{adjustbox}"}
132+
\usetikzlibrary{arrows}
133+
\tikzstyle{int}=[draw, fill=blue!20, minimum size=2em]
134+
\tikzstyle{init} = [pin edge={to-,thin,black}]
135+
136+
\resizebox{16cm}{!}{%
137+
\trimbox{3.5cm 0cm 0cm 0cm}{
138+
\begin{tikzpicture}[node distance=2.5cm,auto,>=latex']
139+
\node [int, pin={[init]above:$v_0$}] (a) {$\frac{1}{s}$};
140+
\node (b) [left of=a,node distance=2cm, coordinate] {a};
141+
\node [int, pin={[init]above:$p_0$}] at (0,0) (c)
142+
[right of=a] {$\frac{1}{s}$};
143+
\node [coordinate] (end) [right of=c, node distance=2cm]{};
144+
\path[->] (b) edge node {$a$} (a);
145+
\path[->] (a) edge node {$v$} (c);
146+
\draw[->] (c) edge node {$p$} (end) ;
147+
\end{tikzpicture}
148+
}
149+
}
150+
```
151+
152+
### Python
153+
In order to use Python to generate an diagram, your Python code must store the
154+
final image data in a temporary file with the correct format. In case you use
155+
matplotlib for a diagram, add the following line to do so:
156+
157+
```python
158+
plt.savefig("$DESTINATION$", dpi=300, fomat="$FORMAT$")
159+
```
160+
161+
The placeholder `$FORMAT$` gets replace by the necessary format. Most of the
162+
time, this will be `png` or `svg`. The second placeholder, `$DESTINATION$`
163+
gets replaced by the path and file name of the destination. Both placeholders
164+
can be used as many times as you want. Example usage from the [Matplotlib
165+
examples](https://matplotlib.org/gallery/lines_bars_and_markers/cohere.html#sphx-glr-gallery-lines-bars-and-markers-cohere-py):
166+
167+
```{.py2image caption="This is an image, created by **Python**."}
168+
import matplotlib
169+
matplotlib.use('Agg')
170+
171+
import sys
172+
import numpy as np
173+
import matplotlib.pyplot as plt
174+
175+
# Fixing random state for reproducibility
176+
np.random.seed(19680801)
177+
178+
dt = 0.01
179+
t = np.arange(0, 30, dt)
180+
nse1 = np.random.randn(len(t)) # white noise 1
181+
nse2 = np.random.randn(len(t)) # white noise 2
182+
183+
# Two signals with a coherent part at 10Hz and a random part
184+
s1 = np.sin(2 * np.pi * 10 * t) + nse1
185+
s2 = np.sin(2 * np.pi * 10 * t) + nse2
186+
187+
fig, axs = plt.subplots(2, 1)
188+
axs[0].plot(t, s1, t, s2)
189+
axs[0].set_xlim(0, 2)
190+
axs[0].set_xlabel('time')
191+
axs[0].set_ylabel('s1 and s2')
192+
axs[0].grid(True)
193+
194+
cxy, f = axs[1].cohere(s1, s2, 256, 1. / dt)
195+
axs[1].set_ylabel('coherence')
196+
197+
fig.tight_layout()
198+
plt.savefig("$DESTINATION$", dpi=300, fomat="$FORMAT$")
199+
```
200+
201+
Precondition to use Python is a Python environment which contains all
202+
necessary libraries you want to use. To use, for example, the standard
203+
[Anaconda Python](https://www.anaconda.com/distribution/) environment
204+
on a Microsoft Windows system ...
205+
206+
- set the environment variable `PYTHON` or the meta key `pythonPath`
207+
to `c:\ProgramData\Anaconda3\python.exe`
208+
209+
- set the environment variable `PYTHON_ACTIVATE` or the meta
210+
key `activatePythonPath` to `c:\ProgramData\Anaconda3\Scripts\activate.bat`.
211+
212+
Pandoc will activate this Python environment and starts Python with your code.
213+
214+
## How to run pandoc
215+
This section will show, how to call Pandoc in order to use this filter with
216+
meta keys. The following command assume, that the filters are stored in the
217+
subdirectory `filters`. Further, this is a example for a Microsoft Windows
218+
system.
219+
220+
Command to use PlantUML (a single line):
221+
222+
```
223+
pandoc.exe README.md -f markdown -t docx --self-contained --standalone --lua-filter=filters\diagram-generator.lua --metadata=plantumlPath:"c:\ProgramData\chocolatey\lib\plantuml\tools\plantuml.jar" --metadata=javaPath:"c:\Program Files\Java\jre1.8.0_201\bin\java.exe" -o README.docx
224+
```
225+
226+
All available environment variables:
227+
228+
- `PLANTUML` e.g. `c:\ProgramData\chocolatey\lib\plantuml\tools\plantuml.jar`; Default: `plantuml.jar`
229+
- `INKSCAPE` e.g. `c:\Program Files\Inkscape\inkscape.exe`; Default: `inkscape`
230+
- `PYTHON` e.g. `c:\ProgramData\Anaconda3\python.exe`; Default: n/a
231+
- `PYTHON_ACTIVATE` e.g. `c:\ProgramData\Anaconda3\Scripts\activate.bat`; Default: n/a
232+
- `JAVA_HOME` e.g. `c:\Program Files\Java\jre1.8.0_201`; Default: n/a
233+
- `DOT` e.g. `c:\ProgramData\chocolatey\bin\dot.exe`; Default: `dot`
234+
- `PDFLATEX` e.g. `c:\Program Files\MiKTeX 2.9\miktex\bin\x64\pdflatex.exe`; Default: `pdflatex`
235+
236+
All available meta keys:
237+
238+
- `plantumlPath`
239+
- `inkscapePath`
240+
- `pythonPath`
241+
- `activatePythonPath`
242+
- `javaPath`
243+
- `dotPath`
244+
- `pdflatexPath`

Diff for: ‎plantuml/Makefile

-3
This file was deleted.

Diff for: ‎plantuml/output.html

-45
This file was deleted.

Diff for: ‎plantuml/plantuml.lua

-56
This file was deleted.

Diff for: ‎plantuml/readme.md

-30
This file was deleted.

0 commit comments

Comments
 (0)
Please sign in to comment.