49 lines
1.8 KiB
Python
49 lines
1.8 KiB
Python
"""浏览器合成海报的文件与尺寸校验。"""
|
||
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) -> dict:
|
||
"""完整解码 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 not 1 <= height <= 32767:
|
||
raise PosterRenderValidationError("长图高度超出浏览器安全范围")
|
||
|
||
return {"width": width, "height": height, "format": image_format}
|