从PIL图像或StringIO将图像插入到Reportlab中

时间:2021-07-11 00:26:15

I'm trying to insert a barcode image into Reportlab. I know there are a lot of questions asked on this, but all of them assume that you already have the image file in the directory or on the filesystem.

我正在尝试将条形码图像插入到Reportlab中。我知道有很多问题要问这个问题,但是他们都认为你已经在目录或文件系统中有了图像文件。

Due to the fact that Reportlab has issues with EAN13 barcodes, I decided to use another package called pyBarcode to generate the image for me.

由于Reportlab存在EAN13条形码问题,我决定使用另一个名为pyBarcode的包来为我生成图像。

Initially I saved the image in a StringIO instance and passed it directly to reportlab.platypus.flowables.Image but that didn't seem to work. Then I read the documentation:

最初我将图像保存在StringIO实例中并将其直接传递给reportlab.platypus.flowables.Image,但这似乎不起作用。然后我阅读了文档:

Formats supported by PIL/Java 1.4 (the Python/Java Imaging Library) are supported.

支持PIL / Java 1.4(Python / Java Imaging Library)支持的格式。

Does this mean that if I pass a PIL image, this should work? I got an exception when I tried the following:

这是否意味着如果我传递PIL图像,这应该有效吗?我尝试以下操作时遇到异常:

>>> import PIL
>>> from reportlab.platypus.flowables import Image
>>> fp = StringIO(the_barcode.getvalue())
>>> barcode_image = PIL.Image.open(fp)
>>> doc = SimpleDocTemplate('barcode.pdf')
>>> story = [Image(barcode_image)]
>>> Traceback (most recent call last):
  File "create.py", line 57, in <module>
    main()
  File "create.py", line 24, in main
    save_pdf(fp, STYLE, ART, COLOR, SIZE)
  File "create.py", line 28, in save_pdf
    fp = StringIO(fp.getvalue())
  File "/home/mark/.virtualenvs/barcode/local/lib/python2.7/site-packages/reportlab-2.6-py2.7-linux-i686.egg/reportlab/platypus/flowables.py", line 402, in __init__
    if not fp and os.path.splitext(filename)[1] in ['.jpg', '.JPG', '.jpeg', '.JPEG']:
  File "/home/mark/.virtualenvs/barcode/lib/python2.7/posixpath.py", line 95, in splitext
    return genericpath._splitext(p, sep, altsep, extsep)
  File "/home/mark/.virtualenvs/barcode/lib/python2.7/genericpath.py", line 91, in _splitext
    sepIndex = p.rfind(sep)
  File "/home/mark/.virtualenvs/barcode/local/lib/python2.7/site-packages/PIL/Image.py", line 512, in __getattr__
    raise AttributeError(name)
AttributeError: rfind

Somehow PIL Image doesn't seem to work either. What should I pass as the first argument to Reportlab's Image function if I don't have the filename of the image (because my images are created in memory)?

不知怎的,PIL Image似乎也不起作用。如果我没有图像的文件名(因为我的图像是在内存中创建的),我应该作为Reportlab的Image函数的第一个参数传递什么?

3 个解决方案

#1


9  

The repetitive declaration "Formats supported by PIL/Java 1.4 (the Python/Java Imaging Library) are supported" simply means that data formats supported by PIL are supported by reportlab (since it uses PIL to read them).

支持“由PIL / Java 1.4支持的格式(Python / Java成像库)”的重复声明“只是意味着报告支持PIL支持的数据格式(因为它使用PIL来读取它们)。

Now, from peeking in reportlab.platypus.flowables.Image code it is possible to see that it accepts either a filename or a file object as input. The former is not what you want, so let us focus on the later. You said StringIO didn't seem to work, but it does if you take some care. You probably did something wrong with it, here are two correct ways to use StringIO:

现在,通过查看reportlab.platypus.flowables.Image代码,可以看到它接受文件名或文件对象作为输入。前者不是你想要的,所以让我们专注于后者。你说StringIO似乎不起作用,但是如果你小心翼翼的话就行了。你可能做错了,这是使用StringIO的两种正确方法:

import sys
import PIL
from cStringIO import StringIO
from reportlab.platypus.flowables import Image

# Method 1
data = open(sys.argv[1]).read()
img1 = StringIO(data)

# Method 2
img2 = StringIO()
PIL.Image.open(sys.argv[2]).save(img2, 'PNG')
img2.seek(0)

# Method 3 (fails)
img3 = StringIO(PIL.Image.open(sys.argv[2]).tostring())

story = [Image(img1), Image(img2)]
#Image(img3)

The method 3 fails because img3 now holds the raw data of the image, so it has no idea about the actual format of this data. There is no reason to attempt to use this method for such task.

方法3失败,因为img3现在保存图像的原始数据,因此它不知道该数据的实际格式。没有理由尝试将此方法用于此类任务。

If you have raw data and you know the image mode of your data ('L', 'RGB', etc) and also its width, height, then you can use a fourth (correct) method based on PIL.Image.fromstring(...).save(mystrio, 'someformat').

如果您有原始数据并且您知道数据的图像模式('L','RGB'等)以及它的宽度,高度,那么您可以使用基于PIL.Image.fromstring的第四种(正确的)方法( ...)。save(mystrio,'someformat')。

#2


8  

I had no luck with the proposed methods.

我对提出的方法没有好运。

Checking the code in pdfdoc.py shows, that the AttributError results from treating the StringIO as a filename:

检查pdfdoc.py中的代码显示,AttributError是将StringIO视为文件名的结果:

    if source is None:
        pass # use the canned one.
    elif hasattr(source,'jpeg_fh'):
        self.loadImageFromSRC(source)   #it is already a PIL Image
    else:
        # it is a filename

Further checking the source, shows that jpeg_fh is an attribute of class ImageReader in reportlab.lib.utils. ImageReader accepts both StringIO and PIL images.

进一步检查源,显示jpeg_fh是reportlab.lib.utils中类ImageReader的一个属性。 ImageReader接受StringIO和PIL图像。

So wrapping the StringIO in an ImageReader solved the problem for me:

因此将StringIO包装在ImageReader中解决了我的问题:

import PIL
from reportlab.lib.utils import ImageReader

io_img = StringIO(data)
pil_img = PIL.Image.open(StringIO(data))

reportlab_io_img = ImageReader(io_img)
reportlab_pil_img = ImageReader(pil_img)

canvas.drawImage(reportlab_io_img, ...)
canvas.drawImage(reportlab_pil_img, ...)

#3


0  

I believe that what the PIL docs mean to say is that it is using PIL internally to process the image data.

我相信PIL文档的意思是它在内部使用PIL来处理图像数据。

From what I see in the source code, you can pass a file object directly, so, something with a read() method:

从我在源代码中看到的,您可以直接传递文件对象,因此,使用read()方法:

https://github.com/ejucovy/reportlab/blob/master/src/reportlab/platypus/flowables.py#L314

https://github.com/ejucovy/reportlab/blob/master/src/reportlab/platypus/flowables.py#L314

I guess you can somehow wrap the raw image data in a file-like object (StringIO or such).

我想你可以以某种方式将原始图像数据包装在类似文件的对象(StringIO等)中。

EDIT: I guess that's what you were doing before, sorry. Anyway, it seems to be the correct way. Maybe if you tell us what is the problem in that case, we'll be able to sort it out.

编辑:我想这就是你之前做过的事,抱歉。无论如何,这似乎是正确的方式。也许如果你告诉我们在这种情况下有什么问题,我们将能够解决它。

#1


9  

The repetitive declaration "Formats supported by PIL/Java 1.4 (the Python/Java Imaging Library) are supported" simply means that data formats supported by PIL are supported by reportlab (since it uses PIL to read them).

支持“由PIL / Java 1.4支持的格式(Python / Java成像库)”的重复声明“只是意味着报告支持PIL支持的数据格式(因为它使用PIL来读取它们)。

Now, from peeking in reportlab.platypus.flowables.Image code it is possible to see that it accepts either a filename or a file object as input. The former is not what you want, so let us focus on the later. You said StringIO didn't seem to work, but it does if you take some care. You probably did something wrong with it, here are two correct ways to use StringIO:

现在,通过查看reportlab.platypus.flowables.Image代码,可以看到它接受文件名或文件对象作为输入。前者不是你想要的,所以让我们专注于后者。你说StringIO似乎不起作用,但是如果你小心翼翼的话就行了。你可能做错了,这是使用StringIO的两种正确方法:

import sys
import PIL
from cStringIO import StringIO
from reportlab.platypus.flowables import Image

# Method 1
data = open(sys.argv[1]).read()
img1 = StringIO(data)

# Method 2
img2 = StringIO()
PIL.Image.open(sys.argv[2]).save(img2, 'PNG')
img2.seek(0)

# Method 3 (fails)
img3 = StringIO(PIL.Image.open(sys.argv[2]).tostring())

story = [Image(img1), Image(img2)]
#Image(img3)

The method 3 fails because img3 now holds the raw data of the image, so it has no idea about the actual format of this data. There is no reason to attempt to use this method for such task.

方法3失败,因为img3现在保存图像的原始数据,因此它不知道该数据的实际格式。没有理由尝试将此方法用于此类任务。

If you have raw data and you know the image mode of your data ('L', 'RGB', etc) and also its width, height, then you can use a fourth (correct) method based on PIL.Image.fromstring(...).save(mystrio, 'someformat').

如果您有原始数据并且您知道数据的图像模式('L','RGB'等)以及它的宽度,高度,那么您可以使用基于PIL.Image.fromstring的第四种(正确的)方法( ...)。save(mystrio,'someformat')。

#2


8  

I had no luck with the proposed methods.

我对提出的方法没有好运。

Checking the code in pdfdoc.py shows, that the AttributError results from treating the StringIO as a filename:

检查pdfdoc.py中的代码显示,AttributError是将StringIO视为文件名的结果:

    if source is None:
        pass # use the canned one.
    elif hasattr(source,'jpeg_fh'):
        self.loadImageFromSRC(source)   #it is already a PIL Image
    else:
        # it is a filename

Further checking the source, shows that jpeg_fh is an attribute of class ImageReader in reportlab.lib.utils. ImageReader accepts both StringIO and PIL images.

进一步检查源,显示jpeg_fh是reportlab.lib.utils中类ImageReader的一个属性。 ImageReader接受StringIO和PIL图像。

So wrapping the StringIO in an ImageReader solved the problem for me:

因此将StringIO包装在ImageReader中解决了我的问题:

import PIL
from reportlab.lib.utils import ImageReader

io_img = StringIO(data)
pil_img = PIL.Image.open(StringIO(data))

reportlab_io_img = ImageReader(io_img)
reportlab_pil_img = ImageReader(pil_img)

canvas.drawImage(reportlab_io_img, ...)
canvas.drawImage(reportlab_pil_img, ...)

#3


0  

I believe that what the PIL docs mean to say is that it is using PIL internally to process the image data.

我相信PIL文档的意思是它在内部使用PIL来处理图像数据。

From what I see in the source code, you can pass a file object directly, so, something with a read() method:

从我在源代码中看到的,您可以直接传递文件对象,因此,使用read()方法:

https://github.com/ejucovy/reportlab/blob/master/src/reportlab/platypus/flowables.py#L314

https://github.com/ejucovy/reportlab/blob/master/src/reportlab/platypus/flowables.py#L314

I guess you can somehow wrap the raw image data in a file-like object (StringIO or such).

我想你可以以某种方式将原始图像数据包装在类似文件的对象(StringIO等)中。

EDIT: I guess that's what you were doing before, sorry. Anyway, it seems to be the correct way. Maybe if you tell us what is the problem in that case, we'll be able to sort it out.

编辑:我想这就是你之前做过的事,抱歉。无论如何,这似乎是正确的方式。也许如果你告诉我们在这种情况下有什么问题,我们将能够解决它。