lets-plot 4.6.2__cp39-cp39-win_amd64.whl → 4.7.0__cp39-cp39-win_amd64.whl
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.
Potentially problematic release.
This version of lets-plot might be problematic. Click here for more details.
- lets_plot/_global_settings.py +5 -0
- lets_plot/_kbridge.py +7 -0
- lets_plot/_type_utils.py +29 -6
- lets_plot/_version.py +1 -1
- lets_plot/bistro/im.py +2 -2
- lets_plot/bistro/waterfall.py +93 -12
- lets_plot/export/ggsave_.py +22 -14
- lets_plot/package_data/lets-plot.min.js +2 -1
- lets_plot/plot/annotation.py +75 -18
- lets_plot/plot/core.py +152 -33
- lets_plot/plot/geom.py +752 -93
- lets_plot/plot/geom_function_.py +1 -1
- lets_plot/plot/geom_imshow_.py +42 -51
- lets_plot/plot/pos.py +13 -44
- lets_plot/plot/scale_position.py +9 -3
- lets_plot/plot/series_meta.py +179 -105
- lets_plot/plot/stat.py +4 -4
- lets_plot/plot/subplots.py +4 -4
- lets_plot/plot/theme_.py +57 -54
- lets_plot/plot/util.py +31 -5
- {lets_plot-4.6.2.dist-info → lets_plot-4.7.0.dist-info}/METADATA +52 -24
- {lets_plot-4.6.2.dist-info → lets_plot-4.7.0.dist-info}/RECORD +30 -26
- {lets_plot-4.6.2.dist-info → lets_plot-4.7.0.dist-info}/WHEEL +1 -1
- lets_plot-4.7.0.dist-info/licenses/licenses/LICENSE.FreeType +166 -0
- lets_plot-4.7.0.dist-info/licenses/licenses/LICENSE.ImageMagick +106 -0
- lets_plot-4.7.0.dist-info/licenses/licenses/LICENSE.expat +21 -0
- lets_plot-4.7.0.dist-info/licenses/licenses/LICENSE.fontconfig +200 -0
- lets_plot_kotlin_bridge.cp39-win_amd64.pyd +0 -0
- {lets_plot-4.6.2.dist-info → lets_plot-4.7.0.dist-info}/licenses/LICENSE +0 -0
- {lets_plot-4.6.2.dist-info → lets_plot-4.7.0.dist-info}/top_level.txt +0 -0
lets_plot/plot/annotation.py
CHANGED
|
@@ -14,7 +14,24 @@ __all__ = ['layer_labels']
|
|
|
14
14
|
|
|
15
15
|
class layer_labels(FeatureSpec):
|
|
16
16
|
"""
|
|
17
|
-
Configure annotations
|
|
17
|
+
Configure annotations for geometry layers.
|
|
18
|
+
|
|
19
|
+
Annotations are currently supported for bar, pie, and crossbar geometry
|
|
20
|
+
layers. This class provides methods to customize the appearance and
|
|
21
|
+
content of text labels displayed on these geometries.
|
|
22
|
+
|
|
23
|
+
Notes
|
|
24
|
+
-----
|
|
25
|
+
By default, annotation text color is automatically selected for optimal
|
|
26
|
+
contrast: white text appears on darker filled geometries, and black text
|
|
27
|
+
appears on lighter filled geometries.
|
|
28
|
+
|
|
29
|
+
The text color can be manually specified using:
|
|
30
|
+
``theme(label_text=element_text(color=...))``
|
|
31
|
+
|
|
32
|
+
Alternatively, the ``inherit_color()`` method can be used to override both
|
|
33
|
+
automatic and manual color settings, making the annotation text use the
|
|
34
|
+
geometry's ``color`` aesthetic instead.
|
|
18
35
|
|
|
19
36
|
Examples
|
|
20
37
|
--------
|
|
@@ -24,9 +41,9 @@ class layer_labels(FeatureSpec):
|
|
|
24
41
|
|
|
25
42
|
from lets_plot import *
|
|
26
43
|
LetsPlot.setup_html()
|
|
27
|
-
data = {'name': ['a', 'b', 'c', 'd', 'b'], 'value': [40, 90, 10, 50, 20 ]
|
|
44
|
+
data = {'name': ['a', 'b', 'c', 'd', 'b'], 'value': [40, 90, 10, 50, 20 ]}
|
|
28
45
|
ggplot(data) + geom_pie(aes(slice='value', fill='name'), size=15, hole=0.4, \\
|
|
29
|
-
stat='identity', tooltips
|
|
46
|
+
stat='identity', tooltips='none', \\
|
|
30
47
|
labels=layer_labels().line('@value'))
|
|
31
48
|
|
|
32
49
|
"""
|
|
@@ -46,11 +63,12 @@ class layer_labels(FeatureSpec):
|
|
|
46
63
|
self._lines: List = None
|
|
47
64
|
self._variables = variables
|
|
48
65
|
self._size = None
|
|
66
|
+
self._useLayerColor = None
|
|
49
67
|
super().__init__('labels', name=None)
|
|
50
68
|
|
|
51
69
|
def as_dict(self):
|
|
52
70
|
"""
|
|
53
|
-
Return
|
|
71
|
+
Return a dictionary of all properties of the object.
|
|
54
72
|
|
|
55
73
|
Returns
|
|
56
74
|
-------
|
|
@@ -76,6 +94,7 @@ class layer_labels(FeatureSpec):
|
|
|
76
94
|
d['lines'] = self._lines
|
|
77
95
|
d['variables'] = self._variables
|
|
78
96
|
d['annotation_size'] = self._size
|
|
97
|
+
d['use_layer_color'] = self._useLayerColor
|
|
79
98
|
return _filter_none(d)
|
|
80
99
|
|
|
81
100
|
def format(self, field=None, format=None):
|
|
@@ -113,7 +132,7 @@ class layer_labels(FeatureSpec):
|
|
|
113
132
|
|
|
114
133
|
from lets_plot import *
|
|
115
134
|
LetsPlot.setup_html()
|
|
116
|
-
data = {'name': ['a', 'b', 'c', 'd', 'b'], 'value': [40, 90, 10, 50, 20 ]
|
|
135
|
+
data = {'name': ['a', 'b', 'c', 'd', 'b'], 'value': [40, 90, 10, 50, 20 ]}
|
|
117
136
|
ggplot(data) + geom_pie(aes(fill=as_discrete('name', order_by='..count..'), weight='value'), \\
|
|
118
137
|
size=15, tooltips='none', \\
|
|
119
138
|
labels=layer_labels(['..proppct..']) \\
|
|
@@ -127,7 +146,7 @@ class layer_labels(FeatureSpec):
|
|
|
127
146
|
|
|
128
147
|
from lets_plot import *
|
|
129
148
|
LetsPlot.setup_html()
|
|
130
|
-
data = {'name': ['a', 'b', 'c', 'd', 'b'], 'value': [40, 90, 10, 50, 20 ]
|
|
149
|
+
data = {'name': ['a', 'b', 'c', 'd', 'b'], 'value': [40, 90, 10, 50, 20 ]}
|
|
131
150
|
ggplot(data) + geom_pie(aes(fill=as_discrete('name', order_by='..count..', order=1), weight='value'), \\
|
|
132
151
|
size=15, tooltips='none', \\
|
|
133
152
|
labels=layer_labels() \\
|
|
@@ -146,12 +165,17 @@ class layer_labels(FeatureSpec):
|
|
|
146
165
|
|
|
147
166
|
def line(self, value):
|
|
148
167
|
"""
|
|
149
|
-
|
|
168
|
+
Add a line of text to the multiline label annotation.
|
|
169
|
+
|
|
170
|
+
This method configures one line of text that will be displayed in a
|
|
171
|
+
multiline label. Multiple calls to this method can be chained to build
|
|
172
|
+
up a complete multiline annotation.
|
|
150
173
|
|
|
151
174
|
Parameters
|
|
152
175
|
----------
|
|
153
176
|
value : str
|
|
154
|
-
|
|
177
|
+
The text content for this line of the annotation. Can include
|
|
178
|
+
variable and aesthetic references.
|
|
155
179
|
|
|
156
180
|
Returns
|
|
157
181
|
-------
|
|
@@ -162,17 +186,16 @@ class layer_labels(FeatureSpec):
|
|
|
162
186
|
-----
|
|
163
187
|
Variables and aesthetics can be accessed via special syntax:
|
|
164
188
|
|
|
165
|
-
- ^color for
|
|
189
|
+
- ^color for aesthetics,
|
|
166
190
|
- @x for variable,
|
|
167
191
|
- @{x + 1} for variable with spaces in the name,
|
|
168
192
|
- @{x^2 + 1} for variable with spaces and '^' symbol in the name,
|
|
169
193
|
- @x^2 for variable with '^' symbol in its name.
|
|
170
194
|
|
|
171
|
-
|
|
172
|
-
in the literal text - by doubling:
|
|
195
|
+
Special characters can be escaped:
|
|
173
196
|
|
|
174
|
-
- 'x\\\\^2' -> "x^2"
|
|
175
|
-
- '{{x}}' -> "{x}"
|
|
197
|
+
- 'x\\\\^2' -> "x^2" (escape ^ with backslash)
|
|
198
|
+
- '{{x}}' -> "{x}" (escape braces by doubling)
|
|
176
199
|
|
|
177
200
|
Examples
|
|
178
201
|
--------
|
|
@@ -182,7 +205,7 @@ class layer_labels(FeatureSpec):
|
|
|
182
205
|
|
|
183
206
|
from lets_plot import *
|
|
184
207
|
LetsPlot.setup_html()
|
|
185
|
-
data = {'name': ['a', 'b', 'c', 'd', 'b'], 'value': [40, 90, 10, 50, 20 ]
|
|
208
|
+
data = {'name': ['a', 'b', 'c', 'd', 'b'], 'value': [40, 90, 10, 50, 20 ]}
|
|
186
209
|
ggplot(data) + geom_pie(aes(fill='name', weight='value'), size=15, \\
|
|
187
210
|
tooltips='none', \\
|
|
188
211
|
labels=layer_labels()\\
|
|
@@ -200,12 +223,12 @@ class layer_labels(FeatureSpec):
|
|
|
200
223
|
|
|
201
224
|
def size(self, value):
|
|
202
225
|
"""
|
|
203
|
-
|
|
226
|
+
Set the text size for the annotation.
|
|
204
227
|
|
|
205
228
|
Parameters
|
|
206
229
|
----------
|
|
207
230
|
value : float
|
|
208
|
-
|
|
231
|
+
The text size value for the annotation.
|
|
209
232
|
|
|
210
233
|
Returns
|
|
211
234
|
-------
|
|
@@ -221,9 +244,9 @@ class layer_labels(FeatureSpec):
|
|
|
221
244
|
|
|
222
245
|
from lets_plot import *
|
|
223
246
|
LetsPlot.setup_html()
|
|
224
|
-
data = {'name': ['a', 'b', 'c', 'd', 'b'], 'value': [40, 90, 10, 50, 20 ]
|
|
247
|
+
data = {'name': ['a', 'b', 'c', 'd', 'b'], 'value': [40, 90, 10, 50, 20 ]}
|
|
225
248
|
ggplot(data) + geom_pie(aes(slice='value', fill='name'), size=15, hole=0.4, \\
|
|
226
|
-
stat='identity', tooltips
|
|
249
|
+
stat='identity', tooltips='none', \\
|
|
227
250
|
labels=layer_labels().line('@value')
|
|
228
251
|
.size(25))
|
|
229
252
|
|
|
@@ -231,3 +254,37 @@ class layer_labels(FeatureSpec):
|
|
|
231
254
|
|
|
232
255
|
self._size = value
|
|
233
256
|
return self
|
|
257
|
+
|
|
258
|
+
def inherit_color(self):
|
|
259
|
+
"""
|
|
260
|
+
Use the layer's color for the annotation text.
|
|
261
|
+
|
|
262
|
+
When enabled, the annotation text will inherit the color from the
|
|
263
|
+
layer it's associated with, rather than using a default or
|
|
264
|
+
explicitly set color.
|
|
265
|
+
|
|
266
|
+
Returns
|
|
267
|
+
-------
|
|
268
|
+
`layer_labels`
|
|
269
|
+
Annotations specification.
|
|
270
|
+
|
|
271
|
+
Examples
|
|
272
|
+
--------
|
|
273
|
+
|
|
274
|
+
.. jupyter-execute::
|
|
275
|
+
:linenos:
|
|
276
|
+
:emphasize-lines: 8
|
|
277
|
+
|
|
278
|
+
from lets_plot import *
|
|
279
|
+
LetsPlot.setup_html()
|
|
280
|
+
data = {'name': ['a', 'b', 'c', 'd', 'b'], 'value': [40, 90, 10, 50, 20 ]}
|
|
281
|
+
ggplot(data) + geom_pie(aes(slice='value', color='name'), alpha=0, size=15, hole=0.4, \\
|
|
282
|
+
stroke=5, spacer_color='pen', \\
|
|
283
|
+
stat='identity', tooltips='none', \\
|
|
284
|
+
labels=layer_labels().line('@value')
|
|
285
|
+
.inherit_color())
|
|
286
|
+
|
|
287
|
+
"""
|
|
288
|
+
|
|
289
|
+
self._useLayerColor = True
|
|
290
|
+
return self
|
lets_plot/plot/core.py
CHANGED
|
@@ -9,7 +9,7 @@ from typing import Union
|
|
|
9
9
|
|
|
10
10
|
__all__ = ['aes', 'layer']
|
|
11
11
|
|
|
12
|
-
from lets_plot._global_settings import get_global_bool, has_global_value, FRAGMENTS_ENABLED
|
|
12
|
+
from lets_plot._global_settings import get_global_bool, has_global_value, FRAGMENTS_ENABLED, MAGICK_EXPORT
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
def aes(x=None, y=None, **kwargs):
|
|
@@ -96,7 +96,7 @@ def layer(geom=None, stat=None, data=None, mapping=None, position=None, **kwargs
|
|
|
96
96
|
mapped to plot "aesthetics".
|
|
97
97
|
position : str or `FeatureSpec`
|
|
98
98
|
Position adjustment.
|
|
99
|
-
Either a position adjustment name: 'dodge', '
|
|
99
|
+
Either a position adjustment name: 'dodge', 'jitter', 'nudge', 'jitterdodge', 'fill',
|
|
100
100
|
'stack' or 'identity', or the result of calling a position adjustment function (e.g., `position_dodge()` etc.).
|
|
101
101
|
kwargs:
|
|
102
102
|
Other arguments passed on to layer. These are often aesthetics settings, used to set an aesthetic to a fixed
|
|
@@ -402,9 +402,11 @@ class PlotSpec(FeatureSpec):
|
|
|
402
402
|
return plot
|
|
403
403
|
|
|
404
404
|
if other.kind == 'mapping': # +aes(..)
|
|
405
|
-
existing_spec = plot.props().get('mapping', aes())
|
|
406
|
-
merged_mapping = {**existing_spec.as_dict(), **other.as_dict()}
|
|
407
|
-
plot.props()['mapping'] = aes(**merged_mapping)
|
|
405
|
+
# existing_spec = plot.props().get('mapping', aes())
|
|
406
|
+
# merged_mapping = {**existing_spec.as_dict(), **other.as_dict()}
|
|
407
|
+
# plot.props()['mapping'] = aes(**merged_mapping)
|
|
408
|
+
from lets_plot.plot.util import update_plot_aes_mapping # local import to break circular reference
|
|
409
|
+
update_plot_aes_mapping(plot, other)
|
|
408
410
|
return plot
|
|
409
411
|
|
|
410
412
|
# add feature to properties
|
|
@@ -566,10 +568,10 @@ class PlotSpec(FeatureSpec):
|
|
|
566
568
|
h : float, default=None
|
|
567
569
|
Height of the output image in units.
|
|
568
570
|
Only applicable when exporting to PNG or PDF.
|
|
569
|
-
unit : {'in', 'cm', 'mm'}, default=
|
|
571
|
+
unit : {'in', 'cm', 'mm'}, default='in'
|
|
570
572
|
Unit of the output image. One of: 'in', 'cm', 'mm'.
|
|
571
573
|
Only applicable when exporting to PNG or PDF.
|
|
572
|
-
dpi : int, default=
|
|
574
|
+
dpi : int, default=300
|
|
573
575
|
Resolution in dots per inch.
|
|
574
576
|
Only applicable when exporting to PNG or PDF.
|
|
575
577
|
|
|
@@ -580,9 +582,27 @@ class PlotSpec(FeatureSpec):
|
|
|
580
582
|
|
|
581
583
|
Notes
|
|
582
584
|
-----
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
585
|
+
- If `w`, `h`, `unit`, and `dpi` are all specified:
|
|
586
|
+
|
|
587
|
+
- The plot's pixel size (default or set by `ggsize()`) is ignored.
|
|
588
|
+
- The output size is calculated using the specified `w`, `h`, `unit`, and `dpi`.
|
|
589
|
+
|
|
590
|
+
- The plot is resized to fit the specified `w` x `h` area, which may affect the layout, tick labels, and other elements.
|
|
591
|
+
|
|
592
|
+
- If only `dpi` is specified:
|
|
593
|
+
|
|
594
|
+
- The plot's pixel size (default or set by `ggsize()`) is converted to inches using the standard display PPI of 96.
|
|
595
|
+
- The output size is then calculated based on the specified DPI.
|
|
596
|
+
|
|
597
|
+
- The plot maintains its aspect ratio, preserving layout, tick labels, and other visual elements.
|
|
598
|
+
- Useful for printing - the plot will appear nearly the same size as on screen.
|
|
599
|
+
|
|
600
|
+
- If `w`, `h` are not specified:
|
|
601
|
+
|
|
602
|
+
- The `scale` parameter is used to determine the output size.
|
|
603
|
+
|
|
604
|
+
- The plot maintains its aspect ratio, preserving layout, tick labels, and other visual elements.
|
|
605
|
+
- Useful for generating high-resolution images suitable for publication.
|
|
586
606
|
|
|
587
607
|
Examples
|
|
588
608
|
--------
|
|
@@ -600,6 +620,7 @@ class PlotSpec(FeatureSpec):
|
|
|
600
620
|
file_like = io.BytesIO()
|
|
601
621
|
p.to_png(file_like)
|
|
602
622
|
display.Image(file_like.getvalue())
|
|
623
|
+
|
|
603
624
|
"""
|
|
604
625
|
return _export_as_raster(self, path, scale, 'png', w=w, h=h, unit=unit, dpi=dpi)
|
|
605
626
|
|
|
@@ -623,10 +644,10 @@ class PlotSpec(FeatureSpec):
|
|
|
623
644
|
h : float, default=None
|
|
624
645
|
Height of the output image in units.
|
|
625
646
|
Only applicable when exporting to PNG or PDF.
|
|
626
|
-
unit : {'in', 'cm', 'mm'}, default=
|
|
647
|
+
unit : {'in', 'cm', 'mm'}, default='in'
|
|
627
648
|
Unit of the output image. One of: 'in', 'cm', 'mm'.
|
|
628
649
|
Only applicable when exporting to PNG or PDF.
|
|
629
|
-
dpi : int, default=
|
|
650
|
+
dpi : int, default=300
|
|
630
651
|
Resolution in dots per inch.
|
|
631
652
|
Only applicable when exporting to PNG or PDF.
|
|
632
653
|
|
|
@@ -637,9 +658,27 @@ class PlotSpec(FeatureSpec):
|
|
|
637
658
|
|
|
638
659
|
Notes
|
|
639
660
|
-----
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
661
|
+
- If `w`, `h`, `unit`, and `dpi` are all specified:
|
|
662
|
+
|
|
663
|
+
- The plot's pixel size (default or set by `ggsize()`) is ignored.
|
|
664
|
+
- The output size is calculated using the specified `w`, `h`, `unit`, and `dpi`.
|
|
665
|
+
|
|
666
|
+
- The plot is resized to fit the specified `w` x `h` area, which may affect the layout, tick labels, and other elements.
|
|
667
|
+
|
|
668
|
+
- If only `dpi` is specified:
|
|
669
|
+
|
|
670
|
+
- The plot's pixel size (default or set by `ggsize()`) is converted to inches using the standard display PPI of 96.
|
|
671
|
+
- The output size is then calculated based on the specified DPI.
|
|
672
|
+
|
|
673
|
+
- The plot maintains its aspect ratio, preserving layout, tick labels, and other visual elements.
|
|
674
|
+
- Useful for printing - the plot will appear nearly the same size as on screen.
|
|
675
|
+
|
|
676
|
+
- If `w`, `h` are not specified:
|
|
677
|
+
|
|
678
|
+
- The `scale` parameter is used to determine the output size.
|
|
679
|
+
|
|
680
|
+
- The plot maintains its aspect ratio, preserving layout, tick labels, and other visual elements.
|
|
681
|
+
- Useful for generating high-resolution images suitable for publication.
|
|
643
682
|
|
|
644
683
|
Examples
|
|
645
684
|
--------
|
|
@@ -660,6 +699,7 @@ class PlotSpec(FeatureSpec):
|
|
|
660
699
|
p = ggplot({'x': x, 'y': y}, aes(x='x', y='y')) + geom_jitter()
|
|
661
700
|
file_like = io.BytesIO()
|
|
662
701
|
p.to_pdf(file_like)
|
|
702
|
+
|
|
663
703
|
"""
|
|
664
704
|
return _export_as_raster(self, path, scale, 'pdf', w=w, h=h, unit=unit, dpi=dpi)
|
|
665
705
|
|
|
@@ -875,29 +915,108 @@ def _to_html(spec, path, iframe: bool) -> Union[str, None]:
|
|
|
875
915
|
return None
|
|
876
916
|
|
|
877
917
|
|
|
878
|
-
def _export_as_raster(spec, path, scale: float, export_format: str, w=None, h=None, unit=None, dpi=None) -> Union[
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
return
|
|
918
|
+
def _export_as_raster(spec, path, scale: float, export_format: str, w=None, h=None, unit=None, dpi=None) -> Union[str, None]:
|
|
919
|
+
if get_global_bool(MAGICK_EXPORT):
|
|
920
|
+
if w is None and h is None and unit is None and dpi is None:
|
|
921
|
+
def_scale = 2.0
|
|
922
|
+
def_dpi = -1
|
|
923
|
+
def_unit = ""
|
|
924
|
+
else:
|
|
925
|
+
def_scale = 1.0
|
|
926
|
+
def_dpi = 300
|
|
927
|
+
def_unit = 'in'
|
|
928
|
+
|
|
929
|
+
return _export_with_magick(
|
|
930
|
+
spec,
|
|
931
|
+
path,
|
|
932
|
+
scale if scale is not None else def_scale,
|
|
933
|
+
export_format,
|
|
934
|
+
w if w is not None else -1,
|
|
935
|
+
h if h is not None else -1,
|
|
936
|
+
unit if unit is not None else def_unit,
|
|
937
|
+
dpi if dpi is not None else def_dpi
|
|
938
|
+
)
|
|
939
|
+
else:
|
|
940
|
+
return _export_with_cairo(spec, path, scale, export_format, w, h, unit, dpi)
|
|
941
|
+
|
|
942
|
+
|
|
943
|
+
def _export_with_magick(spec, path, scale: float, export_format: str, w, h, unit, dpi) -> Union[str, None]:
|
|
944
|
+
import base64
|
|
945
|
+
from .. import _kbridge
|
|
946
|
+
|
|
947
|
+
if isinstance(path, str):
|
|
948
|
+
file_path = _makedirs(path)
|
|
949
|
+
file_like_object = None
|
|
950
|
+
else:
|
|
951
|
+
file_like_object = path
|
|
952
|
+
file_path = None
|
|
953
|
+
|
|
954
|
+
png_base64 = _kbridge._export_png(spec.as_dict(), float(w), float(h), unit, int(dpi), float(scale))
|
|
955
|
+
png = base64.b64decode(png_base64)
|
|
890
956
|
|
|
891
957
|
if export_format.lower() == 'png':
|
|
892
|
-
|
|
958
|
+
if file_path is not None:
|
|
959
|
+
with open(file_path, 'wb') as f:
|
|
960
|
+
f.write(png)
|
|
961
|
+
return file_path
|
|
962
|
+
else:
|
|
963
|
+
file_like_object.write(png)
|
|
964
|
+
return None
|
|
893
965
|
elif export_format.lower() == 'pdf':
|
|
894
|
-
|
|
966
|
+
try:
|
|
967
|
+
from PIL import Image
|
|
968
|
+
except ImportError:
|
|
969
|
+
import sys
|
|
970
|
+
print("\n"
|
|
971
|
+
"To export Lets-Plot figure to a PDF file please install pillow library"
|
|
972
|
+
"to your Python environment.\n"
|
|
973
|
+
"Pillow is free and distributed under the MIT-CMU license.\n"
|
|
974
|
+
"For more details visit: https://python-pillow.github.io/\n", file=sys.stderr)
|
|
975
|
+
return None
|
|
976
|
+
|
|
977
|
+
|
|
978
|
+
with Image.open(io.BytesIO(png)) as img:
|
|
979
|
+
if img.mode == 'RGBA':
|
|
980
|
+
img = img.convert('RGB')
|
|
981
|
+
|
|
982
|
+
dpi = dpi if dpi is not None else 96 # Default DPI if not specified
|
|
983
|
+
if file_path is not None:
|
|
984
|
+
img.save(file_path, "PDF", dpi=(dpi, dpi))
|
|
985
|
+
return file_path
|
|
986
|
+
else:
|
|
987
|
+
img.save(file_like_object, "PDF", dpi=(dpi, dpi))
|
|
988
|
+
return None
|
|
895
989
|
else:
|
|
896
990
|
raise ValueError("Unknown export format: {}".format(export_format))
|
|
897
991
|
|
|
992
|
+
|
|
993
|
+
def _export_with_cairo(spec, path, scale: float, export_format: str, w=None, h=None, unit=None, dpi=None) -> Union[str, None]:
|
|
898
994
|
from .. import _kbridge
|
|
899
|
-
|
|
900
|
-
|
|
995
|
+
|
|
996
|
+
input = None
|
|
997
|
+
export_function = None
|
|
998
|
+
|
|
999
|
+
if export_format.lower() == 'png' or export_format.lower() == 'pdf':
|
|
1000
|
+
try:
|
|
1001
|
+
import cairosvg
|
|
1002
|
+
except ImportError:
|
|
1003
|
+
import sys
|
|
1004
|
+
print("\n"
|
|
1005
|
+
"To export Lets-Plot figure to a PNG or PDF file please install CairoSVG library"
|
|
1006
|
+
"to your Python environment.\n"
|
|
1007
|
+
"CairoSVG is free and distributed under the LGPL-3.0 license.\n"
|
|
1008
|
+
"For more details visit: https://cairosvg.org/documentation/\n", file=sys.stderr)
|
|
1009
|
+
return None
|
|
1010
|
+
|
|
1011
|
+
if export_format.lower() == 'png':
|
|
1012
|
+
export_function = cairosvg.svg2png
|
|
1013
|
+
elif export_format.lower() == 'pdf':
|
|
1014
|
+
export_function = cairosvg.svg2pdf
|
|
1015
|
+
|
|
1016
|
+
# Use SVG image-rendering style as Cairo doesn't support CSS image-rendering style,
|
|
1017
|
+
input = _kbridge._generate_svg(spec.as_dict(), use_css_pixelated_image_rendering=False)
|
|
1018
|
+
else:
|
|
1019
|
+
raise ValueError("Unknown export format: {}".format(export_format))
|
|
901
1020
|
|
|
902
1021
|
if isinstance(path, str):
|
|
903
1022
|
abspath = _makedirs(path)
|
|
@@ -910,10 +1029,10 @@ def _export_as_raster(spec, path, scale: float, export_format: str, w=None, h=No
|
|
|
910
1029
|
raise ValueError("w, h, unit, and dpi must all be specified")
|
|
911
1030
|
|
|
912
1031
|
w, h = _to_inches(w, unit) * dpi, _to_inches(h, unit) * dpi
|
|
913
|
-
export_function(bytestring=
|
|
1032
|
+
export_function(bytestring=input, write_to=path, dpi=dpi, output_width=w, output_height=h)
|
|
914
1033
|
else:
|
|
915
1034
|
scale = scale if scale is not None else 2.0
|
|
916
|
-
export_function(bytestring=
|
|
1035
|
+
export_function(bytestring=input, write_to=path, scale=scale)
|
|
917
1036
|
|
|
918
1037
|
return result
|
|
919
1038
|
|