-
Notifications
You must be signed in to change notification settings - Fork 46
Description
Inconsistent behavior of the scale
factor for vector exports that include encapsulated rasters
Thank you so much for your work that brings the amazing features of plotly
to static images too. This is extremely useful for publications that still rely on static figures (e.g. scientific journals). I was trying out some vector exports for different kind of plots at it was made clear from the plotly
documentation that part of figures with specific plots (e.g. surface
and mesh3d
) would be “rasterized”.
Apparently, increasing the scale
factor when calling write_image
for vector formats does not produce encapsulated rasters with a higher resolution as it does for png
(see details below). The main problem is that the behavior is not consistent between png
and vector formats (tested with 'pdf'
and 'svg'
).
Is there a way to have a consistent behavior?
(Sorry for the long issue message, I am trying to give as much info as possible.)
Image resolution using the scale
factor
To (somehow) control the image resolution (only relevant for raster formats or raster components encapsulated in vector formats) the write_image
(doc) method provides a scale
factor. This works as expected for 'png'
format but does not seem to work consistently for vector formats such as 'pdf'
and 'svg'
.
Different exports (using the kaleido
engine) of the same image with 'png'
, 'pdf'
, and 'svg'
formats and using different scale
factors of 1
and 2
are attached. The code to reproduce these exports is detailed below. PNG and PDF files are also attached to this message (SVG is not supported as direct upload).
From a quick inspection of the file sizes, it is clear that 'png'
does produce a larger file size (of “higher resolution”) when using a greater scale
factor. This is not the case for the vector format (i.e. 'pdf'
and 'svg'
). Their file sizes do not change when using a greater scale
factor (though their dimensions are scaled). I believe that the encapsulated rasters are similar (if not identical) despite the different scale
factors. This unfortunately prevents from any high-resolution exports in vector formats (most suitable for print).
Additional information about the package versions and the PDF exports are provided below.
Ideal image resolution control
For all raster formats or rasters encapsulated within vector formats, it would be ideal if one could define a dpi
parameter to control the final resolution of raster components. This would be much nicer than playing with a scale
factor and dividing the final image by that factor to include it somewhere else (e.g. an image within a PDF).
Apparently, the current resolution for png
exports is 72 dpi and 96 dpi for the vector exports. Both values are “default” values for screens (though many screens have much better resolutions nowadays), but clearly insufficient for print (for which 300 dpi and even 600 dpi is often required).
Additional observation
For the vector formats, it appears that not only the surface gets rasterized (again this is fine), but also the axis ticks (and the background). I do know if this is the expected behavior, but it would of course be optimal not to raster all components belonging to the surface plot (in particular fonts). I believe this would be much more complicated to control though.
Code
import os
import plotly.graph_objects as go
import pandas as pd
import itertools
# Read data from a csv
# https://plotly.com/python/3d-surface-plots/
z_data = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/api_docs/mt_bruno_elevation.csv')
# Create figure
fig = go.Figure(data=[go.Surface(z=z_data.values)])
fig.update_layout(
title='Mt Bruno Elevation',
autosize=False, width=500, height=500,
margin=dict(l=65, r=50, b=65, t=90)
)
# Export
export_dir = "images"
if not os.path.isdir(export_dir):
os.mkdir(path=export_dir)
scale_seq = 1, 2
format_seq = 'png', 'svg', 'pdf'
for scale, fmt in itertools.product(scale_seq, format_seq):
fig_name = f"export-surface-scale-{scale}.{fmt}"
file = os.path.join(export_dir, fig_name)
fig.write_image(file, format=fmt, scale=scale, engine='kaleido')
Additional information
plotly
and kaleido
versions
plotly==4.12.0
kaleido==0.0.3.post1
png
outputs
$ identify -format "format: %m\nfilename: %f\nfile size: %b\npixels: %wx%h\ndpi: %xx%y\n" export-surface-scale-1.png
format: PNG
filename: export-surface-scale-1.png
file size: 95676B
pixels: 500x500
dpi: 72x72
$ identify -format "format: %m\nfilename: %f\nfile size: %b\npixels: %wx%h\ndpi: %xx%y\n" export-surface-scale-2.png
format: PNG
filename: export-surface-scale-2.png
file size: 253429B
pixels: 1000x1000
dpi: 72x72
pdfinfo
outputs
From the page size property given in points, it seems like the renderer used a dpi of 96 as it started from a 500px x 500px image (i.e. 500 * 72 / 96 = 375
).
$ pdfinfo export-surface-scale-1.pdf
Creator: Chromium
Producer: Skia/PDF m83
CreationDate: Wed Nov 11 16:21:43 2020
ModDate: Wed Nov 11 16:21:43 2020
Tagged: no
UserProperties: no
Suspects: no
Form: none
JavaScript: no
Pages: 1
Encrypted: no
Page size: 375.12 x 375.12 pts
Page rot: 0
File size: 221278 bytes
Optimized: no
PDF version: 1.4
$ pdfinfo export-surface-scale-2.pdf
Creator: Chromium
Producer: Skia/PDF m83
CreationDate: Wed Nov 11 16:21:47 2020
ModDate: Wed Nov 11 16:21:47 2020
Tagged: no
UserProperties: no
Suspects: no
Form: none
JavaScript: no
Pages: 1
Encrypted: no
Page size: 750 x 750 pts
Page rot: 0
File size: 221264 bytes
Optimized: no
PDF version: 1.4