Writing matplotlib figure to a buffer, then reading as a file for embedding an image in a PDF

烂漫一生 提交于 2020-02-02 13:40:13

问题


I'm trying to save several matplotlib figures into specific locations in a PDF being created with FPDF. I'd prefer not to have to dump these figures to files and then pull them back in. The better solution seems to be writing them to a buffer, then reading them back in, but I'm not sure that's possible.

Simplified code:

import pandas as pd
import matplotlib.pyplot as plt
from fpdf import FPDF
from io import BytesIO
import base64

# create figure
fig = plt.figure()

# add plot
ax = pd.Series( [1,3,2] ).plot()

# create bytesio object
bio = BytesIO()        

# save figure to bytesio
fig.savefig(bio, format="png", bbox_inches='tight')

# this works to save the figure to a file
with open("myfile.png", "wb") as file:
    file.write(bio.getvalue())

# create a PDF and set some attributes    
pdf = FPDF(orientation="L", unit="in", format="letter")
pdf.set_font('Arial', 'B', 14)
pdf.add_page()

# try to read the bytesio object as though it's a file
# pdf.image() expects a file name or a URL: https://pyfpdf.readthedocs.io/en/latest/reference/image/index.html
# trying to create a data url with the base64-encoded data inside it

pdf.image( name='data://image/png;base64,' + base64.b64encode(bio.getvalue()).decode() , x=1, y=1, w=3.5, type='png')

# save pdf
pdf.output("mypdf.pdf")

Full stack trace:

---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
<ipython-input-17-0ec65d0f583e> in <module>()
     30 # trying to create a data url with the base64-encoded data inside it
     31 
---> 32 pdf.image( name='data://image/png;base64,' + base64.b64encode(bio.getvalue()).decode() , x=1, y=1, w=3.5, type='png')
     33 
     34 # save pdf

~\AppData\Local\Continuum\anaconda3\lib\site-packages\fpdf\fpdf.py in wrapper(self, *args, **kwargs)
    148                 self.error("No page open, you need to call add_page() first")
    149             else:
--> 150                 return fn(self, *args, **kwargs)
    151         return wrapper
    152 

~\AppData\Local\Continuum\anaconda3\lib\site-packages\fpdf\fpdf.py in image(self, name, x, y, w, h, type, link)
    969                 info=self._parsejpg(name)
    970             elif(type=='png'):
--> 971                 info=self._parsepng(name)
    972             else:
    973                 #Allow for additional formats

~\AppData\Local\Continuum\anaconda3\lib\site-packages\fpdf\fpdf.py in _parsepng(self, name)
   1770                f = urlopen(name)
   1771         else:
-> 1772             f=open(name,'rb')
   1773         if(not f):
   1774             self.error("Can't open image file: "+name)

FileNotFoundError: [Errno 2] No such file or directory: 'data://image/png;base64,iVBORw0KGgoAAAANSU  #snipped for length#

Since pdf.image() can take a URL, I'd like to construct a data URL that doesn't actually try to open up a file. It would be like embedding an image inline in an HTML page. That sort of idea. I've tried several iterations of getvalue() and encode() and decode(), but I'm not getting the right combination.

I am successfully writing to a file in this example to make sure my BytesIO object has the right data in it, but dumping it into pdf.image() is not working.

来源:https://stackoverflow.com/questions/54376569/writing-matplotlib-figure-to-a-buffer-then-reading-as-a-file-for-embedding-an-i

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!