kaxe 1.4.2__tar.gz → 1.4.4.dev2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {kaxe-1.4.2/src/kaxe.egg-info → kaxe-1.4.4.dev2}/PKG-INFO +1 -1
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/pyproject.toml +1 -1
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/core/legend.py +33 -17
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/core/shapes.py +3 -1
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/core/svg.py +54 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/core/window.py +1 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/plot/d3/plot3d.py +2 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/plot/grid.py +180 -62
- {kaxe-1.4.2 → kaxe-1.4.4.dev2/src/kaxe.egg-info}/PKG-INFO +1 -1
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/LICENSE +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/MANIFEST.in +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/README.md +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/setup.cfg +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/__init__.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/chart/__init__.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/chart/bar.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/chart/box.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/chart/pie.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/chart/qqplot.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/core/__init__.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/core/axis.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/core/color.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/core/d3/backend.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/core/d3/camera.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/core/d3/helper.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/core/d3/hud.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/core/d3/objects/__init__.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/core/d3/objects/color.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/core/d3/objects/line.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/core/d3/objects/point.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/core/d3/objects/pointer.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/core/d3/objects/triangle.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/core/d3/openglrender.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/core/d3/translator.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/core/draw.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/core/fileloader.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/core/helper.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/core/line.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/core/marker.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/core/profiler.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/core/round.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/core/styles.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/core/symbol.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/core/text.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/data/__init__.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/data/excel.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/objects/__init__.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/objects/_lazy.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/objects/d2/__init__.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/objects/d2/arrow.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/objects/d2/bubble.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/objects/d2/contour.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/objects/d2/equation.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/objects/d2/fill.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/objects/d2/function.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/objects/d2/map.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/objects/d2/parameter.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/objects/d2/pillar.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/objects/d2/point.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/objects/d3/__init__.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/objects/d3/base.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/objects/d3/function.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/objects/d3/mesh.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/objects/d3/point.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/objects/d3/potato.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/objects/function.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/objects/legend.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/objects/mapdata.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/objects/point.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/objects/text.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/plot/__init__.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/plot/_lazy.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/plot/box.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/plot/constants.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/plot/d3/__init__.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/plot/d3/axes.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/plot/d3/geometry.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/plot/d3/variants.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/plot/double.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/plot/empty.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/plot/log.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/plot/polar.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/plot/standard.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/plot/themes.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/plot/zoom.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/plot/zoom_connector.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/__init__.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/__init__.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.bright-oblique.ttf +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.bright-roman.ttf +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.bright-semibold.ttf +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.bright-semiboldoblique.ttf +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.classical-serif-italic.ttf +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.concrete-bold.ttf +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.concrete-bolditalic.ttf +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.concrete-italic.ttf +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.concrete-roman.ttf +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.sans-serif-bold.ttf +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.sans-serif-boldoblique.ttf +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.sans-serif-demi-condensed-demicondensed.ttf +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.sans-serif-medium.ttf +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.sans-serif-oblique.ttf +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.serif-bold.ttf +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.serif-bolditalic.ttf +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.serif-extra-boldslanted.ttf +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.serif-extra-romanslanted.ttf +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.serif-italic.ttf +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.serif-roman.ttf +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.serif-upright-italic-uprightitalic.ttf +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-bold.ttf +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-bolditalic.ttf +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-italic.ttf +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-light.ttf +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-lightoblique.ttf +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-regular.ttf +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-variable-width-italic.ttf +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-variable-width-medium.ttf +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/readme.txt +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/logo-small.png +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/symbolcross.png +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/symboldonut.png +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/symbollollipop.png +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/symboltriangle.png +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe.egg-info/SOURCES.txt +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe.egg-info/dependency_links.txt +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe.egg-info/requires.txt +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe.egg-info/top_level.txt +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/tests/test.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/tests/test2.py +0 -0
- {kaxe-1.4.2 → kaxe-1.4.4.dev2}/tests/test3.py +0 -0
|
@@ -3,6 +3,7 @@ from .symbol import makeSymbolShapes, symbol
|
|
|
3
3
|
from .text import Text
|
|
4
4
|
from .shapes import shapes
|
|
5
5
|
from .styles import AttrObject, ComputedAttribute
|
|
6
|
+
from .svg import SvgDocument
|
|
6
7
|
import math
|
|
7
8
|
from types import MappingProxyType
|
|
8
9
|
from PIL import Image
|
|
@@ -46,12 +47,12 @@ class LegendBox(AttrObject):
|
|
|
46
47
|
self.objects = objects
|
|
47
48
|
|
|
48
49
|
|
|
49
|
-
def finalize(self, parent, sneaky=False): # maybe add as a seperate object
|
|
50
|
+
def finalize(self, parent, sneaky=False, vector=False): # maybe add as a seperate object
|
|
50
51
|
"""
|
|
51
52
|
stadigvæk lidt skrøbelig
|
|
52
53
|
|
|
53
54
|
if sneaky is True the method will not do anything to parent
|
|
54
|
-
The function will also just return an image of the legendbox
|
|
55
|
+
The function will also just return an image of the legendbox (or SvgDocument when vector=True)
|
|
55
56
|
|
|
56
57
|
"""
|
|
57
58
|
self.setAttrMap(parent.attrmap)
|
|
@@ -90,6 +91,7 @@ class LegendBox(AttrObject):
|
|
|
90
91
|
legendGridSpacing = self.getAttr('gaps')
|
|
91
92
|
legendSymbolTextSpacing = self.getAttr('symbolTextSpacing')
|
|
92
93
|
legendGap = self.getAttr('topMargin')
|
|
94
|
+
legendPadding = max(1, fontSize // 16)
|
|
93
95
|
|
|
94
96
|
# legendSizeThickness = 2 #NOTE: STYLE
|
|
95
97
|
# legendPadding = (5, 5, 5, 5) # NOTE: STYLE, left bottom right top
|
|
@@ -106,19 +108,19 @@ class LegendBox(AttrObject):
|
|
|
106
108
|
width = symbolSize[0] + text.width + legendGridSpacing[0] + legendSymbolTextSpacing
|
|
107
109
|
height = max(symbolSize[1], text.height)
|
|
108
110
|
|
|
109
|
-
currentLineWidth
|
|
110
|
-
# hvis bredden er for meget så gå en linje ned
|
|
111
|
-
if currentLineWidth > legendMaxWidth:
|
|
111
|
+
if currentLineWidth > 0 and currentLineWidth + width > legendMaxWidth:
|
|
112
112
|
grid.append({"elements":[], "height":0})
|
|
113
113
|
currentLineWidth = 0
|
|
114
|
-
|
|
114
|
+
|
|
115
|
+
currentLineWidth += width
|
|
115
116
|
grid[-1]["elements"].append((symbol, text, width, height))
|
|
116
117
|
grid[-1]["height"] = max(grid[-1]["height"], height)
|
|
117
118
|
|
|
119
|
+
grid = [row for row in grid if row["elements"]]
|
|
118
120
|
grid.reverse()
|
|
119
121
|
|
|
120
122
|
# create legends
|
|
121
|
-
currentLinePos = [
|
|
123
|
+
currentLinePos = [legendPadding, legendPadding]
|
|
122
124
|
maxPos = [0, 0]
|
|
123
125
|
minPos = [math.inf, math.inf]
|
|
124
126
|
for row in grid:
|
|
@@ -132,23 +134,24 @@ class LegendBox(AttrObject):
|
|
|
132
134
|
|
|
133
135
|
currentLinePos[0] += width
|
|
134
136
|
row_mid = y + row["height"] / 2
|
|
135
|
-
symbol.x = x
|
|
136
137
|
if isinstance(symbol, shapes.Circle):
|
|
138
|
+
symbol.x = x + symbolSize[0] / 2
|
|
137
139
|
symbol.centerAlign()
|
|
138
140
|
symbol.y = row_mid
|
|
139
|
-
sym_min_x =
|
|
140
|
-
sym_max_x =
|
|
141
|
+
sym_min_x = x
|
|
142
|
+
sym_max_x = x + symbolSize[0]
|
|
141
143
|
sym_min_y = symbol.y - symbolSize[1] / 2
|
|
142
144
|
sym_max_y = symbol.y + symbolSize[1] / 2
|
|
143
145
|
else:
|
|
146
|
+
symbol.x = x
|
|
144
147
|
symbol.y = row_mid - symbolSize[1] / 2
|
|
145
148
|
sym_min_x = symbol.x
|
|
146
149
|
sym_max_x = symbol.x + symbolSize[0]
|
|
147
150
|
sym_min_y = symbol.y
|
|
148
151
|
sym_max_y = symbol.y + symbolSize[1]
|
|
149
152
|
text.setLeftTopPos(
|
|
150
|
-
x + symbolSize[0] + legendSymbolTextSpacing,
|
|
151
|
-
y +
|
|
153
|
+
x + symbolSize[0] + legendSymbolTextSpacing,
|
|
154
|
+
y + row["height"] / 2 + text.height / 2,
|
|
152
155
|
)
|
|
153
156
|
|
|
154
157
|
if debug:
|
|
@@ -166,8 +169,13 @@ class LegendBox(AttrObject):
|
|
|
166
169
|
|
|
167
170
|
if debug: shapes.Circle(0,0, 10, batch=self.batch)
|
|
168
171
|
|
|
169
|
-
|
|
170
|
-
|
|
172
|
+
minPos[0] -= legendPadding
|
|
173
|
+
minPos[1] -= legendPadding
|
|
174
|
+
maxPos[0] += legendPadding
|
|
175
|
+
maxPos[1] += legendPadding
|
|
176
|
+
|
|
177
|
+
height = math.ceil(maxPos[1] - minPos[1])
|
|
178
|
+
width = math.ceil(maxPos[0] - minPos[0])
|
|
171
179
|
|
|
172
180
|
### skub den ned til window 0,0 (her burde title være med)
|
|
173
181
|
# self.batch.push(-minPos[0], -minPos[1])
|
|
@@ -192,11 +200,19 @@ class LegendBox(AttrObject):
|
|
|
192
200
|
# tilføj plads til legenden
|
|
193
201
|
parent.addPaddingCondition(bottom=height+legendGap)
|
|
194
202
|
|
|
195
|
-
else:
|
|
203
|
+
else:
|
|
196
204
|
self.batch.push(-minPos[0], -minPos[1])
|
|
197
205
|
self.boxshape.push(-minPos[0], -minPos[1])
|
|
206
|
+
if vector:
|
|
207
|
+
doc = SvgDocument((int(width), int(height)))
|
|
208
|
+
self.boxshape.draw(doc)
|
|
209
|
+
self.batch.draw(doc)
|
|
210
|
+
return doc
|
|
198
211
|
surface = Image.new('RGBA', (int(width), int(height)), (0,0,0,0))
|
|
199
|
-
|
|
200
212
|
self.boxshape.draw(surface)
|
|
201
213
|
self.batch.draw(surface)
|
|
202
|
-
return surface
|
|
214
|
+
return surface
|
|
215
|
+
|
|
216
|
+
def finalize_svg_sneaky(self, parent):
|
|
217
|
+
"""Return an SvgDocument sized to the legend for grid SVG export."""
|
|
218
|
+
return self.finalize(parent, sneaky=True, vector=True)
|
|
@@ -276,7 +276,9 @@ class Circle(Shape):
|
|
|
276
276
|
doubleradius = int(2*self.radius)
|
|
277
277
|
circle = Image.new("RGBA", (doubleradius, doubleradius), (0, 0, 0, 0))
|
|
278
278
|
draw = ImageDraw.Draw(circle)
|
|
279
|
-
|
|
279
|
+
# PIL treats the bbox as inclusive on both ends; shrink by half a pixel so
|
|
280
|
+
# the rendered circle is symmetric and not clipped at the bottom edge.
|
|
281
|
+
pos = (-0.5, -0.5, 2 * self.radius - 0.5, 2 * self.radius - 0.5)
|
|
280
282
|
if self.fill:
|
|
281
283
|
draw.ellipse(pos, fill=self.color)
|
|
282
284
|
else:
|
|
@@ -368,6 +368,60 @@ class SvgDocument:
|
|
|
368
368
|
return '<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n' + body
|
|
369
369
|
|
|
370
370
|
|
|
371
|
+
def parse_svg_root(xml: str) -> ET.Element:
|
|
372
|
+
"""Parse SVG XML and return the root element."""
|
|
373
|
+
if xml.startswith("<?"):
|
|
374
|
+
xml = xml.split("\n", 1)[1]
|
|
375
|
+
root = ET.fromstring(xml)
|
|
376
|
+
if not (root.tag.endswith("svg") or root.tag == "svg"):
|
|
377
|
+
raise ValueError("Expected SVG root element")
|
|
378
|
+
return root
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
def extract_svg_children(root: ET.Element) -> tuple[list[ET.Element], Optional[str]]:
|
|
382
|
+
"""Return drawable children (excluding defs) and optional fondi style text."""
|
|
383
|
+
fondi_css: Optional[str] = None
|
|
384
|
+
children: list[ET.Element] = []
|
|
385
|
+
for child in root:
|
|
386
|
+
local = child.tag.split("}")[-1] if "}" in child.tag else child.tag
|
|
387
|
+
if local == "defs":
|
|
388
|
+
for sub in child:
|
|
389
|
+
sub_local = sub.tag.split("}")[-1] if "}" in sub.tag else sub.tag
|
|
390
|
+
if sub_local == "style" and sub.text:
|
|
391
|
+
fondi_css = sub.text
|
|
392
|
+
continue
|
|
393
|
+
children.append(child)
|
|
394
|
+
return children, fondi_css
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
def embed_svg_children(
|
|
398
|
+
doc: SvgDocument,
|
|
399
|
+
children: list[ET.Element],
|
|
400
|
+
tx: float,
|
|
401
|
+
ty: float,
|
|
402
|
+
) -> None:
|
|
403
|
+
"""Embed copied SVG children at top-left offset (SVG y-down coordinates)."""
|
|
404
|
+
if not children:
|
|
405
|
+
return
|
|
406
|
+
group = ET.Element(
|
|
407
|
+
f"{{{SVG_NS}}}g",
|
|
408
|
+
{"transform": f"translate({round(tx, 3)},{round(ty, 3)})"},
|
|
409
|
+
)
|
|
410
|
+
for child in children:
|
|
411
|
+
group.append(copy.deepcopy(child))
|
|
412
|
+
doc._elements.append(group)
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
def merge_fondi_css(doc: SvgDocument, *style_texts: Optional[str]) -> None:
|
|
416
|
+
"""Set fondi font CSS on doc once (skip duplicates and empty)."""
|
|
417
|
+
if doc._fondi_font_css is not None:
|
|
418
|
+
return
|
|
419
|
+
for text in style_texts:
|
|
420
|
+
if text:
|
|
421
|
+
doc._fondi_font_css = text
|
|
422
|
+
return
|
|
423
|
+
|
|
424
|
+
|
|
371
425
|
def is_file_path(fname: Any) -> bool:
|
|
372
426
|
"""Return True when fname is a filesystem path (str or os.PathLike)."""
|
|
373
427
|
return isinstance(fname, (str, os.PathLike))
|
|
@@ -1,12 +1,28 @@
|
|
|
1
1
|
|
|
2
2
|
from io import BytesIO
|
|
3
|
+
from typing import Optional, Union
|
|
3
4
|
from ..core.styles import AttrObject, AttrMap
|
|
4
5
|
from ..core.window import *
|
|
5
6
|
from ..core.legend import LegendBox
|
|
6
|
-
from
|
|
7
|
+
from ..core.svg import (
|
|
8
|
+
SvgDocument,
|
|
9
|
+
infer_format,
|
|
10
|
+
is_file_path,
|
|
11
|
+
parse_svg_root,
|
|
12
|
+
extract_svg_children,
|
|
13
|
+
embed_svg_children,
|
|
14
|
+
merge_fondi_css,
|
|
15
|
+
)
|
|
7
16
|
from PIL import Image
|
|
8
17
|
from .constants import XYZPLOT
|
|
9
18
|
|
|
19
|
+
|
|
20
|
+
def _cell_supports_svg(plot) -> bool:
|
|
21
|
+
if plot == XYZPLOT or isinstance(plot, str):
|
|
22
|
+
return False
|
|
23
|
+
return getattr(plot, "supports_vector_export", False)
|
|
24
|
+
|
|
25
|
+
|
|
10
26
|
class Grid(AttrObject):
|
|
11
27
|
"""
|
|
12
28
|
Assemble multiple plots in one image.
|
|
@@ -19,6 +35,7 @@ class Grid(AttrObject):
|
|
|
19
35
|
>>> grid.addColumn(plt3, plt4)
|
|
20
36
|
>>> grid.show()
|
|
21
37
|
>>> grid.save('fname.png')
|
|
38
|
+
>>> grid.save('fname.svg') # 2D vector; 3D cells embed as raster
|
|
22
39
|
|
|
23
40
|
"""
|
|
24
41
|
|
|
@@ -47,6 +64,7 @@ class Grid(AttrObject):
|
|
|
47
64
|
self.padding = [0,0,0,0]
|
|
48
65
|
|
|
49
66
|
self.__bakedImage__ = False
|
|
67
|
+
self.__bakedSvg__ = None
|
|
50
68
|
self.laterDraws = []
|
|
51
69
|
|
|
52
70
|
|
|
@@ -76,39 +94,27 @@ class Grid(AttrObject):
|
|
|
76
94
|
self.__legends = legends
|
|
77
95
|
|
|
78
96
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
"""
|
|
82
|
-
only supports pillow images
|
|
83
|
-
|
|
84
|
-
TODO: Clean up
|
|
85
|
-
"""
|
|
86
|
-
|
|
97
|
+
def _prepare_cells(self, vector: bool = False):
|
|
98
|
+
"""Style cells, export to memory, and compute composite layout."""
|
|
87
99
|
grid = self.grid
|
|
88
100
|
gridSize = ((max([len(i) for i in grid])), len(grid))
|
|
89
101
|
self.width, self.height = self.getAttr('width'), self.getAttr('height')
|
|
90
102
|
self.outerPadding = self.getAttr('outerPadding')
|
|
91
103
|
|
|
92
|
-
|
|
93
|
-
cellWidth, cellHeight = self.width//gridSize[0], self.height//gridSize[1]
|
|
104
|
+
cellWidth, cellHeight = self.width // gridSize[0], self.height // gridSize[1]
|
|
94
105
|
|
|
95
|
-
# calculated values
|
|
96
106
|
height = 0
|
|
97
107
|
leftpadding = 0
|
|
98
108
|
rightpadding = 0
|
|
99
109
|
toppadding = 0
|
|
100
110
|
bottompadding = 0
|
|
101
111
|
gapcol = 0
|
|
102
|
-
|
|
103
|
-
# add styles to window
|
|
104
|
-
# and calculate sizes
|
|
105
112
|
maxWidth = 0
|
|
113
|
+
|
|
106
114
|
for row in grid:
|
|
107
|
-
|
|
108
115
|
maxHeight = 0
|
|
109
|
-
|
|
116
|
+
|
|
110
117
|
for colNum, plot in enumerate(row):
|
|
111
|
-
|
|
112
118
|
plot.style(width=cellWidth, height=cellHeight)
|
|
113
119
|
plot.style(outerPadding=self.outerPadding)
|
|
114
120
|
|
|
@@ -119,86 +125,177 @@ class Grid(AttrObject):
|
|
|
119
125
|
if plot == XYZPLOT:
|
|
120
126
|
plot.forceWidthHeight = True
|
|
121
127
|
|
|
122
|
-
plot
|
|
128
|
+
if vector and _cell_supports_svg(plot):
|
|
129
|
+
plot.save(memfile, format="svg")
|
|
130
|
+
else:
|
|
131
|
+
plot.save(memfile)
|
|
132
|
+
|
|
123
133
|
plot.__ioBytes = memfile
|
|
134
|
+
memfile.seek(0)
|
|
124
135
|
|
|
125
136
|
w, h = plot.getSize()
|
|
126
137
|
maxWidth = max(w, maxWidth)
|
|
127
|
-
|
|
128
138
|
maxHeight = max(h, maxHeight)
|
|
129
|
-
|
|
130
|
-
# calculate paddings
|
|
139
|
+
|
|
131
140
|
if colNum == 0:
|
|
132
141
|
leftpadding = max(leftpadding, plot.padding[0])
|
|
133
142
|
bottompadding = max(bottompadding, plot.padding[1])
|
|
134
|
-
|
|
135
|
-
# calculate gaps
|
|
136
|
-
else: # "låner"/genbruger lige else her
|
|
137
|
-
|
|
138
|
-
# Da den næste ikke er lavet bruges den forrige
|
|
143
|
+
else:
|
|
139
144
|
gapcol = max(gapcol, plot.padding[0] + row[colNum - 1].padding[2])
|
|
140
145
|
|
|
141
|
-
if colNum == len(row)-1:
|
|
146
|
+
if colNum == len(row) - 1:
|
|
142
147
|
rightpadding = max(rightpadding, plot.padding[2])
|
|
143
148
|
toppadding = max(toppadding, plot.padding[3])
|
|
144
149
|
|
|
145
150
|
height += maxHeight
|
|
146
|
-
|
|
151
|
+
|
|
147
152
|
largetsRowNumber = max(len(i) for i in grid)
|
|
148
|
-
width =
|
|
153
|
+
width = (
|
|
154
|
+
gapcol * (largetsRowNumber - 1)
|
|
155
|
+
+ largetsRowNumber * cellWidth
|
|
156
|
+
+ leftpadding
|
|
157
|
+
+ rightpadding
|
|
158
|
+
)
|
|
149
159
|
|
|
150
160
|
size = (
|
|
151
161
|
width + self.outerPadding[0] + self.outerPadding[2],
|
|
152
|
-
height + self.outerPadding[1] + self.outerPadding[3] + toppadding
|
|
162
|
+
height + self.outerPadding[1] + self.outerPadding[3] + toppadding,
|
|
153
163
|
)
|
|
154
|
-
|
|
155
|
-
|
|
164
|
+
|
|
165
|
+
legend_image = None
|
|
166
|
+
legend_doc = None
|
|
167
|
+
legend_top_margin = 0
|
|
168
|
+
|
|
156
169
|
if self.__legends:
|
|
157
170
|
self.legendbox = LegendBox()
|
|
158
171
|
for d in self.__legends:
|
|
159
172
|
self.legendbox.add(*d)
|
|
160
173
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
174
|
+
if vector:
|
|
175
|
+
legend_doc = self.legendbox.finalize_svg_sneaky(self)
|
|
176
|
+
legend_h = legend_doc.height
|
|
177
|
+
legend_top_margin = self.legendbox.getAttr('topMargin')
|
|
178
|
+
else:
|
|
179
|
+
legend_image = self.legendbox.finalize(self, sneaky=True)
|
|
180
|
+
legend_h = legend_image.height
|
|
181
|
+
legend_top_margin = self.legendbox.getAttr('topMargin')
|
|
182
|
+
|
|
183
|
+
size = (size[0], size[1] + legend_h + legend_top_margin)
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
"grid": grid,
|
|
187
|
+
"cellWidth": cellWidth,
|
|
188
|
+
"gapcol": gapcol,
|
|
189
|
+
"size": size,
|
|
190
|
+
"leftpadding": leftpadding,
|
|
191
|
+
"toppadding": toppadding,
|
|
192
|
+
"legend_image": legend_image,
|
|
193
|
+
"legend_doc": legend_doc,
|
|
194
|
+
"legend_top_margin": legend_top_margin,
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def _composite_png(self, layout: dict) -> Image.Image:
|
|
199
|
+
grid = layout["grid"]
|
|
200
|
+
size = layout["size"]
|
|
201
|
+
cellWidth = layout["cellWidth"]
|
|
202
|
+
gapcol = layout["gapcol"]
|
|
203
|
+
leftpadding = layout["leftpadding"]
|
|
204
|
+
toppadding = layout["toppadding"]
|
|
167
205
|
|
|
168
206
|
image = Image.new('RGBA', size, self.getAttr('backgroundColor'))
|
|
169
207
|
|
|
170
|
-
|
|
171
|
-
|
|
208
|
+
legend_image = layout["legend_image"]
|
|
209
|
+
if legend_image is not None:
|
|
210
|
+
image.alpha_composite(
|
|
211
|
+
legend_image,
|
|
212
|
+
(
|
|
213
|
+
image.width // 2 - legend_image.width // 2,
|
|
214
|
+
image.height - legend_image.height - self.outerPadding[3],
|
|
215
|
+
),
|
|
216
|
+
)
|
|
172
217
|
|
|
173
|
-
# TEGNER!
|
|
174
|
-
# add plots to grid image
|
|
175
218
|
y = toppadding + self.outerPadding[1]
|
|
176
219
|
|
|
177
220
|
for row in grid:
|
|
178
|
-
|
|
179
221
|
maxHeight = 0
|
|
180
222
|
x = leftpadding + self.outerPadding[0]
|
|
181
223
|
|
|
182
224
|
for plot in row:
|
|
183
|
-
"""
|
|
184
|
-
Is a little ineffecient to write and then read from memory with png extenseion
|
|
185
|
-
but here goes.
|
|
186
|
-
"""
|
|
187
|
-
|
|
188
|
-
w, h = plot.getSize()
|
|
189
|
-
|
|
190
225
|
img = Image.open(plot.__ioBytes)
|
|
226
|
+
plot.__ioBytes.seek(0)
|
|
191
227
|
image.paste(img, (x - plot.padding[0], y - plot.padding[3]))
|
|
192
|
-
|
|
193
228
|
x += cellWidth + gapcol
|
|
194
|
-
maxHeight = max(maxHeight,
|
|
229
|
+
maxHeight = max(maxHeight, plot.getSize()[1])
|
|
195
230
|
|
|
196
231
|
y += maxHeight
|
|
197
232
|
|
|
198
|
-
|
|
233
|
+
return image
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def _composite_svg(self, layout: dict) -> str:
|
|
237
|
+
size = layout["size"]
|
|
238
|
+
grid = layout["grid"]
|
|
239
|
+
cellWidth = layout["cellWidth"]
|
|
240
|
+
gapcol = layout["gapcol"]
|
|
241
|
+
leftpadding = layout["leftpadding"]
|
|
242
|
+
toppadding = layout["toppadding"]
|
|
243
|
+
|
|
244
|
+
doc = SvgDocument(size)
|
|
245
|
+
bg = self.getAttr('backgroundColor')
|
|
246
|
+
if bg[3] != 0:
|
|
247
|
+
doc.add_rect(0, 0, size[0], size[1], bg)
|
|
248
|
+
|
|
249
|
+
legend_doc = layout["legend_doc"]
|
|
250
|
+
if legend_doc is not None:
|
|
251
|
+
lx = size[0] // 2 - legend_doc.width // 2
|
|
252
|
+
ly = size[1] - legend_doc.height - self.outerPadding[3]
|
|
253
|
+
embed_svg_children(doc, list(legend_doc._elements), lx, ly)
|
|
254
|
+
merge_fondi_css(doc, legend_doc._fondi_font_css)
|
|
255
|
+
|
|
256
|
+
y = toppadding + self.outerPadding[1]
|
|
257
|
+
|
|
258
|
+
for row in grid:
|
|
259
|
+
maxHeight = 0
|
|
260
|
+
x = leftpadding + self.outerPadding[0]
|
|
261
|
+
|
|
262
|
+
for plot in row:
|
|
263
|
+
px = x - plot.padding[0]
|
|
264
|
+
py = y - plot.padding[3]
|
|
265
|
+
|
|
266
|
+
if _cell_supports_svg(plot):
|
|
267
|
+
plot.__ioBytes.seek(0)
|
|
268
|
+
xml = plot.__ioBytes.read().decode("utf-8")
|
|
269
|
+
root = parse_svg_root(xml)
|
|
270
|
+
children, fondi_css = extract_svg_children(root)
|
|
271
|
+
embed_svg_children(doc, children, px, py)
|
|
272
|
+
merge_fondi_css(doc, fondi_css)
|
|
273
|
+
else:
|
|
274
|
+
plot.__ioBytes.seek(0)
|
|
275
|
+
img = Image.open(plot.__ioBytes)
|
|
276
|
+
doc.add_image(img, px, py, y_coord="top")
|
|
277
|
+
|
|
278
|
+
x += cellWidth + gapcol
|
|
279
|
+
maxHeight = max(maxHeight, plot.getSize()[1])
|
|
280
|
+
|
|
281
|
+
y += maxHeight
|
|
282
|
+
|
|
283
|
+
return doc.serialize()
|
|
199
284
|
|
|
285
|
+
|
|
286
|
+
def __bake__(self):
|
|
287
|
+
layout = self._prepare_cells(vector=False)
|
|
288
|
+
image = self._composite_png(layout)
|
|
289
|
+
self.__bakedImage__ = image
|
|
200
290
|
return image
|
|
201
291
|
|
|
292
|
+
|
|
293
|
+
def __bake_svg__(self) -> str:
|
|
294
|
+
layout = self._prepare_cells(vector=True)
|
|
295
|
+
xml = self._composite_svg(layout)
|
|
296
|
+
self.__bakedSvg__ = xml
|
|
297
|
+
return xml
|
|
298
|
+
|
|
202
299
|
|
|
203
300
|
def addRow(self, *row:list):
|
|
204
301
|
"""
|
|
@@ -232,18 +329,36 @@ class Grid(AttrObject):
|
|
|
232
329
|
self.grid[i].append(plot)
|
|
233
330
|
|
|
234
331
|
|
|
235
|
-
def save(self,
|
|
236
|
-
|
|
332
|
+
def save(self, fname: Union[str, BytesIO], format: Optional[str] = None):
|
|
333
|
+
fmt = infer_format(fname, format)
|
|
334
|
+
|
|
335
|
+
if fmt == "svg":
|
|
336
|
+
if self.__bakedSvg__ is not None:
|
|
337
|
+
xml = self.__bakedSvg__
|
|
338
|
+
else:
|
|
339
|
+
xml = self.__bake_svg__()
|
|
340
|
+
|
|
341
|
+
if fname is not None:
|
|
342
|
+
if is_file_path(fname):
|
|
343
|
+
with open(fname, 'w', encoding='utf-8') as f:
|
|
344
|
+
f.write(xml)
|
|
345
|
+
else:
|
|
346
|
+
fname.write(xml.encode('utf-8'))
|
|
347
|
+
return
|
|
348
|
+
|
|
237
349
|
if self.__bakedImage__:
|
|
238
|
-
|
|
350
|
+
if is_file_path(fname):
|
|
351
|
+
self.__bakedImage__.save(fname)
|
|
352
|
+
else:
|
|
353
|
+
self.__bakedImage__.save(fname, format="png")
|
|
239
354
|
return
|
|
240
355
|
|
|
241
356
|
img = self.__bake__()
|
|
242
|
-
|
|
243
|
-
if
|
|
244
|
-
img.save(
|
|
357
|
+
|
|
358
|
+
if isinstance(fname, str):
|
|
359
|
+
img.save(fname)
|
|
245
360
|
else:
|
|
246
|
-
img.save(
|
|
361
|
+
img.save(fname, format="png")
|
|
247
362
|
|
|
248
363
|
|
|
249
364
|
def show(self):
|
|
@@ -277,4 +392,7 @@ class Grid(AttrObject):
|
|
|
277
392
|
|
|
278
393
|
|
|
279
394
|
def getSize(self):
|
|
280
|
-
|
|
395
|
+
if self.__bakedImage__:
|
|
396
|
+
return self.__bakedImage__.size
|
|
397
|
+
layout = self._prepare_cells(vector=False)
|
|
398
|
+
return layout["size"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.bright-oblique.ttf
RENAMED
|
File without changes
|
{kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.bright-roman.ttf
RENAMED
|
File without changes
|
{kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.bright-semibold.ttf
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.concrete-bold.ttf
RENAMED
|
File without changes
|
{kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.concrete-bolditalic.ttf
RENAMED
|
File without changes
|
{kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.concrete-italic.ttf
RENAMED
|
File without changes
|
{kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.concrete-roman.ttf
RENAMED
|
File without changes
|
{kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.sans-serif-bold.ttf
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.sans-serif-medium.ttf
RENAMED
|
File without changes
|
{kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.sans-serif-oblique.ttf
RENAMED
|
File without changes
|
|
File without changes
|
{kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.serif-bolditalic.ttf
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.serif-italic.ttf
RENAMED
|
File without changes
|
{kaxe-1.4.2 → kaxe-1.4.4.dev2}/src/kaxe/resources/computer-modern-family/cmu.serif-roman.ttf
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|