You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
the plt.imsave(..) method creates a blank file using the .pdf/.eps format.
Code for reproduction
importmatplotlib.pyplotaspltimportnumpyasnpdata=np.arange(100*100).reshape(100, 100).astype(float)
plt.imsave("test.pdf", data, origin="lower", format="pdf") #same behavior also with eps instead of pdf
Maybe it is worth noting that the output is different for different backends, while still being completely undesired. Using python 2.7 on windows 8.1 (64bit) and matplotlib 2.0.2,
with the Agg or pdf backend, the output is a completely white pdf
while with Qt4Agg the output shows exactly one small pixel in the lower left corner
This is a pretty major regression, I am inclined to block 2.1 on this.
imsave uses Figure.figimage and mimage.FigureImage which I am not sure are used anywhere else. I have a vague memory of fixing a similar bug for imshow (which uses AxesImage) that probably just needs to be applied here.
Ooops, no. The pdf canvas is hardwired at dpi=72 for some reason, so renderer.get_canvas_width_height() doesn't gove the "correct" width/height either in the user-defined dpi. So relying on the pdf canvas size doesn't work for the clipping.
Frustratingly, for renderer.width,RenderAgg returns fig.width * fig.dpi
For MixedModeRenderer, renderer.width simply returns fig.width. This is the root of the bug since clip in the above is in pixels. I don't know how to fix the root problem without breaking lots of other things.
I guess the fundamental issue is that pdf and eps are not rasterized backends, so having their size defined by a number of pixels doesn't make sense to them fundamentally. Yes, I understand you can put a 100 dpi image on a 2"x2" pdf and it should be an image with 200x200 pixels, but I can understand why the design of the backends doesn't give you access to that information, and hence why FigureImage fails.
On Tue, Sep 19, 2017 at 4:28 PM, Jody Klymak ***@***.***> wrote:
Ooops, no. The pdf canvas is hardwired at dpi=72 for some reason, so
renderer.get_canvas_width_height() doesn't gove the "correct"
width/height either in the user-defined dpi. So relying on the pdf canvas
size doesn't work for the clipping.
Frustratingly, for renderer.width,RenderAgg returns fig.width * fig.dpi
For MixedModeRenderer, renderer.width simply returns fig.width. This is
the root of the bug since clip in the above is in pixels. I don't know
how to fix the root problem without breaking lots of other things.
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#9203 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AARy-Gg0bjQ2DJk2TlTCfUkkC-jq9ykOks5skCPmgaJpZM4PcMie>
.
@tacaswell If it's broken on 2.x already, I'm not sure you need to block 2.1 on this. That said, I think it depends heavily on how long it takes to fix. I wouldn't delay 2.1 past the end of the month for a bug that already exists in several releases.
as I understand it, pdf just has a dpi of 72 just as a reference point for
computing the extent of the page sizes and the extent of any rasterized
components. Everything is resampled on-the-fly when displayed, so the
particular dpi itself is irrelevant, so long as the writer and the reader
both aggree on the same dpi value.
OK, my apologies, maybe I'm not being clear because we don't have the right example...
I got onto this by trying to make the tests work. I think the example above works fine w/ my previous fix. However in general this example from the tests clips the 200x200 pixel image off at 144x144 if I render in pdf...
What happens is that figimage ignores the dpi call in figure and clips the figure to 72dpi when the MixedModeRenderer (pdf) is used.
But maybe thats OK? The docs for figimage say:
figimage complements the axes image
(:meth:`~matplotlib.axes.Axes.imshow`) which will be resampled
to fit the current axes. If you want a resampled image to
fill the entire figure, you can define an
:class:`~matplotlib.axes.Axes` with extent [0,0,1,1].
...so does the PDF backend simply always resample and fig.figimage is poorly defined for pdf? I guess I'd be OK w/ that logic.
@dopplershift If the 2.1 update will not be blocked until this issue is solved, I think it would at least be useful to show a warning when using the function for pdf files. Otherwise, a user can spend a long time in which he doesn't know, if he's doing something wrong, or matplotlib is not working properly.
I think that using the "dpi" keyword for savefig() of a pdf (and I think
eps/ps) should probably be considered an error for anything but 72. But,
I'll admit, I haven't considered the implications.
On Tue, Sep 19, 2017 at 5:07 PM, Jody Klymak ***@***.***> wrote:
OK, my apologies, maybe I'm not being clear because we don't have the
right example...
I got onto this by trying to make the tests work. I think the example
above works fine w/ my previous fix. However in general this example from
the tests clips the 200x200 pixel image off at 144x144 if I render in pdf...
import matplotlib
matplotlib.use('pdf')import matplotlib.pyplot as pltimport numpy as npfor suppressComposite in [False]:
fig = plt.figure(figsize=(2,2), dpi=100)
fig.suppressComposite = suppressComposite
x,y = np.ix_(np.arange(100.0)/100.0, np.arange(100.0)/100.0)
z = np.sin(x**2 + y**2 - x*y)
c = np.sin(20*x**2 + 50*y**2)
img = z + c/5
fig.figimage(img, xo=0, yo=0, origin='lower')
fig.figimage(img[::-1,:], xo=0, yo=100, origin='lower')
fig.figimage(img[:,::-1], xo=100, yo=0, origin='lower')
fig.figimage(img[::-1,::-1], xo=100, yo=100, origin='lower')
if suppressComposite:
fig.savefig('Boo.pdf', dpi=100)
fig.savefig('Boo.png', dpi=100)
else:
fig.savefig('Boo2.pdf', dpi=100)
#fig.savefig('Boo2.png', dpi=100)
What happens is that figimage ignores the dpi call in figure and clips
the figure to 72dpi when the MixedModeRenderer (pdf) is used.
But maybe thats OK? The docs for figimage say:
figimage complements the axes image
(:meth:`~matplotlib.axes.Axes.imshow`) which will be resampled
to fit the current axes. If you want a resampled image to
fill the entire figure, you can define an
:class:`~matplotlib.axes.Axes` with extent [0,0,1,1].
...so does the PDF backend simply always resample and fig.figimage is
poorly defined for pdf? I guess I'd be OK w/ that logic.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#9203 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AARy-L3MTEJcGbnV1JJCygAxjz-XbBWFks5skC0TgaJpZM4PcMie>
.
I will still argue that hardwiring the pdf backend to have dpi=72 is not practically useful, and makes it hard to deal with rasterized objects which need not have pixels-per-inch=72, which is how dpi is used in savefig (appropriately in my opinion).
The PDF spec makes lots of reference to 1/72" as the default size of UserUnit, but I don't think that has anything fundamentally to do with pixels-per-inch of rasterized objects.
To me, the fig.savefig(fname, dpi=100., fortmat='pdf') has always meant save any rasterized components at 100 pixels-per-inch. Which it does just fine as is. But, I'm not familiar enough with the backend machinery to understand why it needs to set the figure dpi to 72.
The reason that all of the the render has the dpi set to 72 is that it gets fed into the transform stack that converts data space coordinates to 'screen' space coordinates. In the case of Agg we then feed those coordinates to the resterizer which colors the pixels, in the case of pdf we just write the values out.
Individual artists have a 'rasterized' flag that determines if they get output to the vector backends as vector data or if it should be rasterized and then embedded into the vector output as an image. This can be useful to reduce ex 1M points in a scatter to something more manageable in the output.
and have the figure return different values for dpi depending on where I query fig.dpi. I guess I can see why the dpi gets set by the pdf canvas, so that the figure transform gives the right distances, but it means that the original dpi=200 gets ignored. As a user, I expect that this dpi will get carried through to the savefig statement so that I don't need to set the dpi at that stage. For instance:
If you pass 'dpi='figure'' to save fig (or set it in you rcparams) the dpi on the figure will be respected in savefig. As of 2.0 that is the default (via 016406a).
The reason for having a separate configuration for the savefig dpi (as far as I know) is lost to the sands of time. Changing the default to 'figure' is the next best thing to deprecating it.
dpi='figure' is certainly not the default on master using the PDF backend. Or rather, perhaps it is saving at 72 dpi, which is what the pdf backend sets the figure dpi to, but its certainly not respecting the dpi=10 that I used in the subplots call.
The fundamental issue is that FigureCanvasPdf.print_pdf does:
defprint_pdf(self, filename, **kwargs):
image_dpi=kwargs.get('dpi', 72) # dpi to use for imagesself.figure.set_dpi(72) # there are 72 pdf points to an inch
So maybe a simple change of image_dpi = kwargs.get('dpi', self.figure.dpi) would do the trick?
Activity
dstansby commentedon Sep 19, 2017
I can reproduce this on the
masterbranch and OSX. Seems to be present in2.0but not an issue in1.5.3ImportanceOfBeingErnest commentedon Sep 19, 2017
Maybe it is worth noting that the output is different for different backends, while still being completely undesired. Using python 2.7 on windows 8.1 (64bit) and matplotlib 2.0.2,
with the

Aggorpdfbackend, the output is a completely white pdfwhile with

Qt4Aggthe output shows exactly one small pixel in the lower left cornertacaswell commentedon Sep 19, 2017
This is a pretty major regression, I am inclined to block 2.1 on this.
imsaveusesFigure.figimageandmimage.FigureImagewhich I am not sure are used anywhere else. I have a vague memory of fixing a similar bug forimshow(which usesAxesImage) that probably just needs to be applied here.jklymak commentedon Sep 19, 2017
So,
FigureImageclips the image to the size of the figure, but was usingrenderer.widthandrenderer.height. If I change this toI think it gives the desired result
jklymak commentedon Sep 19, 2017
Possible fix? #9204
jklymak commentedon Sep 19, 2017
Ooops, no. The
pdfcanvas is hardwired at dpi=72 for some reason, sorenderer.get_canvas_width_height()doesn't gove the "correct" width/height either in the user-defineddpi. So relying on the pdf canvas size doesn't work for the clipping.Frustratingly, for
renderer.width,RenderAggreturnsfig.width * fig.dpiFor
MixedModeRenderer,renderer.widthsimply returnsfig.width. This is the root of the bug sinceclipin the above is in pixels. I don't know how to fix the root problem without breaking lots of other things.I guess the fundamental issue is that
pdfandepsare not rasterized backends, so having their size defined by a number of pixels doesn't make sense to them fundamentally. Yes, I understand you can put a 100 dpi image on a 2"x2" pdf and it should be an image with 200x200 pixels, but I can understand why the design of the backends doesn't give you access to that information, and hence whyFigureImagefails.WeatherGod commentedon Sep 19, 2017
jklymak commentedon Sep 19, 2017
Not sure what you mean. I can place a 200 dpi raster into a pdf w/o a problem, can't I?
dopplershift commentedon Sep 19, 2017
@tacaswell If it's broken on 2.x already, I'm not sure you need to block 2.1 on this. That said, I think it depends heavily on how long it takes to fix. I wouldn't delay 2.1 past the end of the month for a bug that already exists in several releases.
WeatherGod commentedon Sep 19, 2017
jklymak commentedon Sep 19, 2017
OK, my apologies, maybe I'm not being clear because we don't have the right example...
I got onto this by trying to make the tests work. I think the example above works fine w/ my previous fix. However in general this example from the tests clips the 200x200 pixel image off at 144x144 if I render in pdf...
What happens is that
figimageignores thedpicall infigureand clips the figure to 72dpi when theMixedModeRenderer(pdf) is used.But maybe thats OK? The docs for
figimagesay:...so does the PDF backend simply always resample and
fig.figimageis poorly defined forpdf? I guess I'd be OK w/ that logic.zimmerrol commentedon Sep 19, 2017
@dopplershift If the
2.1update will not be blocked until this issue is solved, I think it would at least be useful to show a warning when using the function forpdffiles. Otherwise, a user can spend a long time in which he doesn't know, if he's doing something wrong, ormatplotlibis not working properly.WeatherGod commentedon Sep 19, 2017
jklymak commentedon Sep 19, 2017
@WeatherGod
Test.pdfandTest2.pdflook very different! FWIW, this is the number-one reason I switched to matplotlib!Test.pdf
Test2.pdf
jklymak commentedon Sep 19, 2017
OK, re-opened #9204. New approach seems OK.
I will still argue that hardwiring the pdf backend to have dpi=72 is not practically useful, and makes it hard to deal with rasterized objects which need not have pixels-per-inch=72, which is how
dpiis used in savefig (appropriately in my opinion).jklymak commentedon Sep 20, 2017
The PDF spec makes lots of reference to 1/72" as the default size of
UserUnit, but I don't think that has anything fundamentally to do with pixels-per-inch of rasterized objects.To me, the
fig.savefig(fname, dpi=100., fortmat='pdf')has always meant save any rasterized components at 100 pixels-per-inch. Which it does just fine as is. But, I'm not familiar enough with the backend machinery to understand why it needs to set the figure dpi to 72.tacaswell commentedon Sep 20, 2017
The reason that all of the the render has the dpi set to 72 is that it gets fed into the transform stack that converts data space coordinates to 'screen' space coordinates. In the case of Agg we then feed those coordinates to the resterizer which colors the pixels, in the case of pdf we just write the values out.
Individual artists have a 'rasterized' flag that determines if they get output to the vector backends as vector data or if it should be rasterized and then embedded into the vector output as an image. This can be useful to reduce ex 1M points in a scatter to something more manageable in the output.
jklymak commentedon Sep 20, 2017
Right, but what is confusing is that I can do
and have the figure return different values for dpi depending on where I query
fig.dpi. I guess I can see why the dpi gets set by the pdf canvas, so that the figure transform gives the right distances, but it means that the original dpi=200 gets ignored. As a user, I expect that this dpi will get carried through to thesavefigstatement so that I don't need to set the dpi at that stage. For instance:completely ignores
dpi=10and saves the raster atdpi=100(my default). But the print statement returns10.Even more surprising, if I do
savefig('Boo10.png')it still ignores the dpi=10. I find this pretty confusing, but maybe its fine.tacaswell commentedon Sep 20, 2017
If you pass 'dpi='figure'' to save fig (or set it in you rcparams) the dpi on the figure will be respected in savefig. As of 2.0 that is the default (via 016406a).
The reason for having a separate configuration for the savefig dpi (as far as I know) is lost to the sands of time. Changing the default to
'figure'is the next best thing to deprecating it.jklymak commentedon Sep 20, 2017
dpi='figure'is certainly not the default on master using the PDF backend. Or rather, perhaps it is saving at 72 dpi, which is what the pdf backend sets the figure dpi to, but its certainly not respecting thedpi=10that I used in thesubplotscall.yield exactly the same files....
diff Boo10.pdf Boo72.pdfreturns no differences, nor doesdiff Boo10.pdf BooNone.pdf.Boo10.pdf
Boo72.pdf
BooNone.pdf
The fundamental issue is that
FigureCanvasPdf.print_pdfdoes:So maybe a simple change of
image_dpi = kwargs.get('dpi', self.figure.dpi)would do the trick?tacaswell commentedon Sep 24, 2017
Closed by #9204