Files
Foxel/services/processors/image_watermark.py
2025-08-24 18:49:00 +08:00

68 lines
2.6 KiB
Python

from .base import BaseProcessor
from typing import Dict, Any
from PIL import Image, ImageDraw, ImageFont
from io import BytesIO
from fastapi.responses import Response
from services.logging import LogService
class ImageWatermarkProcessor:
name = "图片水印"
supported_exts = ["jpg", "jpeg", "png", "bmp"]
config_schema = [
{"key": "text", "label": "水印文字", "type": "string", "required": True},
{
"key": "position",
"label": "位置",
"type": "select",
"required": False,
"default": "bottom-right",
"options": [
{"value": "top-left", "label": "左上"},
{"value": "center", "label": "居中"},
{"value": "bottom-right", "label": "右下"},
],
},
{"key": "font_size", "label": "字体大小", "type": "number", "required": False, "default": 24},
]
produces_file = True
async def process(self, input_bytes: bytes,path: str, config: Dict[str, Any]) -> Response:
text = config.get("text", "")
position = config.get("position", "bottom-right")
font_size = int(config.get("font_size", 24))
img = Image.open(BytesIO(input_bytes)).convert("RGBA")
watermark = Image.new("RGBA", img.size)
draw = ImageDraw.Draw(watermark)
try:
font = ImageFont.truetype("arial.ttf", font_size)
except Exception:
font = ImageFont.load_default()
w, h = img.size
try:
text_w, text_h = font.getsize(text)
except AttributeError:
bbox = draw.textbbox((0, 0), text, font=font)
text_w, text_h = bbox[2] - bbox[0], bbox[3] - bbox[1]
if position == "bottom-right":
xy = (w - text_w - 10, h - text_h - 10)
elif position == "top-left":
xy = (10, 10)
else:
xy = (w // 2 - text_w // 2, h // 2 - text_h // 2)
draw.text(xy, text, font=font, fill=(255, 255, 255, 128))
out = Image.alpha_composite(img, watermark)
buf = BytesIO()
out.convert("RGB").save(buf, format="JPEG")
await LogService.info(
"processor:image_watermark",
f"Watermarked image {path}",
details={"path": path, "config": config},
)
return Response(content=buf.getvalue(), media_type="image/jpeg")
PROCESSOR_TYPE = "image_watermark"
PROCESSOR_NAME = ImageWatermarkProcessor.name
SUPPORTED_EXTS = ImageWatermarkProcessor.supported_exts
CONFIG_SCHEMA = ImageWatermarkProcessor.config_schema
PROCESSOR_FACTORY = lambda: ImageWatermarkProcessor()