04/27 Updated inspection PDF export with compress, low images to reduce file sizes
This commit is contained in:
+58
-1
@@ -17,6 +17,12 @@ import os
|
|||||||
import json
|
import json
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
try:
|
||||||
|
from PIL import Image as PILImage
|
||||||
|
_PIL_AVAILABLE = True
|
||||||
|
except ImportError:
|
||||||
|
_PIL_AVAILABLE = False
|
||||||
|
|
||||||
from reportlab.lib.pagesizes import letter
|
from reportlab.lib.pagesizes import letter
|
||||||
from reportlab.lib import colors
|
from reportlab.lib import colors
|
||||||
from reportlab.lib.units import inch
|
from reportlab.lib.units import inch
|
||||||
@@ -94,6 +100,51 @@ def _build_styles():
|
|||||||
STYLES = _build_styles()
|
STYLES = _build_styles()
|
||||||
|
|
||||||
|
|
||||||
|
# ── Image compression helper ──────────────────────────────────────────────────
|
||||||
|
|
||||||
|
# Max pixel dimension for any side of an image embedded in the PDF.
|
||||||
|
# Phone cameras produce 12–48 MP images; 800px is plenty for a printed report.
|
||||||
|
_IMG_MAX_PX = 800
|
||||||
|
# JPEG quality for re-encoded images (0–95). 55 is visually acceptable for
|
||||||
|
# a printed report and reduces a typical phone photo by ~90% vs the original.
|
||||||
|
_IMG_JPEG_QUALITY = 55
|
||||||
|
|
||||||
|
|
||||||
|
def _compress_image(src_path: str) -> io.BytesIO | None:
|
||||||
|
"""
|
||||||
|
Load an image from disk, resize it to fit within _IMG_MAX_PX on any side,
|
||||||
|
re-encode as JPEG at _IMG_JPEG_QUALITY, and return a BytesIO buffer.
|
||||||
|
|
||||||
|
Returns None if PIL is unavailable or the image cannot be processed,
|
||||||
|
signalling the caller to fall back to the original file path.
|
||||||
|
"""
|
||||||
|
if not _PIL_AVAILABLE:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
img = PILImage.open(src_path)
|
||||||
|
|
||||||
|
# Strip transparency — JPEG does not support alpha channels.
|
||||||
|
# Convert palette / RGBA / LA → RGB before saving.
|
||||||
|
if img.mode in ('RGBA', 'LA', 'P'):
|
||||||
|
background = PILImage.new('RGB', img.size, (255, 255, 255))
|
||||||
|
if img.mode == 'P':
|
||||||
|
img = img.convert('RGBA')
|
||||||
|
background.paste(img, mask=img.split()[-1] if img.mode in ('RGBA', 'LA') else None)
|
||||||
|
img = background
|
||||||
|
elif img.mode != 'RGB':
|
||||||
|
img = img.convert('RGB')
|
||||||
|
|
||||||
|
# Resize proportionally so neither dimension exceeds _IMG_MAX_PX.
|
||||||
|
img.thumbnail((_IMG_MAX_PX, _IMG_MAX_PX), PILImage.LANCZOS)
|
||||||
|
|
||||||
|
buf = io.BytesIO()
|
||||||
|
img.save(buf, format='JPEG', quality=_IMG_JPEG_QUALITY, optimize=True)
|
||||||
|
buf.seek(0)
|
||||||
|
return buf
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
# ── Header / footer callbacks ─────────────────────────────────────────────────
|
# ── Header / footer callbacks ─────────────────────────────────────────────────
|
||||||
|
|
||||||
def _on_page(canvas, doc, title, generated_at):
|
def _on_page(canvas, doc, title, generated_at):
|
||||||
@@ -417,7 +468,13 @@ def _form_fields_section(form_fields, form_data, static_folder):
|
|||||||
# File existence already verified in the skip gate above
|
# File existence already verified in the skip gate above
|
||||||
img_path = os.path.join(static_folder, val)
|
img_path = os.path.join(static_folder, val)
|
||||||
try:
|
try:
|
||||||
val_p = RLImage(img_path,
|
# Attempt to compress the image before embedding.
|
||||||
|
# _compress_image() resizes to _IMG_MAX_PX and re-encodes
|
||||||
|
# as JPEG at _IMG_JPEG_QUALITY — typically 85–95% smaller
|
||||||
|
# than the original phone photo.
|
||||||
|
compressed = _compress_image(img_path)
|
||||||
|
img_src = compressed if compressed is not None else img_path
|
||||||
|
val_p = RLImage(img_src,
|
||||||
width=min(cell_w - 12, 1.4 * inch),
|
width=min(cell_w - 12, 1.4 * inch),
|
||||||
height=1.0 * inch,
|
height=1.0 * inch,
|
||||||
kind='proportional')
|
kind='proportional')
|
||||||
|
|||||||
Reference in New Issue
Block a user