简体   繁体   中英

Including base64-encoded image in ReportLab-generated PDF

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?

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)

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)

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)

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM