From 629a520201ea5a50c2d7eb29cd06ed12d5d0aa46 Mon Sep 17 00:00:00 2001 From: Mayorz Date: Wed, 4 Mar 2026 16:31:28 +0800 Subject: [PATCH] fix: convert webp images to png before excel embedding --- .../scripts/convert_excel_image_links.py | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/remote_sync/skills/dingtalk-excel-image-return/scripts/convert_excel_image_links.py b/remote_sync/skills/dingtalk-excel-image-return/scripts/convert_excel_image_links.py index 5363fec..e322ca1 100755 --- a/remote_sync/skills/dingtalk-excel-image-return/scripts/convert_excel_image_links.py +++ b/remote_sync/skills/dingtalk-excel-image-return/scripts/convert_excel_image_links.py @@ -2,6 +2,8 @@ from __future__ import annotations import argparse +import io +import mimetypes import re import ssl import tempfile @@ -15,6 +17,10 @@ from typing import Iterable from openpyxl import load_workbook from openpyxl.drawing.image import Image as XLImage 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 = { @@ -27,6 +33,8 @@ CONTENT_TYPE_TO_EXT = { } 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 = ( "图片链接", "商品图片", @@ -36,6 +44,8 @@ IMAGE_HINT_HEADERS = ( "image url", "图片", ) +mimetypes.add_type("image/webp", ".webp", strict=True) +mimetypes.add_type("image/webp", ".webp", strict=False) @dataclass @@ -235,6 +245,22 @@ def download_image(url: str, timeout: int, ssl_context): 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: 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: 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: temp_file.write(payload) temp_path = Path(temp_file.name)