# 水印功能实现计划(服务端后处理合成,非 AI 模型加水印) ## 架构决策 - 水印在 **AI 出图返回后、落盘前** 由服务端合成(`run_suite` 里 `generator()` 返回字节之后、`storage.write_bytes` 之前)。预览(/media URL)与导出 ZIP 自然都是带水印图,所见即所得;不经过提示词/AI 模型。 - 开关与配置放插件"服务端设置"Popover,随生成请求下发(服务端不做 .env 配置项);持久化在 chrome.storage.local。 - 默认样式复刻 ozonSeller「图表处理」:图片水印 = `imgs/watermark.jpg` 圆形徽章(宽 15%、右下、透明度 30%);文字水印 = 默认文案 `xiongmaoyx`(白字黑描边、字号 6% 宽度、加粗)。位置本期固定右下,不做调节。 ## 服务端(server/) 1. **依赖与资产** - `requirements.txt` 加 `pillow` 并安装(venv 现无 PIL) - 复制 `ozon-seller-kit/web/imgs/watermark.jpg` → `server/assets/watermark.jpg`(200×200 JPEG) - `config.py` 加 `watermark_image_path`(默认指向上述资产) 2. **新模块 `services/watermark.py`** - `apply_watermark(data: bytes, opts: dict) -> bytes`:魔数识别 PNG/JPEG → PIL 打开转 RGBA 合成 → 按原格式保存(JPEG quality≈95) - 图片水印:资产中心裁方 → 圆形遮罩 → 缩放到图宽 15% → 按 opacity 合成 → 右下角贴入(边距 ≈1% 图宽) - 文字水印:字号 = 图宽 6%(下限 12px)、白色填充 + 黑色描边(alpha 0.55、描边宽 fontSize/8);字体回退链 PingFang → Hiragino Sans GB → STHeiti → Pillow 默认(模块级缓存首次命中) - 容错:字体/资产缺失时 log warning 并返回原图,绝不阻断生图 3. **协议与流转** - `schemas.py`:新增 `WatermarkOptions`(`enabled=False, type='image'|'text', text='xiongmaoyx', opacity=30`),`GenerateRequest` 加 `watermark` 字段 - `tasks.py`:`Task` 加 `watermark: dict | None` - `api/generate.py`:`create_task` 透传 - `generator.py` `run_suite`:落盘前 `if watermark enabled: data = apply_watermark(...)` ## 插件端(extension/) 4. **设置与请求** - `src/storage/settings.ts`:`BackendSettings` 加 `watermark: { enabled: false, type: 'image', text: 'xiongmaoyx', opacity: 30 }`;loadSettings 对该子对象做深合并(兼容老数据) - `src/api/client.ts`:`GeneratePayload` 加 `watermark?`,`buildGeneratePayload` 透传 5. **UI(App.tsx settingsPopup)** - 加水印设置块:开启 checkbox、类型 pills(图片/文字)、文字内容 input(仅文字类型显示)、透明度 number(0–100 步进 5,带 %);Popover 宽度 260→300 - `startGenerate` 的 config 在开启时带 `watermark`,关闭不下发 ## 验证 - 烟测:对已有生成图字节分别跑图片/文字两种水印(含透明度边界),人工查看合成效果 - 前端 `tsc --noEmit`;README 补充说明 ## 影响面 仅生成图被处理;参考图/上传图不动。水印合成失败自动跳过不阻断生成。开关关闭时生成的图保持干净,导出即所见。