diff --git a/.tools/Dockerfile b/.tools/Dockerfile index 0a8067bc..8945afe6 100644 --- a/.tools/Dockerfile +++ b/.tools/Dockerfile @@ -19,6 +19,8 @@ RUN apt-get -q --no-allow-insecure-repositories update \ lmodern \ luarocks \ make \ + nodejs \ + npm \ python-is-python3 \ python3 \ python3-matplotlib \ @@ -38,6 +40,9 @@ ARG plantuml_jar=plantuml.1.2018.9.jar RUN wget https://sourceforge.net/projects/plantuml/files/${plantuml_jar} \ --quiet --output-document=/root/${plantuml_jar} ENV PLANTUML=/root/$plantuml_jar + +RUN npm install --global mathjax-node-cli + ENV DIFF="diff -u" ENV LC_ALL="C.UTF-8" ENV LANG="C.UTF-8" diff --git a/math2svg/Makefile b/math2svg/Makefile new file mode 100644 index 00000000..0f3d076f --- /dev/null +++ b/math2svg/Makefile @@ -0,0 +1,17 @@ +DIFF ?= diff --strip-trailing-cr -u + +test: sample.md math2svg.lua + @pandoc \ + --mathml --lua-filter=math2svg.lua \ + -M math2svg_font='Gyre-Pagella' \ + --to=html sample.yaml $< \ + | $(DIFF) expected.html - + +expected: sample.md math2svg.lua + pandoc \ + --mathml --lua-filter=math2svg.lua \ + -M math2svg_tex2svg='/usr/local/bin/tex2svg' \ + -M math2svg_font='Gyre-Pagella' \ + --output=expected.html sample.yaml $< + +.PHONY: test diff --git a/math2svg/README.md b/math2svg/README.md new file mode 100644 index 00000000..28d82068 --- /dev/null +++ b/math2svg/README.md @@ -0,0 +1,258 @@ +# math2svg + + +## Description + +This [Lua filter][pandoc.lua-filters] for [Pandoc][pandoc] converts +[LaTeX math][latex.math] to [MathJax][mathjax] generated +[scalable vector graphics (SVG)][svg] for insertion into the output document +in a standalone manner. +SVG output is in any of the [available MathJax fonts][mathjax.fonts]. + +This is useful when a CSS paged media engine (such as [Prince XML][prince]) +cannot process complex JavaScript as required by MathJax. +See: for information about CSS paged media, +a [W3C standard][w3c]. + +No Internet connection is required when generating or viewing SVG formulas, +resulting in both absolute privacy and offline, standalone robustness. + + +## Requirements + +First, use the package manager of your operating system to install `pandoc`, +`nodejs` and `npm`. `brew` and `choco` are recommended package managers for +respectively macOS and Windows. See: + +```bash +$ sudo apt install pandoc nodejs npm +$ sudo dnf install pandoc nodejs npm +$ sudo yum install pandoc nodejs npm +$ brew install pandoc nodejs npm +> choco install pandoc nodejs npm +``` + +Then, by means of node's package manager `npm`, install the `mathjax-node-cli` +package. This package comes with the `tex2svg` executable. +Leave out the `sudo` on Windows. + +```bash +$ sudo npm install --global mathjax-node-cli +> npm install --global mathjax-node-cli +``` + + +## Usage + +To be used as a [Pandoc Lua filter][pandoc.lua-filters]. +[MathML][mathml] should be set as a fallback with +the `--mathml` argument. + +```bash +pandoc --mathml --filter='math2svg.lua' +``` + +The math2svg filter is entirely configurable over +[`--metadata` key value pairs](pandoc.metadata). +Nine configuration keys are available with sensible default values. +Hence, depending on your system and intentions, not all keys are necessarily +required. + +|key|default value| +|:--|:-----------:| +|`math2svg_tex2svg`|`''`| +|`math2svg_display2svg`|`true`| +|`math2svg_inline2svg`|`false`| +|`math2svg_speech`|`false`| +|`math2svg_linebreaks`|`true`| +|`math2svg_font`|`'TeX'`| +|`math2svg_ex`|`6`| +|`math2svg_width`|`100`| +|`math2svg_extensions`|`''`| + + +### Key value `math2svg_tex2svg` +This string key value is only required when, on your system, the path to the +`tex2svg` executable of the `mathjax-node-cli` package is not present in the +`$PATH` environment variable. + +The full path to `tex2svg` can be found with the following command on \*nix, +respectively Windows: + +```bash +$ which -a tex2svg +> where tex2svg +``` + +### Key values `math2svg_display2svg` and `math2svg_inline2svg` +These boolean key values specify whether display math, respectively inline math, +should be converted to [SVG][svg] by the filter. +The defaults convert display math to SVG, whereas inline math falls back to +[MathML][mathml] when `--mathml` was specified at `pandoc` evocation. +These defaults offer the following benefits: + +- MathML output gets generated much faster than SVG output. +- Moreover, MathML is well suited for inline math as line heights are kept + small. + + +### Key value `math2svg_speech` +This boolean key value controls whether textual annotations for speech +generation are added to SVG formula. The default is `false`. + +### Key value `math2svg_linebreaks` +This boolean key value automatic switches automatic line breaking. +The default is `true`. + + +### Key value `math2svg_font` +This string key value allows to specify a [MathJax font][mathjax.fonts] +different from the default `'TeX'` font. +The string should correspond to the local directory name of the font in the +`mathjax-node-cli` installation directory. +For example, the key value string for the font in +`/usr/local/lib/node_modules/mathjax-node-cli/node_modules/mathjax/fonts/HTML-CSS/Gyre-Pagella/` +would simply be `Gyre-Pagella`. + + +### Key value `math2svg_ex` +This positive integer key value sets the `ex` unit in pixels. +The default value is `6` pixels. + + +### Key value `math2svg_width` +This positive integer key value sets the container width in `ex` units for line +breaking and tags. The default value is `100` ex. + + +### Key value `math2svg_extensions` +This string key value allows to load one or more comma separated +[MathJax extensions for TeX and LaTeX][mathjax.tex.ext] present on the system. +These MathJaX extensions reside in a subdirectory of the `mathjax-node-cli` +installation directory. + +Take for example, the installation directory of the extensions is +`/usr/local/lib/node_modules/mathjax-node-cli/node_modules/mathjax/unpacked/extensions/` +It contains a subdirectory `TeX` with the extension file `AMSmath.js`. +This MathJaX extension can be loaded by specifying the string `'TeX/AMSmath'` +as the value of the `math2svg_extensions` key. + + +### Adding `header-includes` +It might turn out useful to systematically include LaTeX macros, for example as +shown below, a series of `\newcommand`. + +```latex +--- +header-includes: | + \newcommand{\j}{\text{j}} + \newcommand{\e}[1]{\,\text{e}^{#1}} +... +``` + +This may be achieved either by adding a [YAML][yaml] block with the +[`header-includes`][pandoc.header-includes] key value at the top of the input +document, or by having a separate YAML document loaded before the input +document. In the latter case, simply evoke `pandoc` as follows: + +```bash +pandoc --mathml --filter='math2svg.lua' header-includes.yaml input.md +``` + + +## Adding equation numbers with CSS + +When automatic equation numbers are desired, these need to be added using +[cascading style sheets (CSS)](https://en.wikipedia.org/wiki/CSS), +as JavaScript is not available in CSS paged media. + +Below example CSS code centers display math whilst adding equation numbers +to the right. + +```css +span.math.display { + display: flex; + justify-content: flex-end; + align-items: center; + } + +span.math.display svg { + margin: 0 auto; + max-width: 85%; + } + +span.math.display:after { + counter-increment: equation; + content: '(' counter(equation) ')'; + font-weight: bold; + } +``` + + +## Privacy + +No Internet connection is established when creating MathJax SVG code using +the `tex2svg` command of [`mathjax-node-cli`][mathjax.node.cli]. +Nor will any Internet connection be established when viewing an SVG formula. + +Hence, formulas in SVG can be created and viewed offline whilst remaining +private. + +For code auditing, see also: + +- +- +- + + +## License + +Copyright (c) 2020-2021 Serge Y. Stroobandt + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +## Contact + +```bash +$ echo c2VyZ2VAc3Ryb29iYW5kdC5jb20K |base64 -d +``` + + +[latex.math]: https://en.wikibooks.org/wiki/LaTeX/Mathematics + +[mathjax]: https://www.mathjax.org/ +[mathjax.fonts]: https://docs.mathjax.org/en/latest/output/fonts.html +[mathjax.node.cli]: https://github.com/mathjax/mathjax-node-cli +[mathjax.tex.ext]: https://docs.mathjax.org/en/latest/input/tex/extensions.html +[mathml]: https://en.wikipedia.org/wiki/MathML + +[pandoc]: https://pandoc.org/ +[pandoc.header-includes]: https://pandoc.org/MANUAL.html#metadata-blocks +[pandoc.lua-filters]: https://pandoc.org/lua-filters.html +[pandoc.metadata]: https://pandoc.org/MANUAL.html#reader-options +[prince]: https://www.princexml.com + +[svg]: https://en.wikipedia.org/wiki/Scalable_Vector_Graphics + +[w3c]: https://www.w3.org/TR/css-page-3/ + +[yaml]: https://en.wikipedia.org/wiki/YAML diff --git a/math2svg/expected.html b/math2svg/expected.html new file mode 100644 index 00000000..31e21f28 --- /dev/null +++ b/math2svg/expected.html @@ -0,0 +1,222 @@ +

Only plane waves in the far field exhibit the characteristic impedance of free space, which is exactly:

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+
where:
+c0=299792458msc_0 = 299\,792\,458\,\frac{\text{m}}{\text{s}}: the speed of light in free space
+μ0=4π107Hm\mu_0 = 4\pi\cdot10^{-7}\frac{\text{H}}{\text{m}}: the free space permeability
+ϵ0=1μ0c02\epsilon_0 = \frac{1}{\mu_0 c_0^2}: the absolute permittivity of free space
+Z0Z_0: the characteristic impedance of free space
+
+

Euler’s formula:

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+
+

The input impedance Zin,shortZ_\text{in,$\,$short} of a transmission line stub terminated in a short circuit is given by:

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+
where:
+γ=α+jβ\gamma = \alpha + \text{j}\beta is the propagation constant γ\gamma,
+α\alpha is the attenuation constant, and
+β\beta is the phase constant.
diff --git a/math2svg/math2svg.lua b/math2svg/math2svg.lua new file mode 100644 index 00000000..95b1f578 --- /dev/null +++ b/math2svg/math2svg.lua @@ -0,0 +1,155 @@ +-- DESCRIPTION +-- +-- This Lua filter for Pandoc converts LaTeX math to MathJax generated +-- scalable vector graphics (SVG) for insertion into the output document +-- in a standalone manner. SVG output is in any of the available MathJax +-- fonts. This is useful when a CSS paged media engine cannot process complex +-- JavaScript. No Internet connection is required when generating or viewing +-- SVG formulas, resulting in both absolute privacy and offline, standalone +-- robustness. + + +-- REQUIREMENTS, USAGE & PRIVACY +-- +-- See: https://github.com/pandoc/lua-filters/tree/master/math2svg + + +-- LICENSE +-- +-- Copyright (c) 2020-2021 Serge Y. Stroobandt +-- +-- MIT License +-- +-- Permission is hereby granted, free of charge, to any person obtaining a +-- copy of this software and associated documentation files (the "Software"), +-- to deal in the Software without restriction, including without limitation +-- the rights to use, copy, modify, merge, publish, distribute, sublicense, +-- and/or sell copies of the Software, and to permit persons to whom the +-- Software is furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +-- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +-- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +-- DEALINGS IN THE SOFTWARE. + + +-- CONTACT +-- +-- $ echo c2VyZ2VAc3Ryb29iYW5kdC5jb20K |base64 -d + + +-- The full path to the tex2svg binary of the mathjax-node-cli package. +local tex2svg = 'tex2svg' + +-- By default, DisplayMath is converted to SVG, whereas InlineMath is not. +local display2svg = true +local inline2svg = false +-- The fallback is MathML when pandoc is executed with the --mathml argument. + +-- Textual annotation for speech +local speech = false + +-- Automatic line breaking +local linebreaks = true + +-- MathJax font +local font = 'TeX' +-- Supported MathJax fonts are: +-- https://docs.mathjax.org/en/latest/output/fonts.html + +-- ex unit size in pixels +local ex = 6 + +-- Container width in ex for line breaking and tags +local width = 100 + +-- String of MathJax extensions for TeX and LaTeX to be loaded at run time +local extensions = '' +-- Available MathJax extensions are listed in: +-- /usr/local/lib/node_modules/mathjax-node-cli/node_modules/mathjax/unpacked/\ +-- extensions/ + + +function Meta(meta) + + tex2svg = tostring(meta.math2svg_tex2svg or tex2svg) + display2svg = meta.math2svg_display2svg or display2svg + inline2svg = meta.math2svg_inline2svg or inline2svg + speech = tostring(meta.math2svg_speech or speech) + linebreaks = tostring(meta.math2svg_linebreaks or linebreaks) + font = tostring(meta.math2svg_font or font) + ex = tostring(meta.math2svg_ex or ex) + width = tostring(meta.math2svg_width or width) + extensions = tostring(meta.math2svg_extensions or extensions) + +end + + +function Math(elem) + + local svg = nil + local tags = nil + local argumentlist = { + '--speech', speech, + '--linebreaks', linebreaks, + '--font', font, + '--ex', ex, + '--width', width, + '--extensions', extensions, + elem.text + } + +-- The available options for tex2svg are: + --help Show help [boolean] + --version Show version number [boolean] + --inline process as in-line TeX [boolean] + --speech include speech text [boolean] [default: true] + --linebreaks perform automatic line-breaking [boolean] + --font web font to use [default: "TeX"] + --ex ex-size in pixels [default: 6] + --width width of container in ex [default: 100] + --extensions extra MathJax extensions [default: ""] +-- e.g. 'Safe,TeX/noUndefined' + + if elem.mathtype == 'DisplayMath' and display2svg then + svg = pandoc.pipe(tex2svg, argumentlist, '') + tags = {'', ''} + + elseif elem.mathtype == 'InlineMath' and inline2svg then + table.insert(argumentlist, 1, '--inline') + svg = pandoc.pipe(tex2svg, argumentlist, '') + tags = {'', ''} + + end + + if svg then + + if FORMAT:match '^html.?' then + svg = tags[1] .. svg .. tags[2] + return pandoc.RawInline(FORMAT, svg) + else + local filename = pandoc.sha1(svg) .. '.svg' + pandoc.mediabag.insert(filename, 'image/svg+xml', svg) + return pandoc.Image('', filename) + end + + else + + return elem + + end + +end + + +-- Redefining the execution order. +return { + {Meta = Meta}, + {Math = Math} +} diff --git a/math2svg/sample.md b/math2svg/sample.md new file mode 100644 index 00000000..292273d7 --- /dev/null +++ b/math2svg/sample.md @@ -0,0 +1,27 @@ +Only plane waves in the far field exhibit the +[characteristic impedance of free space](https://en.wikipedia.org/wiki/Impedance_of_free_space), which is exactly: + +$$Z_0 = \frac{\left|\vec{E}\right|}{\left|\vec{H}\right|} = \sqrt{\frac{\mu_0}{\epsilon_0}} = \mu_0\cdot c_0 \approx 376.73\,\Omega$$ + +| where: +| $c_0 = 299\,792\,458\,\frac{\text{m}}{\text{s}}$: the speed of light in free space +| $\mu_0 = 4\pi\cdot10^{-7}\frac{\text{H}}{\text{m}}$: the free space permeability +| $\epsilon_0 = \frac{1}{\mu_0 c_0^2}$: the absolute permittivity of free space +| $Z_0$: the characteristic impedance of free space + +--- + +Euler's formula: + +$$\e{\j\omega t} = \cos{\omega t} + \j \sin{\omega t}$$ + +--- + +The input impedance $Z_\text{in,$\,$short}$ of a transmission line stub terminated in a short circuit is given by: + +$$Z_\text{in,$\,$short} = Z_\text{c} \tanh{(\gamma\ell)} \approx \j\tan{(\beta\ell)}\,Z_\text{c}$$ + +| where: +| $\gamma = \alpha + \j\beta$ is the [propagation constant $\gamma$](https://en.wikipedia.org/wiki/Propagation_constant#Definition), +| $\alpha$ is the attenuation constant, and +| $\beta$ is the [phase constant](https://en.wikipedia.org/wiki/Propagation_constant#Phase_constant). diff --git a/math2svg/sample.yaml b/math2svg/sample.yaml new file mode 100644 index 00000000..f52cdbc1 --- /dev/null +++ b/math2svg/sample.yaml @@ -0,0 +1,5 @@ +--- +header-includes: | + \newcommand{\j}{\text{j}} + \newcommand{\e}[1]{\,\text{e}^{#1}} +...