Reputation: 137390
I am trying to decode base64-encoded image and put it into PDF I generate using ReportLab. I currently do it like that (image_data
is base64-encoded image, story
is already a ReportLab's story):
# There is some "story" I append every element
img_height = 1.5 * inch # max image height
img_file = tempfile.NamedTemporaryFile(mode='wb', suffix='.png')
img_file.seek(0)
img_file.write(image_data.decode('base64'))
img_file.seek(0)
img_size = ImageReader(img_file.name).getSize()
img_ratio = img_size[0] / float(img_size[1])
img = Image(img_file.name,
width=img_ratio * img_height,
height=img_height,
)
story.append(img)
and it works (although still looks ugly to me). I thought about getting rid of the temporary file (shouldn't file-like object do the trick?).
To get rid of the temporary file I tried to use StringIO
module, to create file-like object and pass it instead of file name:
# There is some "story" I append every element
img_height = 1.5 * inch # max image height
img_file = StringIO.StringIO()
img_file.seek(0)
img_file.write(image_data.decode('base64'))
img_file.seek(0)
img_size = ImageReader(img_file).getSize()
img_ratio = img_size[0] / float(img_size[1])
img = Image(img_file,
width=img_ratio * img_height,
height=img_height,
)
story.append(img)
But this gives me IOError with the following message: "cannot identify image file".
I know ReportLab uses PIL to read images different than jpg, but is there any way I can avoid creating named temporary files and do this only with file-like objects, without writing files to the disk?
Upvotes: 6
Views: 4446
Reputation: 1349
This solution works for me. I'm using Flask with Google App Engine.
from reportlab.platypus import Image
from reportlab.lib.units import mm
import cStringIO
from base64 import b64decode
story=[]
encoded_image = "...."
decoded_img = b64decode(encoded_image)
img_string = cStringIO.StringIO(decoded_img)
img_string.seek(0)
im = Image(img_string, 180*mm, 100*mm, kind='bound')
story.append(im)
I have received the image from the client and saved in the database:
from base64 import b64decode
image = request.files['image'].read()
encoded_image = b64encode(image)
Upvotes: 0
Reputation: 23871
You should wrap StringIO() by PIL.Image.open
, so simply img_size = ImageReader(PIL.Image.open(img_file)).getSize()
. Its actually a thin wrapper around Image.size, as Tommaso's answer suggests. Also, there is actually no need to calculate the desc size on your own, bound
mode of reportlab.Image could do it for you:
img_height = 1.5 * inch # max image height
img_file = StringIO.StringIO(image_data.decode('base64'))
img_file.seek(0)
img = Image(PIL.Image.open(img_file),
width=float('inf'),
height=img_height,
kind='bound')
)
story.append(img)
Upvotes: 2
Reputation: 656
This code is working for me without PIL, as the image is already a JPEG: raw just pulls the base64 string out of a dictionary. I just wrap the decoded "string" in a StringIO.
raw = element['photographs'][0]['jpeg']
photo = base64.b64decode(raw)
c.drawImage(ImageReader(StringIO.StringIO(photo)), 0.5*inch, self.y, height = self.PHOTOHEIGHT, preserveAspectRatio = True)
Upvotes: 0
Reputation: 12031
I am not familiar with ReportLab but if you can use PIL directly this will work:
...
img = Image.open(img_file)
width, height = img.size
...
you can have a look here for PIL Image class references
Upvotes: 0