1 Commits

Author SHA1 Message Date
Stéphan Sainléger
fb7ff241f5 [FIX] hr_expense_report_merge_attachment: handle HEIC images uploaded as JPEG
Some iPhone users upload HEIC files with a ``.jpg`` extension. Odoo stores
the file as-is, but the mimetype is detected as ``image/...``, so the code
enters the image processing branch. ``reportlab``'s ``ImageReader`` relies
on ``PIL.Image.open()`` which cannot identify HEIC files without an explicit
plugin, causing a ``PIL.UnidentifiedImageError`` and a 500 error on report
download.

Fix by:
- Registering the ``pillow-heif`` opener at module load time (optional
  dependency, silently ignored if not installed) so that Pillow can decode
  HEIC/HEIF images.
- Converting any image to JPEG in memory via Pillow before passing it to
  ``ImageReader``, bypassing ``reportlab``'s own format detection entirely.
- Wrapping the image block in a ``try/except`` to gracefully skip
  attachments that cannot be decoded, consistent with the existing PDF
  error handling.
2026-05-15 13:36:43 +02:00

View File

@@ -9,6 +9,11 @@ from odoo import http
from reportlab.pdfgen import canvas from reportlab.pdfgen import canvas
from reportlab.lib.utils import ImageReader from reportlab.lib.utils import ImageReader
from reportlab.lib.pagesizes import letter, A4 from reportlab.lib.pagesizes import letter, A4
try:
from pillow_heif import register_heif_opener
register_heif_opener()
except ImportError:
pass
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -50,14 +55,25 @@ class Extension(main.ReportController):
except PyPDF2.utils.PdfReadError: # Case of non-pdf attachments except PyPDF2.utils.PdfReadError: # Case of non-pdf attachments
logger.info('Attachment %s cannot be merged in expense report'%(att.name,)) logger.info('Attachment %s cannot be merged in expense report'%(att.name,))
elif 'image/' in att.mimetype: elif 'image/' in att.mimetype:
packet = io.BytesIO() try:
can = canvas.Canvas(packet) from PIL import Image as PilImage
img = ImageReader(io.BytesIO(base64.b64decode(att.datas))) raw = base64.b64decode(att.datas)
can.drawImage(img, 0, 0, A4[0]*0.9, A4[1]*0.9, preserveAspectRatio=True) pil_img = PilImage.open(io.BytesIO(raw))
can.save() if pil_img.mode not in ('RGB', 'RGBA'):
packet.seek(0) pil_img = pil_img.convert('RGB')
attachment_reader = OdooPdfFileReader(packet) jpeg_buffer = io.BytesIO()
writer.addPage(attachment_reader.getPage(0)) pil_img.save(jpeg_buffer, format='JPEG')
jpeg_buffer.seek(0)
packet = io.BytesIO()
can = canvas.Canvas(packet)
img = ImageReader(jpeg_buffer)
can.drawImage(img, 0, 0, A4[0]*0.9, A4[1]*0.9, preserveAspectRatio=True)
can.save()
packet.seek(0)
attachment_reader = OdooPdfFileReader(packet)
writer.addPage(attachment_reader.getPage(0))
except Exception:
logger.info('Attachment %s cannot be merged in expense report', att.name)
# Write new pdf to res.data # Write new pdf to res.data
buffer = io.BytesIO() buffer = io.BytesIO()