fix: convert webp images to png before excel embedding

This commit is contained in:
Mayorz 2026-03-04 16:31:28 +08:00
parent 20d45b5f1d
commit 629a520201

View File

@ -2,6 +2,8 @@
from __future__ import annotations from __future__ import annotations
import argparse import argparse
import io
import mimetypes
import re import re
import ssl import ssl
import tempfile import tempfile
@ -15,6 +17,10 @@ from typing import Iterable
from openpyxl import load_workbook from openpyxl import load_workbook
from openpyxl.drawing.image import Image as XLImage from openpyxl.drawing.image import Image as XLImage
from openpyxl.utils import column_index_from_string, get_column_letter from openpyxl.utils import column_index_from_string, get_column_letter
try:
from PIL import Image as PILImage
except Exception: # noqa: BLE001
PILImage = None
CONTENT_TYPE_TO_EXT = { CONTENT_TYPE_TO_EXT = {
@ -27,6 +33,8 @@ CONTENT_TYPE_TO_EXT = {
} }
SUPPORTED_EXTENSIONS = {".png", ".jpg", ".jpeg", ".webp", ".bmp", ".gif"} SUPPORTED_EXTENSIONS = {".png", ".jpg", ".jpeg", ".webp", ".bmp", ".gif"}
EXCEL_SAFE_EXTENSIONS = {".png", ".jpg", ".jpeg", ".bmp", ".gif"}
UNSAFE_OR_UNKNOWN_EXTENSIONS = {".webp", ".tif", ".tiff", ".heic", ".avif"}
IMAGE_HINT_HEADERS = ( IMAGE_HINT_HEADERS = (
"图片链接", "图片链接",
"商品图片", "商品图片",
@ -36,6 +44,8 @@ IMAGE_HINT_HEADERS = (
"image url", "image url",
"图片", "图片",
) )
mimetypes.add_type("image/webp", ".webp", strict=True)
mimetypes.add_type("image/webp", ".webp", strict=False)
@dataclass @dataclass
@ -235,6 +245,22 @@ def download_image(url: str, timeout: int, ssl_context):
return data, guess_extension(url, content_type) return data, guess_extension(url, content_type)
def to_excel_safe_image(payload: bytes, extension: str) -> tuple[bytes, str]:
ext = (extension or "").lower()
if ext in EXCEL_SAFE_EXTENSIONS and ext not in UNSAFE_OR_UNKNOWN_EXTENSIONS:
return payload, ext
if PILImage is None:
return payload, ".png"
with PILImage.open(io.BytesIO(payload)) as image:
if image.mode not in {"RGB", "RGBA"}:
image = image.convert("RGBA")
output = io.BytesIO()
image.save(output, format="PNG")
return output.getvalue(), ".png"
def pixels_to_excel_width(px: int) -> float: def pixels_to_excel_width(px: int) -> float:
return max(12.0, round((px + 12) / 7, 2)) return max(12.0, round((px + 12) / 7, 2))
@ -282,6 +308,7 @@ def process_sheet(sheet, args: argparse.Namespace, ssl_context, temp_files: list
try: try:
payload, extension = download_image(url, args.timeout, ssl_context) payload, extension = download_image(url, args.timeout, ssl_context)
payload, extension = to_excel_safe_image(payload, extension)
with tempfile.NamedTemporaryFile(delete=False, suffix=extension) as temp_file: with tempfile.NamedTemporaryFile(delete=False, suffix=extension) as temp_file:
temp_file.write(payload) temp_file.write(payload)
temp_path = Path(temp_file.name) temp_path = Path(temp_file.name)