Reputation: 1043
I'm using pyfpdf in python to generate pdf files. I have a Base64 which I want to insert into a pdf file without having to save it as an image in my file system. But the pyfpdf image function only accepts file path.
fpdf.image(name, x = None, y = None, w = 0, h = 0, type = '', link = '')
Is there a way (hack) to directly insert base64 or buffered image from memory, without having to save into the file system beforehand? I even checked their source code on github and couldn't figure.
link : https://github.com/reingart/pyfpdf/tree/master/fpdf
Upvotes: 4
Views: 9432
Reputation: 193
I've been facing this issue lately and the answer from Uchiha Madara didn't work in my case, so I fixed it in a slightly different way. When I tried it with Uchiha's Method, I the same FileNotFound Error you do if you supply an image without any modification to your code ( without the load_resource function ). Since I really needed a solution and there was no way around, I looked into the module code which can be found in
C:/Users/user/AppData/Local/Programs/Python/Python38/Lib/site-packages/fpdf/fpdf.py
If you look around there for a bit, you notice that the image is imported via the _parsepng function. So, we need to edit this to accept a base64 data string.
Basically, what you need to do to fix it:
In the function, you need to add an elif at the top to check whether the "filename" contains a string indicating it's base64, and you need to import 2 new modules.
Copy & paste this code below the first if-Statement to check for a URL:
elif "data:image/png;base64" in name:
f = name.split('base64,')[1]
f = base64.b64decode(f)
f = io.BytesIO(f)
This just looks for the string which is typical for every base64-encoded image, and if it's there and decodes it.
You need to import the base64 and the io modules at the top of the script, so just do that via
import base64, io
Now just supply your base64 string as the file path like you would do normally, and it should work ( did on my tests with python 3.8 ).
Contact me if you have any questions, I hope I can help some people reading this in the future.
Upvotes: 0
Reputation: 1043
As @pvg mentioned in the comments, overriding load_resource function with your base64 functionality does the trick.
import base64,io
def load_resource(self, reason, filename):
if reason == "image":
if filename.startswith("http://") or filename.startswith("https://"):
f = BytesIO(urlopen(filename).read())
elif filename.startswith("data"):
f = filename.split('base64,')[1]
f = base64.b64decode(f)
f = io.BytesIO(f)
else:
f = open(filename, "rb")
return f
else:
self.error("Unknown resource loading reason \"%s\"" % reason)
EDIT :
This is a sample code to insert images into pdf. I commented some instructions in code.
from fpdf import FPDF
import os
import io
import base64
class PDF(FPDF):
def load_resource(self, reason, filename):
if reason == "image":
if filename.startswith("http://") or filename.startswith("https://"):
f = BytesIO(urlopen(filename).read())
elif filename.startswith("data"):
f = filename.split('base64,')[1]
f = base64.b64decode(f)
f = io.BytesIO(f)
else:
f = open(filename, "rb")
return f
else:
self.error("Unknown resource loading reason \"%s\"" % reason)
def sample_pdf(self,img,path):
self.image(img,h=70,w=150,x=30,y=100,type="jpg")
#make sure you use appropriate image format here jpg/png
pdf.output(path, 'F')
if __name__ == '__main__':
img = # pass your base64 image
# you can find sample base64 here : https://pastebin.com/CaZJ7n6s
pdf = PDF()
pdf.add_page()
pdf_path = # give path to where you want to save pdf
pdf.sample_pdf(img,pdf_path)
Upvotes: 3