simplecharts

SVG charts without dependencies
git clone https://git.ce9e.org/simplecharts.git

commit
cdf2328f7f714f45fbe09455c67015a361e1713c
parent
5ca041793569e0eaf85c83586a31775c80722a0b
Author
Tobias Bengfort <bengfort@mpib-berlin.mpg.de>
Date
2025-04-24 16:15
break legend into multiple rows if necessary

Diffstat

M simplecharts.py 69 +++++++++++++++++++++++++++++++++++++------------------------
M tests/long_ColumnRenderer.svg 22 +++++++++++-----------
M tests/long_LineRenderer.svg 22 +++++++++++-----------
M tests/long_StackedAreaRenderer.svg 22 +++++++++++-----------
M tests/long_StackedColumnRenderer.svg 22 +++++++++++-----------

5 files changed, 86 insertions, 71 deletions


diff --git a/simplecharts.py b/simplecharts.py

@@ -114,35 +114,50 @@ class BaseRenderer:
  114   114         return s
  115   115 
  116   116     def render_legend(self, legend):
  117    -1         s = ''
  118    -1 
  119    -1         width = 2 * self.char_padding - self.char_width
   -1   117         rows = [[]]
   -1   118         width = 0
   -1   119         max_width = 0
  120   120         for label in legend:
  121    -1             width += self.char_width + self.char_padding  # square
  122    -1             width += len(label) * self.char_width  # text
  123    -1             width += self.char_width  # margin
  124    -1 
  125    -1         x = self.width - width
   -1   121             w = (
   -1   122                 self.char_width + self.char_padding  # square
   -1   123                 + len(label) * self.char_width  # text
   -1   124                 + self.char_width  # margin
   -1   125             )
   -1   126             if width + w > self.width:
   -1   127                 rows.append([])
   -1   128                 width = 0
   -1   129             rows[-1].append(label)
   -1   130             width += w
   -1   131             max_width = max(width, max_width)
   -1   132 
   -1   133         max_width += 2 * self.char_padding - self.char_width
   -1   134 
   -1   135         s = self.rect(
   -1   136             self.width - max_width,
   -1   137             -self.y_legend,
   -1   138             max_width,
   -1   139             self.y_legend * len(rows),
   -1   140             fill='none',
   -1   141             stroke=self.ui_color,
   -1   142         )
  126   143 
  127    -1         s += self.rect(x, -self.y_legend, width, self.y_legend, **{
  128    -1             'fill': 'none',
  129    -1             'stroke': self.ui_color,
  130    -1         })
  131    -1         x += self.char_padding
  132    -1 
  133    -1         y = -self.y_legend / 2
  134    -1         for i, label in enumerate(legend):
  135    -1             # square
  136    -1             size = self.char_width
  137    -1             s += self.rect(x, y - size / 2, size, size, fill=self.get_color(i))
  138    -1             x += self.char_width + self.char_padding
  139    -1             # text
  140    -1             s += self.text(label, x, y, **{
  141    -1                 'dominant-baseline': 'middle',
  142    -1             })
  143    -1             x += len(label) * self.char_width
  144    -1             # margin
  145    -1             x += self.char_width
   -1   144         i = 0
   -1   145         for j, row in enumerate(rows):
   -1   146             x = self.width - max_width + self.char_padding
   -1   147             y = (j - 0.5) * self.y_legend
   -1   148             for label in row:
   -1   149                 # square
   -1   150                 size = self.char_width
   -1   151                 s += self.rect(x, y - size / 2, size, size, fill=self.get_color(i))
   -1   152                 x += self.char_width + self.char_padding
   -1   153                 # text
   -1   154                 s += self.text(label, x, y, **{
   -1   155                     'dominant-baseline': 'middle',
   -1   156                 })
   -1   157                 x += len(label) * self.char_width
   -1   158                 # margin
   -1   159                 x += self.char_width
   -1   160                 i += 1
  146   161 
  147   162         return self.element('g', s, **{
  148   163             'aria-hidden': 'true',

diff --git a/tests/long_ColumnRenderer.svg b/tests/long_ColumnRenderer.svg

@@ -14,17 +14,17 @@
   14    14 			<text dominant-baseline="middle" fill="#333" role="columnheader" text-anchor="middle" x="560.0" y="490.0">Bananas</text>
   15    15 		</g>
   16    16 		<g aria-hidden="true">
   17    -1 			<rect fill="none" height="20" stroke="#333" width="848" x="-208" y="-20" />
   18    -1 			<rect fill="#e41a1c" height="10" width="10" x="-204" y="-15.0" />
   19    -1 			<text dominant-baseline="middle" fill="#333" x="-190" y="-10.0">Monozygotic (431)</text>
   20    -1 			<rect fill="#377eb8" height="10" width="10" x="-10" y="-15.0" />
   21    -1 			<text dominant-baseline="middle" fill="#333" x="4" y="-10.0">Dizygotic (464)</text>
   22    -1 			<rect fill="#4daf4a" height="10" width="10" x="164" y="-15.0" />
   23    -1 			<text dominant-baseline="middle" fill="#333" x="178" y="-10.0">Trizygotic (0)</text>
   24    -1 			<rect fill="#984ea3" height="10" width="10" x="328" y="-15.0" />
   25    -1 			<text dominant-baseline="middle" fill="#333" x="342" y="-10.0">Quadrizygotic (0)</text>
   26    -1 			<rect fill="#ff7f00" height="10" width="10" x="522" y="-15.0" />
   27    -1 			<text dominant-baseline="middle" fill="#333" x="536" y="-10.0">Other (82)</text>
   -1    17 			<rect fill="none" height="40" stroke="#333" width="530" x="110" y="-20" />
   -1    18 			<rect fill="#e41a1c" height="10" width="10" x="114" y="-15.0" />
   -1    19 			<text dominant-baseline="middle" fill="#333" x="128" y="-10.0">Monozygotic (431)</text>
   -1    20 			<rect fill="#377eb8" height="10" width="10" x="308" y="-15.0" />
   -1    21 			<text dominant-baseline="middle" fill="#333" x="322" y="-10.0">Dizygotic (464)</text>
   -1    22 			<rect fill="#4daf4a" height="10" width="10" x="482" y="-15.0" />
   -1    23 			<text dominant-baseline="middle" fill="#333" x="496" y="-10.0">Trizygotic (0)</text>
   -1    24 			<rect fill="#984ea3" height="10" width="10" x="114" y="5.0" />
   -1    25 			<text dominant-baseline="middle" fill="#333" x="128" y="10.0">Quadrizygotic (0)</text>
   -1    26 			<rect fill="#ff7f00" height="10" width="10" x="308" y="5.0" />
   -1    27 			<text dominant-baseline="middle" fill="#333" x="322" y="10.0">Other (82)</text>
   28    28 		</g>
   29    29 		<g fill="#e41a1c" role="row" stroke="white">
   30    30 			<rect height="180.0" role="cell" width="22.857142857142858" x="22.857142857142858" y="299.0">

diff --git a/tests/long_LineRenderer.svg b/tests/long_LineRenderer.svg

@@ -14,17 +14,17 @@
   14    14 			<text dominant-baseline="middle" fill="#333" role="columnheader" text-anchor="middle" x="560.0" y="490.0">Bananas</text>
   15    15 		</g>
   16    16 		<g aria-hidden="true">
   17    -1 			<rect fill="none" height="20" stroke="#333" width="848" x="-208" y="-20" />
   18    -1 			<rect fill="#e41a1c" height="10" width="10" x="-204" y="-15.0" />
   19    -1 			<text dominant-baseline="middle" fill="#333" x="-190" y="-10.0">Monozygotic (431)</text>
   20    -1 			<rect fill="#377eb8" height="10" width="10" x="-10" y="-15.0" />
   21    -1 			<text dominant-baseline="middle" fill="#333" x="4" y="-10.0">Dizygotic (464)</text>
   22    -1 			<rect fill="#4daf4a" height="10" width="10" x="164" y="-15.0" />
   23    -1 			<text dominant-baseline="middle" fill="#333" x="178" y="-10.0">Trizygotic (0)</text>
   24    -1 			<rect fill="#984ea3" height="10" width="10" x="328" y="-15.0" />
   25    -1 			<text dominant-baseline="middle" fill="#333" x="342" y="-10.0">Quadrizygotic (0)</text>
   26    -1 			<rect fill="#ff7f00" height="10" width="10" x="522" y="-15.0" />
   27    -1 			<text dominant-baseline="middle" fill="#333" x="536" y="-10.0">Other (82)</text>
   -1    17 			<rect fill="none" height="40" stroke="#333" width="530" x="110" y="-20" />
   -1    18 			<rect fill="#e41a1c" height="10" width="10" x="114" y="-15.0" />
   -1    19 			<text dominant-baseline="middle" fill="#333" x="128" y="-10.0">Monozygotic (431)</text>
   -1    20 			<rect fill="#377eb8" height="10" width="10" x="308" y="-15.0" />
   -1    21 			<text dominant-baseline="middle" fill="#333" x="322" y="-10.0">Dizygotic (464)</text>
   -1    22 			<rect fill="#4daf4a" height="10" width="10" x="482" y="-15.0" />
   -1    23 			<text dominant-baseline="middle" fill="#333" x="496" y="-10.0">Trizygotic (0)</text>
   -1    24 			<rect fill="#984ea3" height="10" width="10" x="114" y="5.0" />
   -1    25 			<text dominant-baseline="middle" fill="#333" x="128" y="10.0">Quadrizygotic (0)</text>
   -1    26 			<rect fill="#ff7f00" height="10" width="10" x="308" y="5.0" />
   -1    27 			<text dominant-baseline="middle" fill="#333" x="322" y="10.0">Other (82)</text>
   28    28 		</g>
   29    29 		<polyline fill="none" points="80,300 240,240 400,240 560,180" stroke="#e41a1c" />
   30    30 		<polyline fill="none" points="80,360 240,360 400,300 560,420" stroke="#377eb8" />

diff --git a/tests/long_StackedAreaRenderer.svg b/tests/long_StackedAreaRenderer.svg

@@ -14,17 +14,17 @@
   14    14 			<text dominant-baseline="middle" fill="#333" role="columnheader" text-anchor="middle" x="560.0" y="490.0">Bananas</text>
   15    15 		</g>
   16    16 		<g aria-hidden="true">
   17    -1 			<rect fill="none" height="20" stroke="#333" width="848" x="-208" y="-20" />
   18    -1 			<rect fill="#e41a1c" height="10" width="10" x="-204" y="-15.0" />
   19    -1 			<text dominant-baseline="middle" fill="#333" x="-190" y="-10.0">Monozygotic (431)</text>
   20    -1 			<rect fill="#377eb8" height="10" width="10" x="-10" y="-15.0" />
   21    -1 			<text dominant-baseline="middle" fill="#333" x="4" y="-10.0">Dizygotic (464)</text>
   22    -1 			<rect fill="#4daf4a" height="10" width="10" x="164" y="-15.0" />
   23    -1 			<text dominant-baseline="middle" fill="#333" x="178" y="-10.0">Trizygotic (0)</text>
   24    -1 			<rect fill="#984ea3" height="10" width="10" x="328" y="-15.0" />
   25    -1 			<text dominant-baseline="middle" fill="#333" x="342" y="-10.0">Quadrizygotic (0)</text>
   26    -1 			<rect fill="#ff7f00" height="10" width="10" x="522" y="-15.0" />
   27    -1 			<text dominant-baseline="middle" fill="#333" x="536" y="-10.0">Other (82)</text>
   -1    17 			<rect fill="none" height="40" stroke="#333" width="530" x="110" y="-20" />
   -1    18 			<rect fill="#e41a1c" height="10" width="10" x="114" y="-15.0" />
   -1    19 			<text dominant-baseline="middle" fill="#333" x="128" y="-10.0">Monozygotic (431)</text>
   -1    20 			<rect fill="#377eb8" height="10" width="10" x="308" y="-15.0" />
   -1    21 			<text dominant-baseline="middle" fill="#333" x="322" y="-10.0">Dizygotic (464)</text>
   -1    22 			<rect fill="#4daf4a" height="10" width="10" x="482" y="-15.0" />
   -1    23 			<text dominant-baseline="middle" fill="#333" x="496" y="-10.0">Trizygotic (0)</text>
   -1    24 			<rect fill="#984ea3" height="10" width="10" x="114" y="5.0" />
   -1    25 			<text dominant-baseline="middle" fill="#333" x="128" y="10.0">Quadrizygotic (0)</text>
   -1    26 			<rect fill="#ff7f00" height="10" width="10" x="308" y="5.0" />
   -1    27 			<text dominant-baseline="middle" fill="#333" x="322" y="10.0">Other (82)</text>
   28    28 		</g>
   29    29 		<polyline fill="#e41a1c" points="80,443 240,431 400,431 560,419 560,479 400,479 240,479 80,479" stroke="white" />
   30    30 		<polyline fill="#377eb8" points="80,419 240,407 400,395 560,407 560,419 400,431 240,431 80,443" stroke="white" />

diff --git a/tests/long_StackedColumnRenderer.svg b/tests/long_StackedColumnRenderer.svg

@@ -14,17 +14,17 @@
   14    14 			<text dominant-baseline="middle" fill="#333" role="columnheader" text-anchor="middle" x="560.0" y="490.0">Bananas</text>
   15    15 		</g>
   16    16 		<g aria-hidden="true">
   17    -1 			<rect fill="none" height="20" stroke="#333" width="848" x="-208" y="-20" />
   18    -1 			<rect fill="#e41a1c" height="10" width="10" x="-204" y="-15.0" />
   19    -1 			<text dominant-baseline="middle" fill="#333" x="-190" y="-10.0">Monozygotic (431)</text>
   20    -1 			<rect fill="#377eb8" height="10" width="10" x="-10" y="-15.0" />
   21    -1 			<text dominant-baseline="middle" fill="#333" x="4" y="-10.0">Dizygotic (464)</text>
   22    -1 			<rect fill="#4daf4a" height="10" width="10" x="164" y="-15.0" />
   23    -1 			<text dominant-baseline="middle" fill="#333" x="178" y="-10.0">Trizygotic (0)</text>
   24    -1 			<rect fill="#984ea3" height="10" width="10" x="328" y="-15.0" />
   25    -1 			<text dominant-baseline="middle" fill="#333" x="342" y="-10.0">Quadrizygotic (0)</text>
   26    -1 			<rect fill="#ff7f00" height="10" width="10" x="522" y="-15.0" />
   27    -1 			<text dominant-baseline="middle" fill="#333" x="536" y="-10.0">Other (82)</text>
   -1    17 			<rect fill="none" height="40" stroke="#333" width="530" x="110" y="-20" />
   -1    18 			<rect fill="#e41a1c" height="10" width="10" x="114" y="-15.0" />
   -1    19 			<text dominant-baseline="middle" fill="#333" x="128" y="-10.0">Monozygotic (431)</text>
   -1    20 			<rect fill="#377eb8" height="10" width="10" x="308" y="-15.0" />
   -1    21 			<text dominant-baseline="middle" fill="#333" x="322" y="-10.0">Dizygotic (464)</text>
   -1    22 			<rect fill="#4daf4a" height="10" width="10" x="482" y="-15.0" />
   -1    23 			<text dominant-baseline="middle" fill="#333" x="496" y="-10.0">Trizygotic (0)</text>
   -1    24 			<rect fill="#984ea3" height="10" width="10" x="114" y="5.0" />
   -1    25 			<text dominant-baseline="middle" fill="#333" x="128" y="10.0">Quadrizygotic (0)</text>
   -1    26 			<rect fill="#ff7f00" height="10" width="10" x="308" y="5.0" />
   -1    27 			<text dominant-baseline="middle" fill="#333" x="322" y="10.0">Other (82)</text>
   28    28 		</g>
   29    29 		<g fill="#e41a1c" role="row" stroke="white">
   30    30 			<rect height="36.0" role="cell" width="53.333333333333336" x="53.33333333333333" y="443.0">