python - examples - Insertar imagen en Reportlab desde PIL image o StringIO
reportlab python 3 (3)
Estoy tratando de insertar una imagen de código de barras en Reportlab. Sé que hay muchas preguntas sobre esto, pero todas suponen que ya tiene el archivo de imagen en el directorio o en el sistema de archivos.
Debido a que Reportlab tiene problemas con los códigos de barras EAN13, decidí usar otro paquete llamado pyBarcode para generar la imagen para mí.
Inicialmente, guardé la imagen en una instancia de StringIO y la pasé directamente a reportlab.platypus.flowables.Image
pero eso no parece funcionar. Luego leo la documentación:
Los formatos admitidos por PIL / Java 1.4 (la biblioteca de imágenes de Python / Java) son compatibles.
¿Esto significa que si paso una imagen PIL, esto debería funcionar? Obtuve una excepción cuando probé lo siguiente:
>>> 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
De alguna manera, la imagen de PIL tampoco parece funcionar. ¿Qué debo pasar como primer argumento a la función Imagen de Reportlab si no tengo el nombre de archivo de la imagen (porque mis imágenes se crean en la memoria)?
Creo que lo que los documentos de PIL quieren decir es que está utilizando PIL internamente para procesar los datos de la imagen.
Por lo que veo en el código fuente, puede pasar un objeto de archivo directamente, así que, algo con un método de read()
:
https://github.com/ejucovy/reportlab/blob/master/src/reportlab/platypus/flowables.py#L314
Supongo que de alguna manera puedes envolver los datos de imagen en bruto en un objeto similar a un archivo (StringIO o similar).
EDIT: Supongo que eso es lo que estabas haciendo antes, lo siento. De todos modos, parece ser la forma correcta. Tal vez si nos dice cuál es el problema en ese caso, podremos resolverlo.
La declaración repetitiva "Los formatos compatibles con PIL / Java 1.4 (la biblioteca de imágenes de Python / Java) son compatibles" simplemente significa que los formatos de datos compatibles con PIL
son compatibles con reportlab
(ya que utiliza PIL
para leerlos).
Ahora, a partir del código reportlab.platypus.flowables.Image
, es posible ver que acepta un nombre de archivo o un objeto de archivo como entrada. Lo primero no es lo que quieres, así que concentrémonos en lo siguiente. Dijiste que StringIO
no parecía funcionar, pero funciona si te cuidas. Probablemente hiciste algo mal con esto, aquí hay dos formas correctas de usar 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)
El método 3 falla porque img3
ahora contiene los datos en bruto de la imagen, por lo que no tiene idea del formato real de estos datos. No hay ninguna razón para intentar usar este método para dicha tarea.
Si tiene datos sin procesar y conoce el modo de imagen de sus datos (''L'', ''RGB'', etc.) y también su ancho, altura, entonces puede usar un cuarto método (correcto) basado en PIL.Image.fromstring(...).save(mystrio, ''someformat'')
.
No tuve suerte con los métodos propuestos.
Verificando el código en pdfdoc.py muestra que AttributError resulta de tratar a StringIO como un nombre de archivo:
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
Además de verificar la fuente, muestra que jpeg_fh es un atributo de la clase ImageReader en reportlab.lib.utils. ImageReader acepta imágenes StringIO y PIL.
Así que envolver el StringIO en un ImageReader solucionó el problema para mí:
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, ...)