.. code:: sh
- sudo pacman -S texlive-most pelican python-pillow
+ sudo pacman -S texlive-most pelican python-pillow python-matplotlib
cower -d python-pyphen # Build the python-pyphen package from AUR
On Ubuntu you need these:
.. code:: sh
- sudo apt-get install texlive-base texlive-latex-extra texlive-fonts-extra
+ sudo apt-get install texlive-base texlive-latex-extra texlive-fonts-extra python3-matplotlib
pip3 install pelican Pyphen Pillow
Once you have all the dependencies, simply go to the ``site/`` subdirectory and
text-align: center;
}
-/* Colored math block, inline math */
+/* Plots */
+div.m-plot svg {
+ text-align: center;
+}
+div.m-plot .m-background {
+ fill: var(--plot-background-color);
+}
+/* Font sizes are hardcoded in m.plots, change there first! */
+div.m-plot svg .m-label { font-size: 11px; }
+div.m-plot svg .m-title { font-size: 13px; }
+div.m-plot svg .m-label, div.m-plot svg .m-title { fill: var(--color); }
+div.m-plot svg .m-line {
+ stroke: var(--color);
+ stroke-width: 0.8;
+}
+div.m-plot svg .m-error {
+ stroke: var(--plot-error-color);
+ stroke-width: 1.5;
+}
+div.m-plot svg .m-label.m-dim { fill: var(--dim-color); }
+
+/* Colored math block, inline math, plots */
div.m-math svg, svg.m-math { fill: var(--color); }
-div.m-math.m-default svg, svg.m-math.m-default { fill: var(--default-color); }
-div.m-math.m-primary svg, svg.m-math.m-primary { fill: var(--primary-color); }
-div.m-math.m-success svg, svg.m-math.m-success { fill: var(--success-color); }
-div.m-math.m-warning svg, svg.m-math.m-warning { fill: var(--warning-color); }
-div.m-math.m-danger svg, svg.m-math.m-danger { fill: var(--danger-color); }
-div.m-math.m-info svg, svg.m-math.m-info { fill: var(--info-color); }
-div.m-math.m-dim svg, svg.m-math.m-dim { fill: var(--dim-color); }
+div.m-math.m-default svg, svg.m-math.m-default, div.m-plot svg .m-bar.m-default {
+ fill: var(--default-color);
+}
+div.m-math.m-primary svg, svg.m-math.m-primary, div.m-plot svg .m-bar.m-primary {
+ fill: var(--primary-color);
+}
+div.m-math.m-success svg, svg.m-math.m-success, div.m-plot svg .m-bar.m-success {
+ fill: var(--success-color);
+}
+div.m-math.m-warning svg, svg.m-math.m-warning, div.m-plot svg .m-bar.m-warning {
+ fill: var(--warning-color);
+}
+div.m-math.m-danger svg, svg.m-math.m-danger, div.m-plot svg .m-bar.m-danger {
+ fill: var(--danger-color);
+}
+div.m-math.m-info svg, svg.m-math.m-info, div.m-plot svg .m-bar.m-info {
+ fill: var(--info-color);
+}
+div.m-math.m-dim svg, svg.m-math.m-dim, div.m-plot svg .m-bar.m-dim {
+ fill: var(--dim-color);
+}
/* Spacing after every block element, but not after the last */
p, ul, ol, dl, blockquote, pre, figure.m-code-figure, figure.m-console-figure,
overflow-y: hidden;
text-align: center;
}
+div.m-plot svg {
+ text-align: center;
+}
+div.m-plot .m-background {
+ fill: #34424d;
+}
+div.m-plot svg .m-label { font-size: 11px; }
+div.m-plot svg .m-title { font-size: 13px; }
+div.m-plot svg .m-label, div.m-plot svg .m-title { fill: #dcdcdc; }
+div.m-plot svg .m-line {
+ stroke: #dcdcdc;
+ stroke-width: 0.8;
+}
+div.m-plot svg .m-error {
+ stroke: #ffffff;
+ stroke-width: 1.5;
+}
+div.m-plot svg .m-label.m-dim { fill: #747474; }
div.m-math svg, svg.m-math { fill: #dcdcdc; }
-div.m-math.m-default svg, svg.m-math.m-default { fill: #dcdcdc; }
-div.m-math.m-primary svg, svg.m-math.m-primary { fill: #a5c9ea; }
-div.m-math.m-success svg, svg.m-math.m-success { fill: #3bd267; }
-div.m-math.m-warning svg, svg.m-math.m-warning { fill: #c7cf2f; }
-div.m-math.m-danger svg, svg.m-math.m-danger { fill: #cd3431; }
-div.m-math.m-info svg, svg.m-math.m-info { fill: #2f83cc; }
-div.m-math.m-dim svg, svg.m-math.m-dim { fill: #747474; }
+div.m-math.m-default svg, svg.m-math.m-default, div.m-plot svg .m-bar.m-default {
+ fill: #dcdcdc;
+}
+div.m-math.m-primary svg, svg.m-math.m-primary, div.m-plot svg .m-bar.m-primary {
+ fill: #a5c9ea;
+}
+div.m-math.m-success svg, svg.m-math.m-success, div.m-plot svg .m-bar.m-success {
+ fill: #3bd267;
+}
+div.m-math.m-warning svg, svg.m-math.m-warning, div.m-plot svg .m-bar.m-warning {
+ fill: #c7cf2f;
+}
+div.m-math.m-danger svg, svg.m-math.m-danger, div.m-plot svg .m-bar.m-danger {
+ fill: #cd3431;
+}
+div.m-math.m-info svg, svg.m-math.m-info, div.m-plot svg .m-bar.m-info {
+ fill: #2f83cc;
+}
+div.m-math.m-dim svg, svg.m-math.m-dim, div.m-plot svg .m-bar.m-dim {
+ fill: #747474;
+}
p, ul, ol, dl, blockquote, pre, figure.m-code-figure, figure.m-console-figure,
hr, article, article > header, article section,
.m-note, .m-frame, .m-block, div.m-button,
overflow-y: hidden;
text-align: center;
}
+div.m-plot svg {
+ text-align: center;
+}
+div.m-plot .m-background {
+ fill: #34424d;
+}
+div.m-plot svg .m-label { font-size: 11px; }
+div.m-plot svg .m-title { font-size: 13px; }
+div.m-plot svg .m-label, div.m-plot svg .m-title { fill: #dcdcdc; }
+div.m-plot svg .m-line {
+ stroke: #dcdcdc;
+ stroke-width: 0.8;
+}
+div.m-plot svg .m-error {
+ stroke: #ffffff;
+ stroke-width: 1.5;
+}
+div.m-plot svg .m-label.m-dim { fill: #747474; }
div.m-math svg, svg.m-math { fill: #dcdcdc; }
-div.m-math.m-default svg, svg.m-math.m-default { fill: #dcdcdc; }
-div.m-math.m-primary svg, svg.m-math.m-primary { fill: #a5c9ea; }
-div.m-math.m-success svg, svg.m-math.m-success { fill: #3bd267; }
-div.m-math.m-warning svg, svg.m-math.m-warning { fill: #c7cf2f; }
-div.m-math.m-danger svg, svg.m-math.m-danger { fill: #cd3431; }
-div.m-math.m-info svg, svg.m-math.m-info { fill: #2f83cc; }
-div.m-math.m-dim svg, svg.m-math.m-dim { fill: #747474; }
+div.m-math.m-default svg, svg.m-math.m-default, div.m-plot svg .m-bar.m-default {
+ fill: #dcdcdc;
+}
+div.m-math.m-primary svg, svg.m-math.m-primary, div.m-plot svg .m-bar.m-primary {
+ fill: #a5c9ea;
+}
+div.m-math.m-success svg, svg.m-math.m-success, div.m-plot svg .m-bar.m-success {
+ fill: #3bd267;
+}
+div.m-math.m-warning svg, svg.m-math.m-warning, div.m-plot svg .m-bar.m-warning {
+ fill: #c7cf2f;
+}
+div.m-math.m-danger svg, svg.m-math.m-danger, div.m-plot svg .m-bar.m-danger {
+ fill: #cd3431;
+}
+div.m-math.m-info svg, svg.m-math.m-info, div.m-plot svg .m-bar.m-info {
+ fill: #2f83cc;
+}
+div.m-math.m-dim svg, svg.m-math.m-dim, div.m-plot svg .m-bar.m-dim {
+ fill: #747474;
+}
p, ul, ol, dl, blockquote, pre, figure.m-code-figure, figure.m-console-figure,
hr, article, article > header, article section,
.m-note, .m-frame, .m-block, div.m-button,
--navpanel-link-color: #ffffff;
--navpanel-link-active-color: #a5c9ea;
+ /* Plots */
+ --plot-background-color: #34424d;
+ --plot-error-color: #ffffff;
+
/* Colored components */
--default-color: #dcdcdc;
--default-filled-color: #dcdcdc;
overflow-y: hidden;
text-align: center;
}
+div.m-plot svg {
+ text-align: center;
+}
+div.m-plot .m-background {
+ fill: #fbf0ec;
+}
+div.m-plot svg .m-label { font-size: 11px; }
+div.m-plot svg .m-title { font-size: 13px; }
+div.m-plot svg .m-label, div.m-plot svg .m-title { fill: #000000; }
+div.m-plot svg .m-line {
+ stroke: #000000;
+ stroke-width: 0.8;
+}
+div.m-plot svg .m-error {
+ stroke: #000000;
+ stroke-width: 1.5;
+}
+div.m-plot svg .m-label.m-dim { fill: #bdbdbd; }
div.m-math svg, svg.m-math { fill: #000000; }
-div.m-math.m-default svg, svg.m-math.m-default { fill: #000000; }
-div.m-math.m-primary svg, svg.m-math.m-primary { fill: #cb4b16; }
-div.m-math.m-success svg, svg.m-math.m-success { fill: #31c25d; }
-div.m-math.m-warning svg, svg.m-math.m-warning { fill: #c7cf2f; }
-div.m-math.m-danger svg, svg.m-math.m-danger { fill: #f60000; }
-div.m-math.m-info svg, svg.m-math.m-info { fill: #2e7dc5; }
-div.m-math.m-dim svg, svg.m-math.m-dim { fill: #bdbdbd; }
+div.m-math.m-default svg, svg.m-math.m-default, div.m-plot svg .m-bar.m-default {
+ fill: #000000;
+}
+div.m-math.m-primary svg, svg.m-math.m-primary, div.m-plot svg .m-bar.m-primary {
+ fill: #cb4b16;
+}
+div.m-math.m-success svg, svg.m-math.m-success, div.m-plot svg .m-bar.m-success {
+ fill: #31c25d;
+}
+div.m-math.m-warning svg, svg.m-math.m-warning, div.m-plot svg .m-bar.m-warning {
+ fill: #c7cf2f;
+}
+div.m-math.m-danger svg, svg.m-math.m-danger, div.m-plot svg .m-bar.m-danger {
+ fill: #f60000;
+}
+div.m-math.m-info svg, svg.m-math.m-info, div.m-plot svg .m-bar.m-info {
+ fill: #2e7dc5;
+}
+div.m-math.m-dim svg, svg.m-math.m-dim, div.m-plot svg .m-bar.m-dim {
+ fill: #bdbdbd;
+}
p, ul, ol, dl, blockquote, pre, figure.m-code-figure, figure.m-console-figure,
hr, article, article > header, article section,
.m-note, .m-frame, .m-block, div.m-button,
overflow-y: hidden;
text-align: center;
}
+div.m-plot svg {
+ text-align: center;
+}
+div.m-plot .m-background {
+ fill: #fbf0ec;
+}
+div.m-plot svg .m-label { font-size: 11px; }
+div.m-plot svg .m-title { font-size: 13px; }
+div.m-plot svg .m-label, div.m-plot svg .m-title { fill: #000000; }
+div.m-plot svg .m-line {
+ stroke: #000000;
+ stroke-width: 0.8;
+}
+div.m-plot svg .m-error {
+ stroke: #000000;
+ stroke-width: 1.5;
+}
+div.m-plot svg .m-label.m-dim { fill: #bdbdbd; }
div.m-math svg, svg.m-math { fill: #000000; }
-div.m-math.m-default svg, svg.m-math.m-default { fill: #000000; }
-div.m-math.m-primary svg, svg.m-math.m-primary { fill: #cb4b16; }
-div.m-math.m-success svg, svg.m-math.m-success { fill: #31c25d; }
-div.m-math.m-warning svg, svg.m-math.m-warning { fill: #c7cf2f; }
-div.m-math.m-danger svg, svg.m-math.m-danger { fill: #f60000; }
-div.m-math.m-info svg, svg.m-math.m-info { fill: #2e7dc5; }
-div.m-math.m-dim svg, svg.m-math.m-dim { fill: #bdbdbd; }
+div.m-math.m-default svg, svg.m-math.m-default, div.m-plot svg .m-bar.m-default {
+ fill: #000000;
+}
+div.m-math.m-primary svg, svg.m-math.m-primary, div.m-plot svg .m-bar.m-primary {
+ fill: #cb4b16;
+}
+div.m-math.m-success svg, svg.m-math.m-success, div.m-plot svg .m-bar.m-success {
+ fill: #31c25d;
+}
+div.m-math.m-warning svg, svg.m-math.m-warning, div.m-plot svg .m-bar.m-warning {
+ fill: #c7cf2f;
+}
+div.m-math.m-danger svg, svg.m-math.m-danger, div.m-plot svg .m-bar.m-danger {
+ fill: #f60000;
+}
+div.m-math.m-info svg, svg.m-math.m-info, div.m-plot svg .m-bar.m-info {
+ fill: #2e7dc5;
+}
+div.m-math.m-dim svg, svg.m-math.m-dim, div.m-plot svg .m-bar.m-dim {
+ fill: #bdbdbd;
+}
p, ul, ol, dl, blockquote, pre, figure.m-code-figure, figure.m-console-figure,
hr, article, article > header, article section,
.m-note, .m-frame, .m-block, div.m-button,
--navpanel-link-color: #292929;
--navpanel-link-active-color: #cb4b16;
+ /* Plots */
+ --plot-background-color: #fbf0ec;
+ --plot-error-color: #000000;
+
/* Colored components */
--default-color: #000000;
--default-filled-color: #000000;
--- /dev/null
+<svg viewBox="0 0 576 113.76">
+ <path d="M 68.22875 70.705312 L 560.62 70.705312 L 560.62 27.136406 L 68.22875 27.136406 z" class="m-background"/>
+
+ <path d="M 68.22875 29.116811 L 482.89476 29.116811 L 482.89476 46.72041 L 68.22875 46.72041 z" class="m-bar m-warning"/>
+ <path d="M 68.22875 51.121309 L 403.676117 51.121309 L 403.676117 68.724908 L 68.22875 68.724908 z" class="m-bar m-primary"/>
+
+ <defs><path d="M 0 0 L 0 3.5" id="m15f0a304df" class="m-line"/></defs>
+ <use x="68.22875" xlink:href="#m15f0a304df" y="70.705312"/>
+ <text class="m-label" style="text-anchor:middle;" transform="rotate(-0, 68.22875, 85.537656)" x="68.22875" y="85.537656">0</text>
+ <use x="144.036065" xlink:href="#m15f0a304df" y="70.705312"/>
+ <text class="m-label" style="text-anchor:middle;" transform="rotate(-0, 144.036065, 85.537656)" x="144.036065" y="85.537656">20</text>
+ <use x="219.843379" xlink:href="#m15f0a304df" y="70.705312"/>
+ <text class="m-label" style="text-anchor:middle;" transform="rotate(-0, 219.843379, 85.537656)" x="219.843379" y="85.537656">40</text>
+ <use x="295.650694" xlink:href="#m15f0a304df" y="70.705312"/>
+ <text class="m-label" style="text-anchor:middle;" transform="rotate(-0, 295.650694, 85.537656)" x="295.650694" y="85.537656">60</text>
+ <use x="371.458008" xlink:href="#m15f0a304df" y="70.705312"/>
+ <text class="m-label" style="text-anchor:middle;" transform="rotate(-0, 371.458008, 85.537656)" x="371.458008" y="85.537656">80</text>
+ <use x="447.265323" xlink:href="#m15f0a304df" y="70.705312"/>
+ <text class="m-label" style="text-anchor:middle;" transform="rotate(-0, 447.265323, 85.537656)" x="447.265323" y="85.537656">100</text>
+ <use x="523.072637" xlink:href="#m15f0a304df" y="70.705312"/>
+ <text class="m-label" style="text-anchor:middle;" transform="rotate(-0, 523.072637, 85.537656)" x="523.072637" y="85.537656">120</text>
+
+ <text class="m-label" style="text-anchor:middle;" transform="rotate(-0, 314.424375, 99.625)" x="314.424375" y="99.625">km/h</text>
+
+ <defs><path d="M 0 0 L -3.5 0" id="mba4ce04b6c" class="m-line"/></defs>
+ <use x="68.22875" xlink:href="#mba4ce04b6c" y="37.91861"/>
+ <text class="m-label" style="text-anchor:end;" transform="rotate(-0, 61.22875, 41.834782)" x="61.22875" y="41.834782">Cheetah</text>
+ <use x="68.22875" xlink:href="#mba4ce04b6c" y="59.923108"/>
+ <text class="m-label" style="text-anchor:end;" transform="rotate(-0, 61.22875, 63.83928)" x="61.22875" y="63.83928">Pronghorn</text>
+
+ <path d="M 428.616723 37.91861 L 537.172798 37.91861" class="m-error"/>
+ <path d="M 382.829105 59.923108 L 424.523128 59.923108" class="m-error"/>
+ <defs><path d="M 0 5 L 0 -5" id="m468f9279d6" class="m-error"/></defs>
+ <use x="428.616723" xlink:href="#m468f9279d6" y="37.91861"/>
+ <use x="382.829105" xlink:href="#m468f9279d6" y="59.923108"/>
+ <use x="537.172798" xlink:href="#m468f9279d6" y="37.91861"/>
+ <use x="424.523128" xlink:href="#m468f9279d6" y="59.923108"/>
+
+ <text class="m-title" style="text-anchor:middle;" transform="rotate(-0, 314.424375, 21.136406)" x="314.424375" y="21.136406">Fastest animals</text>
+</svg>
integrates LaTeX math directly into your :abbr:`reST <reStructuredText>`
markup for convenient content authoring.
+`Plots`_
+========
+
+Wrap a :html:`<svg>` element in a :html:`<div class="m-plot">` to make it
+centered and occupying full width. Mark plot axes background with
+:css:`.m-background`, bars can be styled using :css:`.m-bar` and a
+corresponding `CSS color class <#colors>`_. Mark ticks and various other lines
+with :css:`.m-line`, error bars with :css:`.m-error`. Use
+:html:`<text class="m-label">` for tick and axes labels and
+:html:`<text class="m-title">` for graph title.
+
+.. code-figure::
+
+ .. code:: html
+
+ <div class="m-plot"><svg>
+ <path d="M 68.22875 70.705312 ..." class="m-background"/>
+ <path d="M 68.22875 29.116811 ..." class="m-bar m-warning"/>
+ <path d="M 68.22875 51.121309 ..." class="m-bar m-primary"/>
+ ...
+ <defs><path d="..." id="mba4ce04b6c" class="m-line"/></defs>
+ <use x="68.22875" xlink:href="#mba4ce04b6c" y="37.91861"/>
+ <text class="m-label" style="text-anchor:end;" ...>Cheetah</text>
+ ...
+ <path d="M 428.616723 37.91861 ..." class="m-error"/>
+ ...
+ <text class="m-title" style="text-anchor:middle;" ...>Fastest animals</text>
+ </svg></div>
+
+ .. container:: m-plot
+
+ .. raw:: html
+ :file: components-plot.svg
+
+.. note-success::
+
+ Plot styling is designed to be used with extenal tools, for example Python
+ Matplotlib. If you use Pelican, m.css has a `Plots <{filename}/plugins/plots.rst>`__
+ plugin that allows you to produce plots using :rst:`.. plot::` directives
+ directly in your :abbr:`reST <reStructuredText>` markup.
+
`Padding`_
==========
- :gh:`m.images <mosra/m.css$master/pelican-plugins/m/images.py>`
- :gh:`m.math <mosra/m.css$master/pelican-plugins/m/math.py>` (needs also :gh:`latex2svg <mosra/m.css$master/pelican-plugins/latex2svg.py>`),
:gh:`m.code <mosra/m.css$master/pelican-plugins/m/code.py>` (needs also :gh:`ansilexer <mosra/m.css$master/pelican-plugins/ansilexer.py>`)
+- :gh:`m.plots <mosra/m.css$master/pelican-plugins/m/plots.py>`
- :gh:`m.gh <mosra/m.css$master/pelican-plugins/m/gh.py>`,
:gh:`m.dox <mosra/m.css$master/pelican-plugins/m/dox.py>`,
:gh:`m.gl <mosra/m.css$master/pelican-plugins/m/gl.py>`,
that you can but don't have to use. With these, math and code snippets can be
entered directly in your :abbr:`reST <reStructuredText>` sources.
+`Plots » <{filename}/plugins/plots.rst>`_
+===================================================
+
+With :py:`m.plots` you can render various graphs and charts directly from
+values in your :abbr:`reST <reStructuredText>` sources. The result is embedded
+as an inline SVG and can be styled using CSS like everything else.
+
`Links and other » <{filename}/plugins/links.rst>`_
===================================================
.. note-dim::
:class: m-text-center
- `« Math and code <{filename}/plugins/math-and-code.rst>`_ | `Pelican plugins <{filename}/plugins.rst>`_ | `Metadata » <{filename}/plugins/metadata.rst>`_
+ `« Plots <{filename}/plugins/plots.rst>`_ | `Pelican plugins <{filename}/plugins.rst>`_ | `Metadata » <{filename}/plugins/metadata.rst>`_
.. role:: py(code)
:language: py
.. note-dim::
:class: m-text-center
- `« Images <{filename}/plugins/images.rst>`_ | `Pelican plugins <{filename}/plugins.rst>`_ | `Links and other » <{filename}/plugins/links.rst>`_
+ `« Images <{filename}/plugins/images.rst>`_ | `Pelican plugins <{filename}/plugins.rst>`_ | `Plots » <{filename}/plugins/plots.rst>`_
.. role:: css(code)
:language: css
--- /dev/null
+..
+ This file is part of m.css.
+
+ Copyright © 2017, 2018 Vladimír Vondruš <mosra@centrum.cz>
+
+ 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.
+..
+
+Test
+####
+
+:save_as: plugins/plots/test/index.html
+:breadcrumb: {filename}/plugins.rst Pelican plugins
+ {filename}/plugins/plots.rst Plots
+
+.. container:: m-row
+
+ .. container:: m-col-m-6
+
+ .. plot:: Fastest animals
+ :type: barh
+ :labels:
+ Cheetah
+ Pronghorn
+ :units: km/h
+ :values: 109.4 88.5
+ :colors: warning primary
+ :errors: 14.32 5.5
+
+ .. container:: m-col-m-6
+
+ .. plot:: Fastest animals
+ :type: barh
+ :labels:
+ Cheetah
+ Pronghorn
+ Springbok
+ Wildebeest
+ :units: km/h
+ :values: 109.4 88.5 88 80.5
+ :colors: warning primary danger info
--- /dev/null
+..
+ This file is part of m.css.
+
+ Copyright © 2017, 2018 Vladimír Vondruš <mosra@centrum.cz>
+
+ 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.
+..
+
+Plots
+#####
+
+:breadcrumb: {filename}/plugins.rst Pelican plugins
+:footer:
+ .. note-dim::
+ :class: m-text-center
+
+ `« Math and code <{filename}/plugins/math-and-code.rst>`_ | `Pelican plugins <{filename}/plugins.rst>`_ | `Links and other » <{filename}/plugins/math-and-code.rst>`_
+
+Allows you to render plots directly from data specified inline in the page
+source. Similarly to `math rendering <{filename}/admire/math.rst>`_, the plots
+are rendered to a SVG that's embedded directly in the HTML markup.
+
+.. contents::
+ :class: m-block m-default
+
+`How to use`_
+=============
+
+Download the `m/plots.py <{filename}/plugins.rst>`_ file, put it including the
+``m/`` directory into one of your :py:`PLUGIN_PATHS` and add ``m.plots``
+package to your :py:`PLUGINS` in ``pelicanconf.py``.
+
+.. code:: python
+
+ PLUGINS += ['m.plots']
+
+In addition you need the `Matplotlib <https://matplotlib.org/>`_ library
+installed. Get it via ``pip`` or your distribution package manager:
+
+.. code:: sh
+
+ pip3 install matplotlib
+
+The plugin produces SVG plots that make use of the
+`CSS plot styling <{filename}/css/components.rst#plots>`_.
+
+`Bar graphs`_
+=============
+
+Currently the only supported plot type is a horizontal bar graph, denoted by
+:rst:`:type: hbar`:
+
+.. code-figure::
+
+ .. code:: rst
+
+ .. plot:: Fastest animals
+ :type: barh
+ :labels:
+ Cheetah
+ Pronghorn
+ Springbok
+ Wildebeest
+ :units: km/h
+ :values: 109.4 88.5 88 80.5
+
+ .. plot:: Fastest animals
+ :type: barh
+ :labels:
+ Cheetah
+ Pronghorn
+ Springbok
+ Wildebeest
+ :units: km/h
+ :values: 109.4 88.5 88 80.5
+
+The multi-line :rst:`:labels:` option contain value labels, one per line. You
+can specify unit label using :rst:`:units:`, particular values go into
+:rst:`:values:` separated by whitespace, there should me as many values as
+labels. Hovering over the bars will show the concrete value in a title.
+
+It's also optionally possible to add error bars using :rst:`:error:` and
+configure bar colors using :rst:`:colors:`. The colors correspond to m.css
+`color classes <{filename}/css/components.rst#colors>`_ and you can either
+use one color for all or one for each value, separated by whitespace. It's
+possible to add an extra line of labels using :rst:`:labels_extra:`. Bar graph
+height is calculated automatically based on amount of values, you can adjust
+the bar height using :rst:`:bar_height:`. Default value is :py:`0.4`.
+
+.. code-figure::
+
+ .. code:: rst
+
+ .. plot:: Runtime cost
+ :type: barh
+ :labels:
+ Ours minimal
+ Ours default
+ 3rd party
+ Full setup
+ :labels_extra:
+ 15 modules
+ 60 modules
+ 200 modules
+ for comparison
+ :units: µs
+ :values: 15.09 84.98 197.13 934.27
+ :errors: 0.74 3.65 9.45 25.66
+ :colors: success info danger dim
+ :bar_height: 0.6
+
+ .. plot:: Runtime cost
+ :type: barh
+ :labels:
+ Ours minimal
+ Ours default
+ 3rd party
+ Full setup
+ :labels_extra:
+ 15 modules
+ 60 modules
+ 200 modules
+ for comparison
+ :units: µs
+ :values: 15.09 84.98 197.13 934.27
+ :errors: 0.74 3.65 9.45 25.66
+ :colors: success info danger dim
+ :bar_height: 0.6
--- /dev/null
+#
+# This file is part of m.css.
+#
+# Copyright © 2017, 2018 Vladimír Vondruš <mosra@centrum.cz>
+#
+# 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.
+#
+
+import re
+
+from docutils import nodes, utils
+from docutils.parsers import rst
+from docutils.parsers.rst import directives
+from docutils.parsers.rst.roles import set_classes
+
+import matplotlib as mpl
+import matplotlib.pyplot as plt
+import numpy as np
+import io
+
+import pelican.signals
+
+mpl.rcParams['font.family'] = 'Source Sans Pro'
+mpl.rcParams['font.size'] = '11'
+mpl.rcParams['axes.titlesize'] = '13'
+
+# Plot background. Replaced with .m-plot .m-background later, equivalent to
+# --default-filled-background-color
+mpl.rcParams['axes.facecolor'] = '#cafe01'
+
+# All of these should match --color, replaced with .m-plot .m-text
+mpl.rcParams['text.color'] = '#cafe02'
+mpl.rcParams['axes.labelcolor'] = '#cafe02'
+mpl.rcParams['xtick.color'] = '#cafe02'
+mpl.rcParams['ytick.color'] = '#cafe02'
+
+# no need to have a border around the plot
+mpl.rcParams['axes.spines.left'] = False
+mpl.rcParams['axes.spines.right'] = False
+mpl.rcParams['axes.spines.top'] = False
+mpl.rcParams['axes.spines.bottom'] = False
+
+mpl.rcParams['svg.fonttype'] = 'none' # otherwise it renders text to paths
+mpl.rcParams['figure.autolayout'] = True # so it relayouts everything to fit
+
+# Gets increased for every graph on a page to (hopefully) ensure unique SVG IDs
+mpl.rcParams['svg.hashsalt'] = 0
+
+# Color codes for bars
+style_mapping = {
+ 'default': '#cafe03',
+ 'primary': '#cafe04',
+ 'success': '#cafe05',
+ 'warning': '#cafe06',
+ 'danger': '#cafe07',
+ 'info': '#cafe08',
+ 'dim': '#cafe09'
+}
+
+# Patch to remove preamble and hardcoded sizes
+_patch_src = re.compile(r"""<\?xml version="1\.0" encoding="utf-8" standalone="no"\?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1\.1//EN"
+ "http://www\.w3\.org/Graphics/SVG/1\.1/DTD/svg11\.dtd">
+<!-- Created with matplotlib \(http://matplotlib.org/\) -->
+<svg height="\d+(\.\d+)?pt" version="1.1" (?P<viewBox>viewBox="0 0 \d+ \d+(\.\d+)?") width="\d+(\.\d+)?pt" xmlns="http://www\.w3\.org/2000/svg" xmlns:xlink="http://www\.w3\.org/1999/xlink">
+""")
+_patch_dst = r"""<svg \g<viewBox>>
+"""
+
+# Remove needless newlines and trailing space in path data
+_path_patch_src = re.compile('(?P<prev>[\\dz]) ?\n(?P<next>[LMz])', re.MULTILINE)
+_path_patch_dst = '\\g<prev> \\g<next>'
+_path_patch2_src = re.compile(' ?\n"')
+_path_patch2_dst = '"'
+
+# Mapping from color codes to CSS classes
+_class_mapping = [
+ # Graph background
+ ('style="fill:#cafe01;"', 'class="m-background"'),
+
+ # Tick <path> definition in <defs>
+ ('style="stroke:#cafe02;stroke-width:0.8;"', 'class="m-line"'),
+ # <use>, everything is defined in <defs>, no need to repeat
+ ('<use style="fill:#cafe02;stroke:#cafe02;stroke-width:0.8;"', '<use'),
+
+ # Label text on left
+ ('style="fill:#cafe02;font-family:Source Sans Pro;font-size:11px;font-style:normal;font-weight:normal;"', 'class="m-label"'),
+ # Label text on bottom (has extra style params)
+ ('style="fill:#cafe02;font-family:Source Sans Pro;font-size:11px;font-style:normal;font-weight:normal;', 'class="m-label" style="'),
+ # Secondary label text
+ ('style="fill:#cafe0b;font-family:Source Sans Pro;font-size:11px;font-style:normal;font-weight:normal;"', 'class="m-label m-dim"'),
+ # Title text
+ ('style="fill:#cafe02;font-family:Source Sans Pro;font-size:13px;font-style:normal;font-weight:normal;', 'class="m-title" style="'),
+
+ # Bar colors
+ ('style="fill:#cafe03;"', 'class="m-bar m-default"'),
+ ('style="fill:#cafe04;"', 'class="m-bar m-primary"'),
+ ('style="fill:#cafe05;"', 'class="m-bar m-success"'),
+ ('style="fill:#cafe06;"', 'class="m-bar m-warning"'),
+ ('style="fill:#cafe07;"', 'class="m-bar m-danger"'),
+ ('style="fill:#cafe08;"', 'class="m-bar m-info"'),
+ ('style="fill:#cafe09;"', 'class="m-bar m-dim"'),
+
+ # Error bar line
+ ('style="fill:none;stroke:#cafe0a;stroke-width:1.5;"', 'class="m-error"'),
+ # Error bar <path> definition in <defs>
+ ('style="stroke:#cafe0a;"', 'class="m-error"'),
+ # <use>, everything is defined in <defs>, no need to repeat
+ ('<use style="fill:#cafe0a;stroke:#cafe0a;"', '<use'),
+]
+
+# Titles for bars
+_bar_titles_src = '<g id="plot{}-value{}">'
+_bar_titles_dst = '<g id="plot{}-value{}"><title>{} {}</title>'
+_bar_titles_dst_error = '<g id="plot{}-value{}"><title>{} ± {} {}</title>'
+
+class Plot(rst.Directive):
+ required_arguments = 1
+ optional_arguments = 0
+ final_argument_whitespace = True
+ option_spec = {'class': directives.class_option,
+ 'name': directives.unchanged,
+ 'type': directives.unchanged_required,
+ 'labels': directives.unchanged_required,
+ 'labels_extra': directives.unchanged,
+ 'units': directives.unchanged_required,
+ 'values': directives.unchanged_required,
+ 'errors': directives.unchanged,
+ 'colors': directives.unchanged,
+ 'bar_height': directives.unchanged}
+ has_content = False
+
+ def run(self):
+ set_classes(self.options)
+
+ # Type
+ assert self.options['type'] == 'barh'
+
+ # Graph title and axis labels. Value labels are one per line.
+ title = self.arguments[0]
+ units = self.options['units']
+ labels = self.options['labels'].split('\n')
+
+ # Optional extra labels
+ if 'labels_extra' in self.options:
+ labels_extra = self.options['labels_extra'].split('\n')
+ assert len(labels_extra) == len(labels)
+ else:
+ labels_extra = None
+
+ # Values. Should be one for each label.
+ values = [float(v) for v in self.options['values'].split()]
+ assert len(values) == len(labels)
+
+ # Optional errors
+ if 'errors' in self.options:
+ errors = [float(e) for e in self.options['errors'].split()]
+ else:
+ errors = None
+
+ # Colors. Should be either one for all or one for every value
+ colors = [style_mapping[c] for c in self.options.get('colors', 'default').split()]
+ if len(colors) == 1: colors = colors[0]
+ else: assert len(colors) == len(labels)
+
+ # Bar height
+ bar_height = float(self.options.get('bar_height', '0.4'))
+
+ # Increase hashsalt for every plot to ensure (hopefully) unique SVG IDs
+ mpl.rcParams['svg.hashsalt'] = int(mpl.rcParams['svg.hashsalt']) + 1
+
+ # Setup the graph
+ fig, ax = plt.subplots()
+ # TODO: let matplotlib calculate the height somehow
+ fig.set_size_inches(8, 0.78 + len(values)*bar_height)
+ yticks = np.arange(len(labels))
+ plot = ax.barh(yticks, values, xerr=errors,
+ align='center', color=colors, ecolor='#cafe0a', capsize=5*bar_height/0.4)
+ for i, v in enumerate(plot):
+ v.set_gid('plot{}-value{}'.format(mpl.rcParams['svg.hashsalt'], i))
+ ax.set_yticks(yticks)
+ ax.invert_yaxis() # top-to-bottom
+ ax.set_xlabel(units)
+ ax.set_title(title)
+
+ # Value labels. If extra label is specified, create two multiline texts
+ # with first having the second line empty and second having the first
+ # line empty.
+ if labels_extra:
+ ax.set_yticklabels([y + '\n' for y in labels])
+ for i, label in enumerate(ax.get_yticklabels()):
+ ax.text(0, i + 0.05, '\n' + labels_extra[i],
+ va='center', ha='right',
+ transform=label.get_transform(), color='#cafe0b')
+ else: ax.set_yticklabels(labels)
+
+ # Export to SVG
+ fig.patch.set_visible(False) # hide the white background
+ imgdata = io.StringIO()
+ fig.savefig(imgdata, format='svg')
+
+ # Patch the rendered output: remove preable and hardcoded size
+ imgdata = _patch_src.sub(_patch_dst, imgdata.getvalue())
+ # Remove needless newlines and trailing whitespace in path data
+ imgdata = _path_patch2_src.sub(_path_patch2_dst, _path_patch_src.sub(_path_patch_dst, imgdata))
+ # Replace color codes with CSS classes
+ for src, dst in _class_mapping: imgdata = imgdata.replace(src, dst)
+ # Add titles for bars
+ for i in range(len(values)):
+ if errors: imgdata = imgdata.replace(
+ _bar_titles_src.format(mpl.rcParams['svg.hashsalt'], i),
+ _bar_titles_dst_error.format(mpl.rcParams['svg.hashsalt'], i, values[i], errors[i], units))
+ else: imgdata = imgdata.replace(
+ _bar_titles_src.format(mpl.rcParams['svg.hashsalt'], i),
+ _bar_titles_dst.format(mpl.rcParams['svg.hashsalt'], i, values[i], units))
+
+ container = nodes.container(**self.options)
+ container['classes'] += ['m-plot']
+ node = nodes.raw('', imgdata, format='html')
+ container.append(node)
+ return [container]
+
+def new_page(content):
+ mpl.rcParams['svg.hashsalt'] = 0
+
+def register():
+ pelican.signals.content_object_init.connect(new_page)
+ rst.directives.register_directive('plot', Plot)
--- /dev/null
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8" />
+ <title>m.plots | A Pelican Blog</title>
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i" />
+ <link rel="stylesheet" href="static/m-dark.css" />
+ <link rel="canonical" href="page.html" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+</head>
+<body>
+<header><nav id="navigation">
+ <div class="m-container">
+ <div class="m-row">
+ <a href="./" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">A Pelican Blog</a>
+ </div>
+ </div>
+</nav></header>
+<main>
+<article>
+ <div class="m-container m-container-inflatable">
+ <div class="m-row">
+ <div class="m-col-l-10 m-push-l-1">
+ <h1>m.plots</h1>
+<!-- content -->
+<div class="m-plot">
+<svg viewBox="0 0 576 113.76">
+ <defs>
+ <style type="text/css">
+*{stroke-linecap:butt;stroke-linejoin:round;}
+ </style>
+ </defs>
+ <g id="figure_1">
+ <g id="axes_1">
+ <g id="patch_1">
+ <path d="M 53.31 70.705312 L 560.62 70.705312 L 560.62 27.136406 L 53.31 27.136406 z" class="m-background"/>
+ </g>
+ <g id="plot1-value0"><title>15.0 meters, i guess?</title>
+ <path clip-path="url(#p87df164df2)" d="M 53.31 29.116811 L 294.88619 29.116811 L 294.88619 46.72041 L 53.31 46.72041 z" class="m-bar m-success"/>
+ </g>
+ <g id="plot1-value1"><title>30.0 meters, i guess?</title>
+ <path clip-path="url(#p87df164df2)" d="M 53.31 51.121309 L 536.462381 51.121309 L 536.462381 68.724908 L 53.31 68.724908 z" class="m-bar m-success"/>
+ </g>
+ <g id="matplotlib.axis_1">
+ <g id="xtick_1">
+ <g id="line2d_1">
+ <defs>
+ <path d="M 0 0 L 0 3.5" id="m15f0a304df" class="m-line"/>
+ </defs>
+ <g>
+ <use x="53.31" xlink:href="#m15f0a304df" y="70.705312"/>
+ </g>
+ </g>
+ <g id="text_1">
+ <text class="m-label" style="text-anchor:middle;" transform="rotate(-0, 53.31, 85.537656)" x="53.31" y="85.537656">0</text>
+ </g>
+ </g>
+ <g id="xtick_2">
+ <g id="line2d_2">
+ <g>
+ <use x="133.835397" xlink:href="#m15f0a304df" y="70.705312"/>
+ </g>
+ </g>
+ <g id="text_2">
+ <text class="m-label" style="text-anchor:middle;" transform="rotate(-0, 133.835397, 85.537656)" x="133.835397" y="85.537656">5</text>
+ </g>
+ </g>
+ <g id="xtick_3">
+ <g id="line2d_3">
+ <g>
+ <use x="214.360794" xlink:href="#m15f0a304df" y="70.705312"/>
+ </g>
+ </g>
+ <g id="text_3">
+ <text class="m-label" style="text-anchor:middle;" transform="rotate(-0, 214.360794, 85.537656)" x="214.360794" y="85.537656">10</text>
+ </g>
+ </g>
+ <g id="xtick_4">
+ <g id="line2d_4">
+ <g>
+ <use x="294.88619" xlink:href="#m15f0a304df" y="70.705312"/>
+ </g>
+ </g>
+ <g id="text_4">
+ <text class="m-label" style="text-anchor:middle;" transform="rotate(-0, 294.88619, 85.537656)" x="294.88619" y="85.537656">15</text>
+ </g>
+ </g>
+ <g id="xtick_5">
+ <g id="line2d_5">
+ <g>
+ <use x="375.411587" xlink:href="#m15f0a304df" y="70.705312"/>
+ </g>
+ </g>
+ <g id="text_5">
+ <text class="m-label" style="text-anchor:middle;" transform="rotate(-0, 375.411587, 85.537656)" x="375.411587" y="85.537656">20</text>
+ </g>
+ </g>
+ <g id="xtick_6">
+ <g id="line2d_6">
+ <g>
+ <use x="455.936984" xlink:href="#m15f0a304df" y="70.705312"/>
+ </g>
+ </g>
+ <g id="text_6">
+ <text class="m-label" style="text-anchor:middle;" transform="rotate(-0, 455.936984, 85.537656)" x="455.936984" y="85.537656">25</text>
+ </g>
+ </g>
+ <g id="xtick_7">
+ <g id="line2d_7">
+ <g>
+ <use x="536.462381" xlink:href="#m15f0a304df" y="70.705312"/>
+ </g>
+ </g>
+ <g id="text_7">
+ <text class="m-label" style="text-anchor:middle;" transform="rotate(-0, 536.462381, 85.537656)" x="536.462381" y="85.537656">30</text>
+ </g>
+ </g>
+ <g id="text_8">
+ <text class="m-label" style="text-anchor:middle;" transform="rotate(-0, 306.965, 99.415312)" x="306.965" y="99.415312">meters, i guess?</text>
+ </g>
+ </g>
+ <g id="matplotlib.axis_2">
+ <g id="ytick_1">
+ <g id="line2d_8">
+ <defs>
+ <path d="M 0 0 L -3.5 0" id="mba4ce04b6c" class="m-line"/>
+ </defs>
+ <g>
+ <use x="53.31" xlink:href="#mba4ce04b6c" y="37.91861"/>
+ </g>
+ </g>
+ <g id="text_9">
+ <text class="m-label" style="text-anchor:end;" transform="rotate(-0, 46.31, 41.834782)" x="46.31" y="41.834782">First</text>
+ </g>
+ </g>
+ <g id="ytick_2">
+ <g id="line2d_9">
+ <g>
+ <use x="53.31" xlink:href="#mba4ce04b6c" y="59.923108"/>
+ </g>
+ </g>
+ <g id="text_10">
+ <text class="m-label" style="text-anchor:end;" transform="rotate(-0, 46.31, 63.83928)" x="46.31" y="63.83928">Second</text>
+ </g>
+ </g>
+ </g>
+ <g id="text_11">
+ <text class="m-title" style="text-anchor:middle;" transform="rotate(-0, 306.965, 21.136406)" x="306.965" y="21.136406">A plot with a single color</text>
+ </g>
+ </g>
+ </g>
+ <defs>
+ <clipPath id="p87df164df2">
+ <rect height="43.568906" width="507.31" x="53.31" y="27.136406"/>
+ </clipPath>
+ </defs>
+</svg>
+</div>
+<div class="m-plot">
+<svg viewBox="0 0 576 218.16">
+ <defs>
+ <style type="text/css">
+*{stroke-linecap:butt;stroke-linejoin:round;}
+ </style>
+ </defs>
+ <g id="figure_1">
+ <g id="axes_1">
+ <g id="patch_1">
+ <path d="M 60.154063 175.060625 L 560.62 175.060625 L 560.62 27.136406 L 60.154063 27.136406 z" class="m-background"/>
+ </g>
+ <g id="plot2-value0"><title>3.0 ± 0.1 Mondays</title>
+ <path clip-path="url(#p0d12d49cf2)" d="M 60.154063 33.860234 L 294.564338 33.860234 L 294.564338 72.282109 L 60.154063 72.282109 z" class="m-bar m-success"/>
+ </g>
+ <g id="plot2-value1"><title>4.0 ± 2.1 Mondays</title>
+ <path clip-path="url(#p0d12d49cf2)" d="M 60.154063 81.887578 L 372.701096 81.887578 L 372.701096 120.309453 L 60.154063 120.309453 z" class="m-bar m-info"/>
+ </g>
+ <g id="plot2-value2"><title>5.0 ± 1.0 Mondays</title>
+ <path clip-path="url(#p0d12d49cf2)" d="M 60.154063 129.914922 L 450.837854 129.914922 L 450.837854 168.336797 L 60.154063 168.336797 z" class="m-bar m-danger"/>
+ </g>
+ <g id="matplotlib.axis_1">
+ <g id="xtick_1">
+ <g id="line2d_1">
+ <defs>
+ <path d="M 0 0 L 0 3.5" id="m7a9c636c50" class="m-line"/>
+ </defs>
+ <g>
+ <use x="60.154063" xlink:href="#m7a9c636c50" y="175.060625"/>
+ </g>
+ </g>
+ <g id="text_1">
+ <text class="m-label" style="text-anchor:middle;" transform="rotate(-0, 60.154063, 189.892969)" x="60.154063" y="189.892969">0</text>
+ </g>
+ </g>
+ <g id="xtick_2">
+ <g id="line2d_2">
+ <g>
+ <use x="138.290821" xlink:href="#m7a9c636c50" y="175.060625"/>
+ </g>
+ </g>
+ <g id="text_2">
+ <text class="m-label" style="text-anchor:middle;" transform="rotate(-0, 138.290821, 189.892969)" x="138.290821" y="189.892969">1</text>
+ </g>
+ </g>
+ <g id="xtick_3">
+ <g id="line2d_3">
+ <g>
+ <use x="216.427579" xlink:href="#m7a9c636c50" y="175.060625"/>
+ </g>
+ </g>
+ <g id="text_3">
+ <text class="m-label" style="text-anchor:middle;" transform="rotate(-0, 216.427579, 189.892969)" x="216.427579" y="189.892969">2</text>
+ </g>
+ </g>
+ <g id="xtick_4">
+ <g id="line2d_4">
+ <g>
+ <use x="294.564338" xlink:href="#m7a9c636c50" y="175.060625"/>
+ </g>
+ </g>
+ <g id="text_4">
+ <text class="m-label" style="text-anchor:middle;" transform="rotate(-0, 294.564338, 189.892969)" x="294.564338" y="189.892969">3</text>
+ </g>
+ </g>
+ <g id="xtick_5">
+ <g id="line2d_5">
+ <g>
+ <use x="372.701096" xlink:href="#m7a9c636c50" y="175.060625"/>
+ </g>
+ </g>
+ <g id="text_5">
+ <text class="m-label" style="text-anchor:middle;" transform="rotate(-0, 372.701096, 189.892969)" x="372.701096" y="189.892969">4</text>
+ </g>
+ </g>
+ <g id="xtick_6">
+ <g id="line2d_6">
+ <g>
+ <use x="450.837854" xlink:href="#m7a9c636c50" y="175.060625"/>
+ </g>
+ </g>
+ <g id="text_6">
+ <text class="m-label" style="text-anchor:middle;" transform="rotate(-0, 450.837854, 189.892969)" x="450.837854" y="189.892969">5</text>
+ </g>
+ </g>
+ <g id="xtick_7">
+ <g id="line2d_7">
+ <g>
+ <use x="528.974613" xlink:href="#m7a9c636c50" y="175.060625"/>
+ </g>
+ </g>
+ <g id="text_7">
+ <text class="m-label" style="text-anchor:middle;" transform="rotate(-0, 528.974613, 189.892969)" x="528.974613" y="189.892969">6</text>
+ </g>
+ </g>
+ <g id="text_8">
+ <text class="m-label" style="text-anchor:middle;" transform="rotate(-0, 310.387031, 203.980313)" x="310.387031" y="203.980313">Mondays</text>
+ </g>
+ </g>
+ <g id="matplotlib.axis_2">
+ <g id="ytick_1">
+ <g id="line2d_8">
+ <defs>
+ <path d="M 0 0 L -3.5 0" id="m03b20617ea" class="m-line"/>
+ </defs>
+ <g>
+ <use x="60.154063" xlink:href="#m03b20617ea" y="53.071172"/>
+ </g>
+ </g>
+ <g id="text_9">
+ <text class="m-label" transform="translate(15.827969 51.138094)">January</text>
+ <text class="m-label" transform="translate(53.154063 62.836594)"/>
+ </g>
+ </g>
+ <g id="ytick_2">
+ <g id="line2d_9">
+ <g>
+ <use x="60.154063" xlink:href="#m03b20617ea" y="101.098516"/>
+ </g>
+ </g>
+ <g id="text_10">
+ <text class="m-label" transform="translate(11.88 99.210125)">February</text>
+ <text class="m-label" transform="translate(53.154063 110.908625)"/>
+ </g>
+ </g>
+ <g id="ytick_3">
+ <g id="line2d_10">
+ <g>
+ <use x="60.154063" xlink:href="#m03b20617ea" y="149.125859"/>
+ </g>
+ </g>
+ <g id="text_11">
+ <text class="m-label" transform="translate(24.794688 147.215125)">March</text>
+ <text class="m-label" transform="translate(53.154063 158.868938)"/>
+ </g>
+ </g>
+ </g>
+ <g id="LineCollection_1">
+ <path clip-path="url(#p0d12d49cf2)" d="M 286.750662 53.071172 L 302.378014 53.071172" class="m-error"/>
+ <path clip-path="url(#p0d12d49cf2)" d="M 208.613903 101.098516 L 536.788289 101.098516" class="m-error"/>
+ <path clip-path="url(#p0d12d49cf2)" d="M 372.701096 149.125859 L 528.974613 149.125859" class="m-error"/>
+ </g>
+ <g id="line2d_11">
+ <defs>
+ <path d="M 0 9.375 L 0 -9.375" id="m63fa77ade3" class="m-error"/>
+ </defs>
+ <g clip-path="url(#p0d12d49cf2)">
+ <use x="286.750662" xlink:href="#m63fa77ade3" y="53.071172"/>
+ <use x="208.613903" xlink:href="#m63fa77ade3" y="101.098516"/>
+ <use x="372.701096" xlink:href="#m63fa77ade3" y="149.125859"/>
+ </g>
+ </g>
+ <g id="line2d_12">
+ <g clip-path="url(#p0d12d49cf2)">
+ <use x="302.378014" xlink:href="#m63fa77ade3" y="53.071172"/>
+ <use x="536.788289" xlink:href="#m63fa77ade3" y="101.098516"/>
+ <use x="528.974613" xlink:href="#m63fa77ade3" y="149.125859"/>
+ </g>
+ </g>
+ <g id="text_12">
+ <text class="m-label m-dim" transform="translate(53.154063 52.434305)"/>
+ <text class="m-label m-dim" transform="translate(5.522344 64.088117)">a paradise</text>
+ </g>
+ <g id="text_13">
+ <text class="m-label m-dim" transform="translate(53.154063 100.416961)"/>
+ <text class="m-label m-dim" transform="translate(31.064688 112.070773)">okay</text>
+ </g>
+ <g id="text_14">
+ <text class="m-label m-dim" transform="translate(53.154063 148.488992)"/>
+ <text class="m-label m-dim" transform="translate(32.924375 160.142805)">hell!</text>
+ </g>
+ <g id="text_15">
+ <text class="m-title" style="text-anchor:middle;" transform="rotate(-0, 310.387031, 21.136406)" x="310.387031" y="21.136406">A plot with separate colors, extra labels, error bars and custom height</text>
+ </g>
+ </g>
+ </g>
+ <defs>
+ <clipPath id="p0d12d49cf2">
+ <rect height="147.924219" width="500.465938" x="60.154063" y="27.136406"/>
+ </clipPath>
+ </defs>
+</svg>
+</div>
+<!-- /content -->
+ </div>
+ </div>
+ </div>
+</article>
+</main>
+</body>
+</html>
--- /dev/null
+m.plots
+#######
+
+.. plot:: A plot with a single color
+ :type: barh
+ :labels:
+ First
+ Second
+ :units: meters, i guess?
+ :values: 15 30
+ :colors: success
+
+.. plot:: A plot with separate colors, extra labels, error bars and custom height
+ :type: barh
+ :labels:
+ January
+ February
+ March
+ :labels_extra:
+ a paradise
+ okay
+ hell!
+ :units: Mondays
+ :values: 3 4 5
+ :errors: 0.1 2.1 1.0
+ :colors: success info danger
+ :bar_height: 0.75
--- /dev/null
+#
+# This file is part of m.css.
+#
+# Copyright © 2017, 2018 Vladimír Vondruš <mosra@centrum.cz>
+#
+# 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.
+#
+
+from . import PluginTestCase
+
+class Plots(PluginTestCase):
+ def __init__(self, *args, **kwargs):
+ super().__init__(__file__, '', *args, **kwargs)
+
+ def test(self):
+ self.run_pelican({
+ 'PLUGINS': ['m.htmlsanity', 'm.plots']
+ })
+
+ self.assertEqual(*self.actual_expected_contents('page.html'))
('Images', 'plugins/images/', 'plugins/images'),
('Math and code', 'plugins/math-and-code/', 'plugins/math-and-code'),
('Links and other', 'plugins/links/', 'plugins/links'),
+ ('Plots', 'plugins/plots/', 'plugins/plots'),
('Metadata', 'plugins/metadata/', 'plugins/metadata')]),
('Doxygen theme', 'doxygen/', 'doxygen', []),
('GitHub', 'https://github.com/mosra/m.css', '', [])]
('Components', 'plugins/components/'),
('Images', 'plugins/images/'),
('Math and code', 'plugins/math-and-code/'),
+ ('Plots', 'plugins/plots/'),
('Links and other', 'plugins/links/'),
('Metadata', 'plugins/metadata/')]
'm.htmlsanity',
'm.images',
'm.math',
- 'm.metadata']
+ 'm.metadata',
+ 'm.plots']
THEME = '../pelican-theme'
THEME_STATIC_DIR = 'static'