1
0

feat: 添加图片适配模式支持

- 支持四种图片适配模式:stretch、contain、cover、center
- 支持背景色填充功能(contain 和 center 模式)
- 支持文档级 DPI 配置(metadata.dpi)
- PPTX 渲染器集成 Pillow 实现高质量图片处理
- HTML 渲染器使用 CSS object-fit 实现相同效果
- 添加完整的单元测试、集成测试和端到端测试
- 更新 README 文档和架构文档
- 模块化设计:utils/image_utils.py 图片处理工具模块
- 添加图片配置验证器:validators/image_config.py
- 向后兼容:未指定 fit 时默认使用 stretch 模式
This commit is contained in:
2026-03-04 10:29:21 +08:00
parent 16ca9d77cd
commit 19d6661381
32 changed files with 2310 additions and 57 deletions

View File

@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-03-04

View File

@@ -0,0 +1,260 @@
# 图片适配模式技术设计
## Context
### 当前状态
当前系统图片渲染逻辑非常简单:直接使用 python-pptx 的 `add_picture()` 方法,传入 box 的宽高参数。python-pptx 会自动将图片拉伸到指定尺寸,无法保持宽高比,也无法进行居中或裁剪处理。
```python
# 当前实现 (pptx_renderer.py)
def _render_image(self, slide, elem: ImageElement, base_path):
x, y, w, h = [Inches(v) for v in elem.box]
slide.shapes.add_picture(str(img_path), x, y, width=w, height=h)
```
### 约束条件
- 必须保持向后兼容:未指定 `fit` 参数时,行为与当前一致
- PPTX 渲染使用英寸单位,图片处理需要像素单位
- HTML 预览需要与 PPTX 渲染保持一致的视觉效果
- DPI 配置需要同时影响两个渲染器
### 利益相关者
- 最终用户:需要多样化的图片适配选项
- HTML 预览用户:期望预览效果与最终 PPTX 一致
## Goals / Non-Goals
**Goals:**
- 支持四种图片适配模式stretch、contain、cover、center
- 支持留白区域的背景色填充
- 支持可配置的 DPI 转换
- HTML 预览与 PPTX 渲染效果一致
- 向后兼容现有 YAML 文件
**Non-Goals:**
- 不支持图片的旋转、翻转等变换
- 不支持图片滤镜、水印等高级效果
- 不支持 fill平铺模式
- 不支持自适应 box 尺寸box 必需,不是可选)
## Decisions
### 决策 1: 使用 Pillow 作为图片处理库
**选择理由:**
- Pillow 是 Python 生态中最成熟的图片处理库,从 PIL 发展而来
- 内置 `ImageOps` 模块,直接提供 `contain()``cover()` 方法,与我们的需求完全匹配
- API 简洁直观,社区活跃,文档完善
- 轻量级,专注图片处理,不像 OpenCV 那样重量级
**替代方案考虑:**
| 方案 | 优势 | 劣势 | 结论 |
|------|------|------|------|
| Pillow | API 简洁ImageOps 直接匹配需求 | 需要额外依赖 | ✅ 选择 |
| OpenCV | 功能强大,性能优异 | API 复杂BGR 颜色顺序需转换,过度设计 | ❌ |
| Wand | 功能丰富 | 依赖 ImageMagick安装复杂跨平台问题多 | ❌ |
| 原生实现 | 无额外依赖 | 需要手动实现所有算法,容易出错 | ❌ |
### 决策 2: DPI 作为文档级配置
**选择:**`metadata.dpi` 中配置,默认值 96
**理由:**
- DPI 影响整个文档的所有图片,不应在元素级别配置
- 96 是 Web 标准 DPI与当前 HTML 渲染器一致
- 简洁的配置方式,符合 YAML 声明式风格
**替代方案:** `metadata.image.dpi`(更结构化,但过于复杂)
### 决策 3: PPTX 和 HTML 使用不同的实现方式
**PPTX 实现:**
- 使用 Pillow 处理图片(缩放、裁剪)
- 计算居中位置
- 添加背景色画布(如有需要)
- 使用 python-pptx 添加处理后的图片
**HTML 实现:**
- 使用 CSS `object-fit` 属性
- `stretch``object-fit: fill`
- `contain``object-fit: contain`
- `cover``object-fit: cover`
- `center``object-fit: none` + `object-position: center`
- 背景色使用 CSS `background-color`
**理由:** 两个平台的原生能力不同,使用各自的最佳实践,但确保视觉效果一致
### 决策 4: 图片适配模式算法设计
```
┌─────────────────────────────────────────────────────────────────────┐
│ 图片适配算法流程 │
└─────────────────────────────────────────────────────────────────────┘
输入: img (PIL.Image), box_size (width, height), fit, background
1. stretch 模式
─────────────────────────────────────────────────────────────────
直接使用 img.resize(box_size),不考虑宽高比
2. contain 模式
─────────────────────────────────────────────────────────────────
result = ImageOps.contain(img, box_size)
# result.size <= box_size保持宽高比
if background:
canvas = 创建 box_size 画布,填充背景色
canvas.paste(result, 居中位置)
result = canvas
3. cover 模式
─────────────────────────────────────────────────────────────────
result = ImageOps.cover(img, box_size)
# result.size == box_size保持宽高比裁剪超出部分
4. center 模式
─────────────────────────────────────────────────────────────────
result = img # 不缩放
if img.width > box_size.width or img.height > box_size.height:
result = 裁剪到 box_size居中裁剪
if background:
canvas = 创建 box_size 画布,填充背景色
canvas.paste(result, 居中位置)
result = canvas
输出: result (PIL.Image), display_size, display_position
```
### 决策 6: 模块化设计
```
utils/image_utils.py
├── inches_to_pixels(inches, dpi) -> float
├── pixels_to_inches(pixels, dpi) -> float
├── calculate_contain_size(img_size, box_size) -> tuple
├── calculate_cover_size(img_size, box_size) -> tuple
├── calculate_center_offset(img_size, box_size) -> tuple
└── apply_fit_mode(img, box_size, fit, background) -> PIL.Image
validators/image_config.py (新建)
├── validate_fit_value(fit) -> List[ValidationIssue]
└── validate_fit_box_dependency(elem) -> List[ValidationIssue]
```
**理由:** 单一职责原则,图片处理逻辑与渲染逻辑分离,便于测试和复用
### 决策 5: 模块化设计
```
utils/image_utils.py
├── inches_to_pixels(inches, dpi) -> float
├── pixels_to_inches(pixels, dpi) -> float
├── calculate_contain_size(img_size, box_size) -> tuple
├── calculate_cover_size(img_size, box_size) -> tuple
├── calculate_center_offset(img_size, box_size) -> tuple
└── apply_fit_mode(img, box_size, fit, background) -> PIL.Image
validators/image_config.py (新建)
├── validate_fit_value(fit) -> List[ValidationIssue]
└── validate_background_color(color) -> List[ValidationIssue]
```
**理由:** 单一职责原则,图片处理逻辑与渲染逻辑分离,便于测试和复用
**注意:** 由于 box 参数为必填,不存在"没有指定 box"的情况,因此 `validate_fit_box_dependency` 验证器已移除。
## Risks / Trade-offs
### 风险 1: Pillow 依赖增加
**风险:** 新增外部依赖可能增加安装复杂度
**缓解措施:**
- Pillow 是 Python 生态标准库,安装简单(`pip install pillow`
- 在 pyproject.toml 中不指定版本号,使用最新稳定版
- 在 README 中明确说明依赖变更
### 风险 2: PPTX 和 HTML 渲染效果不完全一致
**风险:** 两个平台实现方式不同,可能在边缘情况下效果有差异
**缓解措施:**
- 核心算法(尺寸计算)使用相同的 Python 函数
- 编写集成测试,对比两种渲染器的输出
- 在文档中说明已知差异(如抗锯齿算法不同)
### 风险 3: 大图片处理性能问题
**风险:** Pillow 处理大图片可能较慢,占用内存
**缓解措施:**
- 仅在需要时才处理图片contain/cover/center 模式)
- stretch 模式直接使用 python-pptx 的原生处理,不经过 Pillow
- 文档中建议用户使用适当尺寸的图片
### 风险 4: DPI 配置不当导致尺寸错误
**风险:** 用户设置的 DPI 与实际使用场景不符,导致图片尺寸不符合预期
**缓解措施:**
- 默认值 96 适用于大多数场景
- 在 README 中说明 DPI 的含义和影响
- 验证器检查 DPI 值是否合理(如 72-300 之间)
## Migration Plan
### 部署步骤
1. **代码变更**
- 添加 Pillow 依赖到 pyproject.toml
- 创建 `utils/image_utils.py`
- 创建 `validators/image_config.py`
- 更新 `core/elements.py` 的 ImageElement
- 更新 `renderers/pptx_renderer.py`
- 更新 `renderers/html_renderer.py`
2. **测试**
- 单元测试image_utils 的各个函数
- 单元测试validators 的图片配置验证
- 集成测试:四种 fit 模式的 PPTX 渲染
- 集成测试:四种 fit 模式的 HTML 渲染
- 端到端测试:完整 YAML 转换流程
3. **文档更新**
- 更新 README.md添加图片适配模式说明
- 更新 README_DEV.md添加架构说明
### 回滚策略
- 如果发现严重问题,可以回退到之前版本
- 向后兼容设计确保未指定 fit 参数的 YAML 文件仍能正常工作
- 回滚后用户只需删除 fit 和 background 参数即可
## Open Questions
### Q1: 是否需要在图片处理失败时提供降级方案?
**决策:** 图片处理失败时抛出 ERROR 级别错误,让用户修复图片问题,不提供降级方案。
**理由:** 保持简单明确,图片问题应该由用户在源头解决,而不是掩盖问题。
### Q2: background 参数是否支持渐变色?
**决策:** background 参数仅支持纯色,不支持渐变色。
**理由:** 简化实现,满足绝大多数使用场景。如有后续需求,可以作为独立功能添加。
### Q3: 是否需要支持图片质量设置?
**决策:** 使用 Pillow 的最高质量重采样算法LANCZOS不向用户暴露配置选项。
**理由:** 演示文稿场景下图片质量优先于处理速度,使用最高质量算法避免用户困惑。

View File

@@ -0,0 +1,44 @@
# 图片适配模式支持
## Why
当前图片元素渲染仅支持简单的拉伸模式,图片会被强制缩放到 box 指定的尺寸,导致图片变形或宽高比失真。实际使用中,用户需要保持图片宽高比、居中显示、填充裁剪等多种适配模式,以满足不同场景的视觉需求。
## What Changes
- 新增图片 `fit` 参数,支持四种适配模式:`stretch`(拉伸)、`contain`(包含)、`cover`(覆盖)、`center`(居中)
- 新增图片 `background` 参数,支持指定留白区域的填充颜色(默认透明)
- 新增文档级 `dpi` 配置(`metadata.dpi`),用于像素与英寸的转换,默认值为 96
- 引入 Pillow 库进行图片处理,利用其 ImageOps 模块实现各种适配模式
- 保持向后兼容:未指定 `fit` 时默认使用 `stretch` 模式(当前行为)
## Capabilities
### New Capabilities
- `image-fit-modes`: 图片元素适配模式处理,支持 stretch、contain、cover、center 四种模式,以及背景色填充和 DPI 配置
### Modified Capabilities
- `element-rendering`: 扩展图片元素渲染能力,新增 `fit``background` 参数支持
- `html-rendering`: 同步扩展 HTML 预览的图片渲染能力,支持 `fit``background` 参数,使用 CSS object-fit 实现
## Impact
### 依赖变更
- 新增 Pillow 依赖(不指定版本号,使用最新版)
### 代码变更
- `core/elements.py`: ImageElement 新增 `fit``background` 字段
- `renderers/pptx_renderer.py`: 重写 `_render_image()` 方法,集成 Pillow 图片处理
- `renderers/html_renderer.py`: 更新 `_render_image()` 方法,使用 CSS object-fit 实现适配模式
- `validators/`: 新增图片参数验证器fit 值校验、background 颜色校验)
- `utils/image_utils.py`: 新增图片处理工具模块(像素转换、尺寸计算、居中定位)
### API 变更
- YAML 语法扩展:
- `metadata` 层级新增可选的 `dpi` 字段
- 图片元素新增可选的 `fit` 字段
- 图片元素新增可选的 `background` 字段
### 文档变更
- `README.md`: 新增图片适配模式使用说明和示例
- `README_DEV.md`: 新增图片处理架构说明

View File

@@ -0,0 +1,76 @@
# Element Rendering - Delta Spec
本 spec 是对 `openspec/specs/element-rendering/spec.md` 的增量修改。
## MODIFIED Requirements
### Requirement: 系统必须支持图片元素渲染
系统 SHALL 将 YAML 中定义的图片元素渲染为 PPTX 图片对象,支持 `fit``background` 参数控制图片适配模式。
#### Scenario: 渲染本地图片
- **WHEN** 元素定义为 `{type: image, src: "images/logo.png", box: [2, 3, 4, 3]}`
- **THEN** 系统从指定路径加载图片,在 (2, 3) 位置渲染为 4×3 英寸大小
- **AND** 使用默认的 `stretch` 模式(向后兼容)
#### Scenario: 使用 fit 模式渲染图片
- **WHEN** 元素定义为 `{type: image, src: "photo.jpg", box: [1, 1, 4, 3], fit: contain}`
- **THEN** 系统使用 `contain` 模式渲染图片
- **AND** 保持图片宽高比,完整显示在 box 内
#### Scenario: 使用背景色渲染图片
- **WHEN** 元素定义为 `{type: image, src: "photo.png", box: [1, 1, 4, 3], fit: contain, background: "#f0f0f0"}`
- **THEN** 系统使用 `contain` 模式渲染图片
- **AND** 用 #f0f0f0 颜色填充留白区域
#### Scenario: 图片文件不存在时报错
- **WHEN** 图片 src 指向不存在的文件路径
- **THEN** 系统抛出错误,明确指出图片文件未找到
#### Scenario: 图片格式不支持时报错
- **WHEN** 图片文件格式不被 Pillow 支持
- **THEN** 系统抛出错误,提示图片格式不支持,并列出支持的格式
#### Scenario: 图片处理失败时报错
- **WHEN** Pillow 处理图片时发生异常(如文件损坏)
- **THEN** 系统抛出 ERROR 级别错误,不降级到其他模式
#### Scenario: 相对路径处理
- **WHEN** 图片 src 使用相对路径 `"assets/images/logo.png"`
- **THEN** 系统基于演示文稿文件所在目录解析相对路径
#### Scenario: 使用 DPI 配置渲染图片
- **WHEN** metadata 定义了 `dpi: 120` 且图片需要处理
- **THEN** 系统使用该 DPI 值进行像素与英寸的转换
#### Scenario: fit 参数值无效时报错
- **WHEN** `fit` 参数值不是 stretch、contain、cover、center 之一
- **THEN** 系统抛出 ERROR并列出有效值
#### Scenario: background 参数颜色格式无效时报错
- **WHEN** `background` 值不是有效的颜色格式
- **THEN** 系统抛出 ERROR提示颜色格式应为 #RRGGBB#RGB
### Requirement: 图片元素的 box 参数必须存在
系统 SHALL 要求图片元素必须包含 `box` 参数,否则验证失败。
#### Scenario: 缺少 box 参数时报错
- **WHEN** 图片元素未定义 `box` 参数
- **THEN** 系统抛出 ERROR提示 box 参数为必需
#### Scenario: box 参数格式正确
- **WHEN** 图片元素定义了 `box: [1, 2, 4, 3]`
- **THEN** 系统验证通过,将 box 用于图片定位和尺寸

View File

@@ -0,0 +1,83 @@
# HTML Rendering - Delta Spec
本 spec 是对 `openspec/specs/html-rendering/spec.md` 的增量修改。
## MODIFIED Requirements
### Requirement: 系统必须渲染图片元素
系统 SHALL 将 YAML 中的图片元素转换为 HTML `<img>` 标签,支持 `fit``background` 参数,使用 CSS object-fit 实现适配模式。
#### Scenario: 渲染本地图片
- **WHEN** 元素定义为 `{type: image, src: "images/logo.png", box: [2, 3, 4, 3]}`
- **THEN** 系统生成 `<img>` 标签src 为图片的文件路径,位置为 (192px, 288px),尺寸为 384x288 像素
- **AND** 使用默认的 CSS 样式 `object-fit: fill`(等同于 stretch 模式)
#### Scenario: 使用 fit 模式渲染图片
- **WHEN** 元素定义为 `{type: image, src: "photo.jpg", box: [1, 1, 4, 3], fit: contain}`
- **THEN** 系统应用 CSS `object-fit: contain`
- **AND** 保持图片宽高比,完整显示在 box 内
#### Scenario: fit 模式与 CSS object-fit 映射
- **WHEN** `fit` 参数为 `stretch`
- **THEN** 系统应用 CSS `object-fit: fill`
- **WHEN** `fit` 参数为 `contain`
- **THEN** 系统应用 CSS `object-fit: contain`
- **WHEN** `fit` 参数为 `cover`
- **THEN** 系统应用 CSS `object-fit: cover`
- **WHEN** `fit` 参数为 `center`
- **THEN** 系统应用 CSS `object-fit: none``object-position: center`
#### Scenario: 使用背景色渲染图片
- **WHEN** 元素定义为 `{type: image, src: "photo.png", box: [1, 1, 4, 3], fit: contain, background: "#f0f0f0"}`
- **THEN** 系统为图片容器添加 CSS `background-color: #f0f0f0`
- **AND** 应用 CSS `object-fit: contain`
#### Scenario: 图片容器支持背景色
- **WHEN** 图片指定了 `background` 参数
- **THEN** 系统创建包装容器,应用背景色到容器
- **AND** 图片在容器内使用 object-fit 定位
#### Scenario: 处理相对路径
- **WHEN** 图片 src 使用相对路径 `"assets/logo.png"`
- **THEN** 系统基于 YAML 文件所在目录解析相对路径
#### Scenario: 图片不存在时显示占位符
- **WHEN** 图片文件不存在
- **THEN** 系统显示占位符或错误提示,而不是崩溃
#### Scenario: 使用 DPI 配置渲染图片
- **WHEN** metadata 定义了 `dpi: 120`
- **THEN** 系统使用该 DPI 值进行英寸到像素的转换
- **AND** box: [1, 1, 4, 3] 转换为 CSS: left: 120px; top: 120px; width: 480px; height: 360px
#### Scenario: HTML 渲染与 PPTX 渲染效果一致
- **WHEN** 同一图片元素使用相同的 fit 和 background 参数
- **THEN** HTML 预览和 PPTX 输出的视觉效果应保持一致
- **AND** 图片位置、尺寸、适配方式相同
### Requirement: 图片元素的 box 参数必须存在
系统 SHALL 要求图片元素必须包含 `box` 参数,否则验证失败。
#### Scenario: 缺少 box 参数时报错
- **WHEN** 图片元素未定义 `box` 参数
- **THEN** 系统抛出 ERROR提示 box 参数为必需
#### Scenario: box 参数转换为像素
- **WHEN** 图片元素定义了 `box: [1, 2, 4, 3]` 且 DPI 为 96
- **THEN** 系统转换为 CSSleft: 96px; top: 192px; width: 384px; height: 288px

View File

@@ -0,0 +1,201 @@
# Image Fit Modes
## Purpose
图片适配模式能力为图片元素提供多种适配策略,允许用户控制图片在指定区域内的显示方式,包括拉伸、保持比例、填充裁剪和居中显示。同时支持 DPI 配置和背景色填充,满足不同场景的视觉需求。
## Requirements
### Requirement: 系统必须支持图片 fit 参数
系统 SHALL 支持图片元素的 `fit` 参数,允许用户指定图片适配模式。
#### Scenario: fit 参数支持四种模式
- **WHEN** 图片元素定义了 `fit` 参数
- **THEN** 系统支持 `stretch``contain``cover``center` 四种模式
#### Scenario: fit 参数默认值为 stretch
- **WHEN** 图片元素未指定 `fit` 参数
- **THEN** 系统使用 `stretch` 模式(向后兼容)
#### Scenario: fit 参数值无效时报错
- **WHEN** `fit` 参数值不是四种有效模式之一
- **THEN** 系统抛出 ERROR并列出有效值stretch、contain、cover、center
### Requirement: 系统必须支持 stretch 模式
系统 SHALL 在 `stretch` 模式下将图片强制缩放到 box 指定的尺寸,不考虑宽高比。
#### Scenario: stretch 模式拉伸图片
- **WHEN** 图片元素定义了 `fit: stretch` 或未指定 `fit`
- **THEN** 系统将图片拉伸到 box 的宽高尺寸
- **AND** 图片可能变形
#### Scenario: stretch 模式不考虑背景色
- **WHEN** 图片使用 `fit: stretch` 并指定了 `background`
- **THEN** 背景色参数被忽略(无留白区域)
### Requirement: 系统必须支持 contain 模式
系统 SHALL 在 `contain` 模式下保持图片宽高比,完整显示图片在 box 内,可能有留白。
#### Scenario: contain 模式保持宽高比
- **WHEN** 图片元素定义了 `fit: contain`
- **THEN** 系统缩放图片使其完整显示在 box 内
- **AND** 保持原始宽高比
- **AND** 图片尺寸不超过 box 尺寸
#### Scenario: contain 模式图片居中
- **WHEN** 图片使用 `fit: contain` 且小于 box 尺寸
- **THEN** 系统将图片居中显示在 box 内
#### Scenario: contain 模式支持背景色
- **WHEN** 图片使用 `fit: contain` 并指定了 `background`
- **THEN** 系统用指定颜色填充 box 内的留白区域
#### Scenario: contain 模式图片比 box 大
- **WHEN** 图片原始尺寸大于 box 尺寸
- **THEN** 系统等比缩小图片使其完整显示在 box 内
#### Scenario: contain 模式图片比 box 小
- **WHEN** 图片原始尺寸小于 box 尺寸
- **THEN** 系统保持原始尺寸,居中显示在 box 内
### Requirement: 系统必须支持 cover 模式
系统 SHALL 在 `cover` 模式下保持图片宽高比,填充整个 box裁剪超出部分。
#### Scenario: cover 模式保持宽高比
- **WHEN** 图片元素定义了 `fit: cover`
- **THEN** 系统缩放图片使其填满 box
- **AND** 保持原始宽高比
- **AND** 裁剪超出 box 的部分
#### Scenario: cover 模式图片居中裁剪
- **WHEN** 图片使用 `fit: cover` 且需要裁剪
- **THEN** 系统从图片中心进行裁剪
#### Scenario: cover 模式不考虑背景色
- **WHEN** 图片使用 `fit: cover` 并指定了 `background`
- **THEN** 背景色参数被忽略(无留白区域)
#### Scenario: cover 模式图片比 box 大
- **WHEN** 图片原始尺寸大于 box 尺寸
- **THEN** 系统等比缩小图片并裁剪超出部分
#### Scenario: cover 模式图片比 box 小
- **WHEN** 图片原始尺寸小于 box 尺寸
- **THEN** 系统等比放大图片并裁剪超出部分
### Requirement: 系统必须支持 center 模式
系统 SHALL 在 `center` 模式下按原始尺寸居中显示图片,不缩放,超出 box 的部分被裁剪。
#### Scenario: center 模式不缩放图片
- **WHEN** 图片元素定义了 `fit: center`
- **THEN** 系统保持图片原始尺寸,不进行缩放
#### Scenario: center 模式图片居中
- **WHEN** 图片使用 `fit: center`
- **THEN** 系统将图片居中显示在 box 内
#### Scenario: center 模式裁剪超出部分
- **WHEN** 图片原始尺寸大于 box 尺寸
- **THEN** 系统裁剪超出 box 的部分(从中心裁剪)
#### Scenario: center 模式支持背景色
- **WHEN** 图片使用 `fit: center` 并指定了 `background`
- **THEN** 系统用指定颜色填充 box 内的留白区域
### Requirement: 系统必须支持 background 参数
系统 SHALL 支持图片元素的 `background` 参数,允许用户指定留白区域的填充颜色。
#### Scenario: background 参数默认透明
- **WHEN** 图片元素未指定 `background` 参数
- **THEN** 留白区域保持透明
#### Scenario: background 参数支持纯色
- **WHEN** 图片元素指定了 `background: "#ff0000"`
- **THEN** 系统使用指定颜色填充留白区域
#### Scenario: background 参数不支持渐变
- **WHEN** 图片元素指定了渐变色(如 `"linear-gradient(...)"`
- **THEN** 系统抛出 ERROR提示仅支持纯色
#### Scenario: background 参数颜色格式验证
- **WHEN** `background` 值不是有效的颜色格式
- **THEN** 系统抛出 ERROR提示颜色格式应为 #RRGGBB#RGB
### Requirement: 系统必须支持文档级 DPI 配置
系统 SHALL 支持在 `metadata.dpi` 中配置 DPI 值,用于像素与英寸的转换。
#### Scenario: DPI 默认值为 96
- **WHEN** metadata 未指定 `dpi` 参数
- **THEN** 系统使用默认值 96
#### Scenario: DPI 配置影响所有图片
- **WHEN** metadata 指定了 `dpi: 120`
- **THEN** 系统使用该值进行所有图片的像素与英寸转换
#### Scenario: DPI 值验证
- **WHEN** `dpi` 值超出合理范围(如小于 72 或大于 300
- **THEN** 系统发出 WARNING提示 DPI 值可能不合适
### Requirement: 系统必须在图片处理失败时抛出错误
系统 SHALL 在图片处理失败时抛出 ERROR 级别错误,不提供降级方案。
#### Scenario: 损坏的图片文件
- **WHEN** Pillow 无法读取图片文件(文件损坏或格式不支持)
- **THEN** 系统抛出 ERROR明确指出图片文件问题
- **AND** 不降级到其他模式
#### Scenario: 图片处理异常
- **WHEN** Pillow 处理图片时发生异常(如内存不足)
- **THEN** 系统抛出 ERROR包含异常信息
- **AND** 不降级到其他模式
### Requirement: 系统必须使用最高质量的图片处理算法
系统 SHALL 使用 Pillow 的最高质量重采样算法LANCZOS进行图片缩放。
#### Scenario: 图片缩放使用 LANCZOS
- **WHEN** 系统需要缩放图片contain、cover 模式)
- **THEN** 使用 Pillow 的 LANCZOS 重采样算法
- **AND** 不向用户暴露质量配置选项
#### Scenario: 图片裁剪保持质量
- **WHEN** 系统需要裁剪图片cover、center 模式)
- **THEN** 裁剪操作不损失图片质量

View File

@@ -0,0 +1,114 @@
# 图片适配模式实现任务清单
## 1. 依赖配置
- [x] 1.1 在 pyproject.toml 中添加 Pillow 依赖(不指定版本号)
- [x] 1.2 运行 `uv sync` 安装新增依赖
## 2. 核心元素扩展
- [x] 2.1 更新 `core/elements.py` 的 ImageElement 类,添加 `fit` 字段(可选,默认 None
- [x] 2.2 更新 `core/elements.py` 的 ImageElement 类,添加 `background` 字段(可选,默认 None
- [x] 2.3 更新 `core/elements.py` 的 ImageElement.__post_init__验证 box 参数为必需
- [x] 2.4 更新 `core/elements.py` 的 ImageElement.validate 方法,添加 fit 和 background 验证
## 3. 图片处理工具模块
- [x] 3.1 创建 `utils/image_utils.py` 模块
- [x] 3.2 实现 `inches_to_pixels(inches, dpi) -> float` 函数
- [x] 3.3 实现 `pixels_to_inches(pixels, dpi) -> float` 函数
- [x] 3.4 实现 `apply_fit_mode(img, box_size, fit, background) -> PIL.Image` 函数,支持四种模式
- [x] 3.5 实现 `create_canvas_with_background(size, background_color) -> PIL.Image` 函数
- [x] 3.6 在 `apply_fit_mode` 中使用 Pillow.ImageOps.contain 实现 contain 模式
- [x] 3.7 在 `apply_fit_mode` 中使用 Pillow.ImageOps.cover 实现 cover 模式
- [x] 3.8 在 `apply_fit_mode` 中实现 center 模式(不缩放,居中裁剪)
- [x] 3.9 在 `apply_fit_mode` 中使用 LANCZOS 重采样算法保证图片质量
## 4. 验证器
- [x] 4.1 创建 `validators/image_config.py` 模块
- [x] 4.2 实现 `validate_fit_value(fit) -> List[ValidationIssue]` 函数,验证 fit 为有效值
- [x] 4.3 实现 `validate_background_color(color) -> List[ValidationIssue]` 函数,验证颜色格式
- [x] 4.4 实现 `validate_dpi_value(dpi) -> List[ValidationIssue]` 函数,验证 DPI 在合理范围
- [x] 4.5 在主验证流程中集成图片配置验证器
## 5. PPTX 渲染器更新
- [x] 5.1 更新 `renderers/pptx_renderer.py`,导入 Pillow 和 image_utils
- [x] 5.2 重写 `_render_image` 方法,读取 metadata.dpi 配置
- [x] 5.3 在 `_render_image` 中实现 stretch 模式(直接使用 python-pptx
- [x] 5.4 在 `_render_image` 中实现 contain 模式(使用 Pillow 处理)
- [x] 5.5 在 `_render_image` 中实现 cover 模式(使用 Pillow 处理)
- [x] 5.6 在 `_render_image` 中实现 center 模式(使用 Pillow 处理)
- [x] 5.7 在 `_render_image` 中实现背景色填充(创建画布并粘贴图片)
- [x] 5.8 在 `_render_image` 中添加图片处理失败的错误处理(抛出 ERROR
- [x] 5.9 更新 PptxGenerator 类,传递 dpi 参数到渲染方法
## 6. HTML 渲染器更新
- [x] 6.1 更新 `renderers/html_renderer.py``_render_image` 方法
- [x] 6.2 实现 fit 模式到 CSS object-fit 的映射stretch->fill, contain->contain, cover->cover, center->none
- [x] 6.3 在 HTML 渲染中添加 object-position: center 样式center 模式)
- [x] 6.4 实现背景色支持(创建包装容器,应用 background-color
- [x] 6.5 更新 HTML 渲染器读取 metadata.dpi 配置
- [x] 6.6 确保 HTML 和 PPTX 渲染效果一致
## 7. 加载器和配置支持
- [x] 7.1 更新 `loaders/yaml_loader.py`,解析 metadata.dpi 配置
- [x] 7.2 将 dpi 配置传递到 Presentation 对象
- [x] 7.3 在 PptxGenerator 和 HtmlRenderer 中访问 dpi 配置
## 8. 单元测试
- [x] 8.1 创建 `tests/unit/test_image_utils.py`
- [x] 8.2 测试 `inches_to_pixels``pixels_to_inches` 函数
- [x] 8.3 测试 `apply_fit_mode` 函数的四种模式
- [x] 8.4 测试 `create_canvas_with_background` 函数
- [x] 8.5 创建 `tests/unit/test_validators/test_image_config.py`
- [x] 8.6 测试 `validate_fit_value` 函数(有效值和无效值)
- [x] 8.7 测试 `validate_background_color` 函数(有效颜色和无效颜色)
- [x] 8.8 测试 `validate_dpi_value` 函数(合理范围和超出范围)
- [x] 8.9 更新 `tests/unit/test_elements.py`,测试 ImageElement 新字段
## 9. 集成测试
- [x] 9.1 创建 `tests/integration/test_image_fit_modes.py`
- [x] 9.2 测试 stretch 模式的 PPTX 渲染
- [x] 9.3 测试 contain 模式的 PPTX 渲染(图片比 box 大)
- [x] 9.4 测试 contain 模式的 PPTX 渲染(图片比 box 小)
- [x] 9.5 测试 contain 模式的 PPTX 渲染(带背景色)
- [x] 9.6 测试 cover 模式的 PPTX 渲染(图片比 box 大)
- [x] 9.7 测试 cover 模式的 PPTX 渲染(图片比 box 小)
- [x] 9.8 测试 center 模式的 PPTX 渲染(带背景色)
- [x] 9.9 测试不同 DPI 配置的渲染结果
- [x] 9.10 测试图片处理失败时的错误处理
- [x] 9.11 测试 HTML 渲染器的四种 fit 模式
- [x] 9.12 测试 HTML 渲染器的背景色支持
- [x] 9.13 对比 HTML 和 PPTX 渲染效果的一致性
## 10. 端到端测试
- [x] 10.1 创建测试 YAML 文件,包含所有 fit 模式
- [x] 10.2 创建测试 YAML 文件,包含背景色配置
- [x] 10.3 创建测试 YAML 文件,包含 DPI 配置
- [x] 10.4 创建测试 YAML 文件,包含无效参数(测试验证)
- [x] 10.5 运行 `check` 命令,验证错误检测
- [x] 10.6 运行 `convert` 命令,验证 PPTX 生成
- [x] 10.7 运行 `preview` 命令,验证 HTML 预览
## 11. 文档更新
- [x] 11.1 更新 `README.md`,添加图片适配模式章节
- [x] 11.2 在 README.md 中添加 fit 参数说明和示例
- [x] 11.3 在 README.md 中添加 background 参数说明和示例
- [x] 11.4 在 README.md 中添加 metadata.dpi 配置说明
- [x] 11.5 更新 `README_DEV.md`,添加图片处理架构说明
- [x] 11.6 在 README_DEV.md 中说明 Pillow 依赖和用途
- [x] 11.7 在 README_DEV.md 中说明 image_utils 模块的设计
## 12. 向后兼容性验证
- [x] 12.1 测试现有 YAML 文件(无 fit 参数)仍能正常转换
- [x] 12.2 测试现有 YAML 文件的渲染结果与之前一致
- [x] 12.3 验证未指定 fit 时默认使用 stretch 模式