baodan/api/insurance/poster/render_validation.py

58 lines
2.1 KiB
Python
Raw Normal View History

2026-07-31 21:42:32 +08:00
"""浏览器合成海报的文件与尺寸校验。"""
import io
from PIL import Image
from insurance.poster.format_registry import list_poster_formats
class PosterRenderValidationError(ValueError):
"""最终海报文件不符合格式契约。"""
def validate_rendered_png(
image_bytes: bytes,
format_id: str,
expected_height: int | None = None,
) -> dict:
2026-07-31 21:42:32 +08:00
"""完整解码 PNG并验证其真实像素尺寸。"""
format_spec = next(
(item for item in list_poster_formats() if item["id"] == format_id),
None,
)
if not format_spec:
raise PosterRenderValidationError(f"不支持的海报格式: {format_id}")
try:
with Image.open(io.BytesIO(image_bytes)) as image:
image.verify()
with Image.open(io.BytesIO(image_bytes)) as image:
image.load()
width, height = image.size
image_format = image.format
except Exception as exc:
raise PosterRenderValidationError("PNG 文件无法解码或已经损坏") from exc
if image_format != "PNG":
raise PosterRenderValidationError("最终海报必须是 PNG 格式")
expected = format_spec["output"]
if width != expected["width"]:
raise PosterRenderValidationError(
f"海报宽度不匹配,应为 {expected['width']}px实际为 {width}px"
)
if expected["height"] is not None and height != expected["height"]:
raise PosterRenderValidationError(
f"海报尺寸不匹配,应为 {expected['width']}×{expected['height']}"
f"实际为 {width}×{height}"
)
if expected["height"] is None and expected_height is not None and height != expected_height:
raise PosterRenderValidationError(
f"长图自定义高度不匹配,应为 {expected['width']}×{expected_height}"
f"实际为 {width}×{height}"
)
2026-07-31 21:42:32 +08:00
if expected["height"] is None and not 1 <= height <= 32767:
raise PosterRenderValidationError("长图高度超出浏览器安全范围")
return {"width": width, "height": height, "format": image_format}