【问题标题】:Insert image into Reportlab either from PIL image or StringIO从 PIL 图像或 StringIO 将图像插入 Reportlab
【发布时间】:2012-12-06 21:07:49
【问题描述】:

我正在尝试将条形码图像插入 Reportlab。我知道对此有很多问题,但所有问题都假设您已经在目录或文件系统中拥有图像文件。

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

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

支持 PIL/Java 1.4(Python/Java 映像库)支持的格式。

这是否意味着如果我传递 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

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

【问题讨论】:

    标签: python python-imaging-library reportlab


    【解决方案1】:

    我对建议的方法没有运气。

    检查 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
    

    进一步查看源码,发现jpeg_fh是reportlab.lib.utils中ImageReader类的一个属性。 ImageReader 接受 StringIO 和 PIL 图像。

    所以将 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, ...)
    

    【讨论】:

      【解决方案2】:

      重复声明“支持 PIL/Java 1.4(Python/Java 映像库)支持的格式”仅表示PIL 支持的数据格式受reportlab 支持(因为它使用PIL 读取他们)。

      现在,通过查看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)
      

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

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

      【讨论】:

      • 在 Python 3 中,使用 'from io import BytesIO' 而不是 'from cStringIO import StringIO'。和 'img2 = BytesIO()' 而不是 'img2 = StringIO()'
      • Image() 也可以采用response.raw,来自requests.get() 的“响应的类文件对象表示”。 Reportlab User Guide 现在承认 Image() 文件名参数可以是“类文件对象”。所以,唷,这不是一个未记录的功能。
      • Image(requests.get("http://example.com/foo.png", stream=True).raw, w, h) 例如。
      【解决方案3】:

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

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

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

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

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

      【讨论】:

        【解决方案4】:

        我发现两种不同的模式很有用,具体取决于我是在画布上绘图还是为 multiBuild 构建可流动的元素。我正在使用保存到 BytesIO 缓冲区的 matplotlib 数字,但我假设相同的缓冲区将满足您的条形码需求。

        对于两者:

        import io
        

        对于画布:

        from reportlab.lib.units import inch
        from reportlab.lib.utils import ImageReader
        
        # assume a proper Canvas object is instantiated as c
        
        buf = io.BytesIO(the_barcode.getvalue())
        buf.seek(0)
        c.drawImage(ImageReader(buf), 0.5*inch, 5.5*inch, )
        

        对于文档的可流动元素:

        from reportlab.platypus.flowables import Image
        
        # assume story is a list of flowable elements for multiBuild
        
        buf = io.BytesIO(the_barcode.getvalue())
        buf.seek(0)
        story.append(Image(buf))
        

        经过多次试验和错误,这些对我有用。

        【讨论】:

          猜你喜欢
          • 2013-07-28
          • 1970-01-01
          • 2016-05-29
          • 2015-12-31
          • 1970-01-01
          • 2014-09-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多