diff --git a/README.md b/README.md
index 5ec51ed..38744fc 100644
--- a/README.md
+++ b/README.md
@@ -102,7 +102,6 @@ uv run yaml2pptx.py convert presentation.yaml --skip-validation
```yaml
metadata:
size: "16:9" # 或 "4:3"
- dpi: 96 # 可选:DPI 配置,默认 96
slides:
- background:
@@ -118,24 +117,6 @@ slides:
align: center
```
-#### DPI 配置
-
-`metadata.dpi` 参数控制图片处理的分辨率,影响图片质量和文件大小:
-
-- **默认值**:96 DPI(标准屏幕分辨率)
-- **建议范围**:72-300 DPI
-- **常用值**:
- - 72 DPI:网页显示
- - 96 DPI:标准屏幕(默认)
- - 150 DPI:高质量打印
- - 300 DPI:专业印刷
-
-```yaml
-metadata:
- size: "16:9"
- dpi: 150 # 高质量输出
-```
-
#### description 字段
`metadata.description` 字段用于描述整个演示文稿的概要和用途,仅用于文档目的,不影响生成的 PPTX 文件:
@@ -189,64 +170,16 @@ slides:
- type: image
box: [x, y, width, height]
src: "path/to/image.png" # 支持相对路径和绝对路径
- fit: contain # 可选:图片适配模式
- background: "#f0f0f0" # 可选:背景色(仅对 contain 和 center 模式有效)
```
-#### 图片适配模式
-
-`fit` 参数控制图片如何适配到指定的 box 区域,支持以下模式:
-
-- **stretch**(默认):拉伸图片以填满整个 box,可能改变宽高比
-- **contain**:保持宽高比,完整显示图片在 box 内,可能留白
-- **cover**:保持宽高比,填满整个 box,可能裁剪图片
-- **center**:不缩放,居中显示,超出部分裁剪
-
**示例**:
```yaml
slides:
- elements:
- # 默认 stretch 模式
- type: image
src: "photo.jpg"
box: [1, 1, 4, 3]
-
- # contain 模式,带灰色背景
- - type: image
- src: "photo.jpg"
- box: [5, 1, 4, 3]
- fit: contain
- background: "#f0f0f0"
-
- # cover 模式,填满区域
- - type: image
- src: "photo.jpg"
- box: [1, 4, 4, 3]
- fit: cover
-
- # center 模式,不缩放
- - type: image
- src: "icon.png"
- box: [5, 4, 2, 2]
- fit: center
- background: "#ffffff"
-```
-
-#### 背景色
-
-`background` 参数为图片添加背景色,支持 `#RRGGBB` 或 `#RGB` 格式:
-
-- 仅对 `contain` 和 `center` 模式有效
-- 当图片小于 box 或保持宽高比时,背景色填充留白区域
-- 默认为透明(无背景色)
-
-```yaml
-- type: image
- src: "logo.png"
- box: [1, 1, 3, 2]
- fit: contain
- background: "#ffffff" # 白色背景
```
### 形状元素
diff --git a/README_DEV.md b/README_DEV.md
index ccb10f5..c25f898 100644
--- a/README_DEV.md
+++ b/README_DEV.md
@@ -67,49 +67,6 @@ yaml2pptx.py (入口)
- 核心层不依赖其他业务模块
- 验证层可以调用核心层的元素验证方法
-### 图片处理架构
-
-图片适配模式功能采用分层设计,将图片处理逻辑与渲染逻辑分离:
-
-```
-ImageElement (核心层)
- ↓ 定义 fit/background 字段
-utils/image_utils.py (工具层)
- ↓ 提供图片处理函数
- ├─→ apply_fit_mode() - 应用适配模式
- ├─→ create_canvas_with_background() - 创建背景画布
- └─→ inches_to_pixels() / pixels_to_inches() - 单位转换
- ↓ 使用 Pillow (PIL)
-renderers/pptx_renderer.py (渲染层)
- ↓ 调用 image_utils 处理图片
- └─→ 将处理后的图片添加到 PPTX
-renderers/html_renderer.py (渲染层)
- └─→ 使用 CSS object-fit 实现相同效果
-```
-
-**设计要点**:
-
-1. **Pillow 依赖**:
- - 用于高质量图片处理(缩放、裁剪、画布创建)
- - 使用 LANCZOS 重采样算法确保最佳质量
- - 支持 RGB 模式和透明度处理
-
-2. **适配模式实现**:
- - **stretch**:直接使用 `Image.resize()`
- - **contain**:使用 `ImageOps.contain()` 保持宽高比
- - **cover**:使用 `ImageOps.cover()` + 中心裁剪
- - **center**:不缩放,使用 `Image.crop()` 裁剪超出部分
-
-3. **DPI 配置**:
- - 在 `metadata.dpi` 配置,默认 96
- - 通过 Presentation → Renderer 传递
- - 用于像素与英寸的转换计算
-
-4. **向后兼容性**:
- - `fit` 和 `background` 参数可选
- - 未指定 `fit` 时默认使用 `stretch` 模式
- - 现有 YAML 文件无需修改即可正常工作
-
## 模块职责
### 1. yaml2pptx.py(入口层)
@@ -128,22 +85,13 @@ renderers/html_renderer.py (渲染层)
- 进度日志显示准确的渲染数量(不包括禁用的幻灯片)
### 2. utils/(工具层)
-- **职责**:通用工具函数和图片处理
+- **职责**:通用工具函数
- **包含**:
- `utils/__init__.py` - 日志和颜色工具
- 日志函数:`log_info()`, `log_success()`, `log_error()`, `log_progress()`
- 颜色转换:`hex_to_rgb()`, `validate_color()`
- - `utils/image_utils.py` - 图片处理工具
- - 单位转换:`inches_to_pixels()`, `pixels_to_inches()`
- - 图片适配:`apply_fit_mode()` - 支持 stretch/contain/cover/center 四种模式
- - 画布创建:`create_canvas_with_background()` - 创建带背景色的画布
- **依赖**:
- - Pillow (PIL) - 用于高质量图片处理
- - 使用 LANCZOS 重采样算法确保最佳质量
-- **设计原则**:
- - 图片处理与渲染逻辑分离
- - 支持 DPI 配置,灵活控制图片分辨率
- - 所有图片操作返回 PIL Image 对象,便于后续处理
+ - Pillow (PIL) - 保留用于未来可能的图片处理需求
### 3. loaders/yaml_loader.py(加载层)
- **职责**:YAML 文件加载和验证
@@ -187,10 +135,6 @@ renderers/html_renderer.py (渲染层)
- `validators/resource.py` - 资源验证器
- `ResourceValidator` - 检查图片、模板文件存在性
- 验证模板文件结构
- - `validators/image_config.py` - 图片配置验证器
- - `validate_fit_value()` - 验证 fit 参数值
- - `validate_background_color()` - 验证背景色格式
- - `validate_dpi_value()` - 验证 DPI 值范围
- `validators/validator.py` - 主验证器
- `Validator` - 协调所有子验证器
- 集成元素级验证、几何验证、资源验证
diff --git a/core/elements.py b/core/elements.py
index 1c2f52d..6c85dc9 100644
--- a/core/elements.py
+++ b/core/elements.py
@@ -78,8 +78,6 @@ class ImageElement:
type: str = 'image'
src: str = ''
box: list = field(default_factory=lambda: [1, 1, 4, 3])
- fit: Optional[str] = None
- background: Optional[str] = None
def __post_init__(self):
"""创建时验证"""
@@ -92,31 +90,7 @@ class ImageElement:
def validate(self) -> List:
"""验证元素自身的完整性"""
- from validators.result import ValidationIssue
- issues = []
-
- # 验证 fit 参数
- if self.fit is not None:
- valid_fits = ['stretch', 'contain', 'cover', 'center']
- if self.fit not in valid_fits:
- issues.append(ValidationIssue(
- level="ERROR",
- message=f"无效的 fit 值: {self.fit} (支持: {', '.join(valid_fits)})",
- location="",
- code="INVALID_FIT_VALUE"
- ))
-
- # 验证 background 参数
- if self.background is not None:
- if not _is_valid_color(self.background):
- issues.append(ValidationIssue(
- level="ERROR",
- message=f"无效的背景颜色格式: {self.background} (应为 #RRGGBB 或 #RGB)",
- location="",
- code="INVALID_COLOR_FORMAT"
- ))
-
- return issues
+ return []
@dataclass
diff --git a/core/presentation.py b/core/presentation.py
index 9cf0024..fc64dfc 100644
--- a/core/presentation.py
+++ b/core/presentation.py
@@ -31,7 +31,6 @@ class Presentation:
# 获取演示文稿尺寸
metadata = self.data.get("metadata", {})
self.size = metadata.get("size", "16:9")
- self.dpi = metadata.get("dpi", 96)
self.description = metadata.get("description") # 可选的描述字段
# 验证尺寸值
diff --git a/openspec/changes/archive/2026-03-04-remove-image-fit-modes/.openspec.yaml b/openspec/changes/archive/2026-03-04-remove-image-fit-modes/.openspec.yaml
new file mode 100644
index 0000000..5aae5cf
--- /dev/null
+++ b/openspec/changes/archive/2026-03-04-remove-image-fit-modes/.openspec.yaml
@@ -0,0 +1,2 @@
+schema: spec-driven
+created: 2026-03-04
diff --git a/openspec/changes/archive/2026-03-04-remove-image-fit-modes/design.md b/openspec/changes/archive/2026-03-04-remove-image-fit-modes/design.md
new file mode 100644
index 0000000..19001ff
--- /dev/null
+++ b/openspec/changes/archive/2026-03-04-remove-image-fit-modes/design.md
@@ -0,0 +1,184 @@
+# 移除图片适配模式技术设计
+
+## Context
+
+### 当前状态
+
+图片适配模式功能已在 `openspec/changes/archive/2026-03-04-add-image-fit-modes/` 中实现并归档,包括:
+
+- `utils/image_utils.py`: 图片处理工具模块(英寸像素转换、fit 模式处理)
+- `validators/image_config.py`: 图片参数验证器
+- `core/elements.py`: ImageElement 添加了 fit 和 background 字段
+- `renderers/pptx_renderer.py`: 重写 _render_image() 支持四种 fit 模式
+- `renderers/html_renderer.py`: render_image() 支持 CSS object-fit 映射
+- `core/presentation.py`: 读取 metadata.dpi 配置
+- 完整的单元测试和集成测试
+
+该功能尚未上线使用,无需考虑向后兼容性。
+
+### 约束条件
+
+- 必须保留 Pillow 依赖(用户要求留作备用)
+- HTML 渲染器需要保留英寸到像素的转换功能(使用硬编码 DPI=96)
+- 移除后图片渲染行为:直接拉伸到 box 指定的尺寸
+
+### 利益相关者
+
+- 最终用户:获得更简单的图片配置(只需 src 和 box)
+- 开发者:降低代码复杂度和维护成本
+
+## Goals / Non-Goals
+
+**Goals:**
+
+- 完全移除图片适配模式功能(fit、background、dpi 配置)
+- 简化图片渲染逻辑,恢复到直接使用 python-pptx 原生功能
+- 删除不再需要的工具模块和验证器
+- 删除相关测试文件
+- 更新规格文档和用户文档
+- 保留 Pillow 依赖用于未来可能的图片处理需求
+
+**Non-Goals:**
+
+- 不移除 Pillow 依赖
+- 不改变 HTML 渲染器的基础英寸到像素转换功能(保留硬编码 DPI=96)
+- 不影响其他元素类型(text、shape、table)的渲染逻辑
+
+## Decisions
+
+### 决策 1: HTML 渲染器使用硬编码 DPI
+
+**选择:** HtmlRenderer 使用硬编码的 `self.dpi = 96`,移除 __init__ 的 dpi 参数
+
+**理由:**
+
+- 96 是 Web 标准 DPI,适用于绝大多数场景
+- HTML 预览的尺寸转换是基础功能,不需要用户配置
+- 移除 metadata.dpi 配置后,简化了用户 YAML 文件
+
+**实现:**
+```python
+# renderers/html_renderer.py
+class HtmlRenderer:
+ def __init__(self): # 移除 dpi 参数
+ self.dpi = 96 # 硬编码 Web 标准 DPI
+```
+
+### 决策 2: PPTX 图片渲染恢复到简单实现
+
+**选择:** 直接使用 python-pptx 的 `slide.shapes.add_picture()` 方法
+
+**理由:**
+
+- python-pptx 原生支持图片添加,会自动拉伸到指定尺寸
+- 移除对 Pillow 的运行时依赖(保留依赖但代码中不使用)
+- 代码更简洁,易于维护
+
+**实现:**
+```python
+# renderers/pptx_renderer.py
+def _render_image(self, slide, elem: ImageElement, base_path):
+ img_path = Path(elem.src)
+ if not img_path.is_absolute() and base_path:
+ img_path = Path(base_path) / elem.src
+
+ if not img_path.exists():
+ raise YAMLError(f"图片文件未找到: {img_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)
+```
+
+### 决策 3: 完全删除相关文件而非注释代码
+
+**选择:** 直接删除 image_utils.py、image_config.py 及相关测试文件
+
+**理由:**
+
+- 功能尚未上线,无需保留代码历史
+- Git 历史已记录所有实现细节
+- 保持代码库整洁
+
+### 决策 4: 删除 image-fit-modes 规格
+
+**选择:** 删除 `openspec/specs/image-fit-modes/` 整个目录
+
+**理由:**
+
+- 该规格对应的 capability 正在被移除
+- 归档的变更中已保留完整规格副本
+
+## Risks / Trade-offs
+
+### 风险 1: HTML 预览尺寸可能与 PPTX 不完全一致
+
+**风险:** 硬编码 DPI=96 可能在某些显示器上导致预览尺寸与实际 PPTX 有差异
+
+**缓解措施:**
+- 96 是 Web 标准,覆盖绝大多数场景
+- 预览功能主要用于布局参考,不需要像素级精确
+- 用户主要关注 PPTX 输出结果
+
+### 风险 2: 删除文件可能影响其他未发现的依赖
+
+**风险:** 可能有其他代码或测试依赖 image_utils 或 image_config
+
+**缓解措施:**
+- 运行完整的测试套件验证
+- 检查 imports 确保没有遗漏的引用
+- 使用 Grep 搜索可能的引用点
+
+### 风险 3: 文档更新可能不完整
+
+**风险:** README.md 或 README_DEV.md 中可能残留相关说明
+
+**缓解措施:**
+- 在 tasks 中明确列出文档更新任务
+- 人工审查最终文档内容
+
+## Migration Plan
+
+### 部署步骤
+
+由于功能尚未上线,无需迁移策略。直接执行删除和修改即可。
+
+1. **删除工具和验证器模块**
+ - 删除 `utils/image_utils.py`
+ - 删除 `validators/image_config.py`
+ - 更新 `validators/validator.py` 移除引用
+
+2. **修改核心代码**
+ - 更新 `core/elements.py` 的 ImageElement
+ - 更新 `core/presentation.py` 的 Presentation
+ - 更新 `renderers/pptx_renderer.py`
+ - 更新 `renderers/html_renderer.py`
+ - 更新 `yaml2pptx.py`
+ - 更新 `preview/server.py`
+
+3. **删除测试文件**
+ - 删除 `tests/unit/test_image_utils.py`
+ - 删除 `tests/unit/test_validators/test_image_config.py`
+ - 删除 `tests/integration/test_image_fit_modes.py`
+
+4. **删除规格文件**
+ - 删除 `openspec/specs/image-fit-modes/` 目录
+
+5. **更新文档**
+ - 更新 `README.md`
+ - 更新 `README_DEV.md`
+ - 更新 `openspec/specs/element-rendering/spec.md`
+ - 更新 `openspec/specs/html-rendering/spec.md`
+
+6. **验证**
+ - 运行完整测试套件
+ - 检查是否有遗留的 import 引用
+
+### 回滚策略
+
+- Git 历史保留了所有实现
+- 如需恢复功能,可以从归档的变更中恢复代码
+- 或者从 Git 历史中恢复提交 `19d6661` 之前的代码
+
+## Open Questions
+
+无。移除操作明确清晰,无未决问题。
diff --git a/openspec/changes/archive/2026-03-04-remove-image-fit-modes/proposal.md b/openspec/changes/archive/2026-03-04-remove-image-fit-modes/proposal.md
new file mode 100644
index 0000000..33babdd
--- /dev/null
+++ b/openspec/changes/archive/2026-03-04-remove-image-fit-modes/proposal.md
@@ -0,0 +1,57 @@
+# 移除图片适配模式功能
+
+## Why
+
+图片适配模式功能已实现但尚未上线使用,经过评估决定移除该功能以简化系统复杂度。移除后系统将恢复到更简单直接的图片渲染方式:直接使用 python-pptx 的原生图片添加功能,图片会被拉伸到指定尺寸。
+
+## What Changes
+
+- **BREAKING** 移除图片元素的 `fit` 参数(stretch、contain、cover、center 四种模式)
+- **BREAKING** 移除图片元素的 `background` 参数(背景色填充)
+- **BREAKING** 移除文档级的 `metadata.dpi` 配置
+- **BREAKING** 删除 `utils/image_utils.py` 模块(图片处理工具)
+- **BREAKING** 删除 `validators/image_config.py` 模块(图片配置验证器)
+- 重写 `renderers/pptx_renderer.py` 的 `_render_image()` 方法,恢复简单实现
+- 简化 `renderers/html_renderer.py` 的 `render_image()` 方法,移除 fit/background 处理
+- 简化 `core/elements.py` 的 `ImageElement` 类,移除 fit/background 字段
+- 简化 `core/presentation.py` 的 `Presentation` 类,移除 dpi 配置读取
+- 保留 Pillow 依赖(留作未来可能的其他用途)
+- HTML 渲染器使用硬编码的 DPI=96(Web 标准,无需用户配置)
+
+## Capabilities
+
+### Modified Capabilities
+- `element-rendering`: 移除图片元素的 fit 和 background 参数支持,恢复到基础图片渲染
+- `html-rendering`: 移除图片元素的 fit 和 background 参数支持,简化 HTML 图片渲染
+
+## Impact
+
+### 代码变更
+- `core/elements.py`: ImageElement 移除 fit 和 background 字段及验证逻辑
+- `core/presentation.py`: Presentation 移除 dpi 配置读取
+- `renderers/pptx_renderer.py`: 重写 _render_image() 方法,移除 Pillow 图片处理和 fit 模式逻辑
+- `renderers/html_renderer.py`: 简化 render_image() 方法,移除 fit 模式映射和背景色容器
+- `yaml2pptx.py`: 移除传递 dpi 参数到 PptxGenerator
+- `preview/server.py`: 移除传递 dpi 参数到 HtmlRenderer
+- `validators/validator.py`: 移除对 image_config 的引用
+
+### 删除的文件
+- `utils/image_utils.py`: 整个文件删除
+- `validators/image_config.py`: 整个文件删除
+- `tests/unit/test_image_utils.py`: 删除
+- `tests/unit/test_validators/test_image_config.py`: 删除
+- `tests/integration/test_image_fit_modes.py`: 删除
+- `openspec/specs/image-fit-modes/`: 整个规格目录删除
+
+### API 变更
+- YAML 语法移除:
+ - 图片元素不再支持 `fit` 字段
+ - 图片元素不再支持 `background` 字段
+ - metadata 层级不再支持 `dpi` 字段
+
+### 依赖变更
+- Pillow 依赖保留(在 pyproject.toml 中),用于未来可能的图片处理需求
+
+### 文档变更
+- `README.md`: 移除图片适配模式使用说明
+- `README_DEV.md`: 移除图片处理架构说明
diff --git a/openspec/changes/archive/2026-03-04-remove-image-fit-modes/specs/element-rendering/spec.md b/openspec/changes/archive/2026-03-04-remove-image-fit-modes/specs/element-rendering/spec.md
new file mode 100644
index 0000000..348e642
--- /dev/null
+++ b/openspec/changes/archive/2026-03-04-remove-image-fit-modes/specs/element-rendering/spec.md
@@ -0,0 +1,82 @@
+# Element Rendering Delta Spec
+
+## REMOVED Requirements
+
+### Requirement: 系统必须支持图片元素渲染 (部分移除)
+
+**Reason**: 移除图片适配模式功能(fit 和 background 参数),简化图片渲染逻辑
+
+**Migration**: 移除图片元素的 `fit` 和 `background` 参数定义,系统将使用简单的拉伸模式渲染图片
+
+#### Scenario: 使用 fit 模式渲染图片
+
+**REMOVED**
+
+- **WHEN** 元素定义为 `{type: image, src: "photo.jpg", box: [1, 1, 4, 3], fit: contain}`
+- **THEN** 系统使用 `contain` 模式渲染图片
+- **AND** 保持图片宽高比,完整显示在 box 内
+
+#### Scenario: 使用背景色渲染图片
+
+**REMOVED**
+
+- **WHEN** 元素定义为 `{type: image, src: "photo.png", box: [1, 1, 4, 3], fit: contain, background: "#f0f0f0"}`
+- **THEN** 系统使用 `contain` 模式渲染图片
+- **AND** 用 #f0f0f0 颜色填充留白区域
+
+#### Scenario: 图片格式不支持时报错
+
+**REMOVED**
+
+- **WHEN** 图片文件格式不被 Pillow 支持
+- **THEN** 系统抛出错误,提示图片格式不支持,并列出支持的格式
+
+#### Scenario: 图片处理失败时报错
+
+**REMOVED**
+
+- **WHEN** Pillow 处理图片时发生异常(如文件损坏)
+- **THEN** 系统抛出 ERROR 级别错误,不降级到其他模式
+
+#### Scenario: 使用 DPI 配置渲染图片
+
+**REMOVED**
+
+- **WHEN** metadata 定义了 `dpi: 120` 且图片需要处理
+- **THEN** 系统使用该 DPI 值进行像素与英寸的转换
+
+#### Scenario: fit 参数值无效时报错
+
+**REMOVED**
+
+- **WHEN** `fit` 参数值不是 stretch、contain、cover、center 之一
+- **THEN** 系统抛出 ERROR,并列出有效值
+
+#### Scenario: background 参数颜色格式无效时报错
+
+**REMOVED**
+
+- **WHEN** `background` 值不是有效的颜色格式
+- **THEN** 系统抛出 ERROR,提示颜色格式应为 #RRGGBB 或 #RGB
+
+## MODIFIED Requirements
+
+### Requirement: 系统必须支持图片元素渲染
+
+系统 SHALL 将 YAML 中定义的图片元素渲染为 PPTX 图片对象,直接使用 python-pptx 的原生图片添加功能。
+
+#### Scenario: 渲染本地图片
+
+- **WHEN** 元素定义为 `{type: image, src: "images/logo.png", box: [2, 3, 4, 3]}`
+- **THEN** 系统从指定路径加载图片,在 (2, 3) 位置渲染为 4×3 英寸大小
+- **AND** 图片会被拉伸到指定尺寸(python-pptx 默认行为)
+
+#### Scenario: 图片文件不存在时报错
+
+- **WHEN** 图片 src 指向不存在的文件路径
+- **THEN** 系统抛出错误,明确指出图片文件未找到
+
+#### Scenario: 相对路径处理
+
+- **WHEN** 图片 src 使用相对路径 `"assets/images/logo.png"`
+- **THEN** 系统基于演示文稿文件所在目录解析相对路径
diff --git a/openspec/changes/archive/2026-03-04-remove-image-fit-modes/specs/html-rendering/spec.md b/openspec/changes/archive/2026-03-04-remove-image-fit-modes/specs/html-rendering/spec.md
new file mode 100644
index 0000000..e8dd7ca
--- /dev/null
+++ b/openspec/changes/archive/2026-03-04-remove-image-fit-modes/specs/html-rendering/spec.md
@@ -0,0 +1,86 @@
+# HTML Rendering Delta Spec
+
+## REMOVED Requirements
+
+### Requirement: 系统必须渲染图片元素 (部分移除)
+
+**Reason**: 移除图片适配模式功能(fit 和 background 参数),简化 HTML 图片渲染
+
+**Migration**: 移除图片元素的 `fit` 和 `background` 参数定义,HTML 渲染器将输出简单的 `` 标签
+
+#### Scenario: 使用 fit 模式渲染图片
+
+**REMOVED**
+
+- **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 映射
+
+**REMOVED**
+
+- **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: 使用背景色渲染图片
+
+**REMOVED**
+
+- **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: 图片容器支持背景色
+
+**REMOVED**
+
+- **WHEN** 图片指定了 `background` 参数
+- **THEN** 系统创建包装容器,应用背景色到容器
+- **AND** 图片在容器内使用 object-fit 定位
+
+#### Scenario: 使用 DPI 配置渲染图片
+
+**REMOVED**
+
+- **WHEN** metadata 定义了 `dpi: 120`
+- **THEN** 系统使用该 DPI 值进行英寸到像素的转换
+- **AND** box: [1, 1, 4, 3] 转换为 CSS: left: 120px; top: 120px; width: 480px; height: 360px
+
+#### Scenario: HTML 渲染与 PPTX 渲染效果一致
+
+**REMOVED**
+
+- **WHEN** 同一图片元素使用相同的 fit 和 background 参数
+- **THEN** HTML 预览和 PPTX 输出的视觉效果应保持一致
+- **AND** 图片位置、尺寸、适配方式相同
+
+## MODIFIED Requirements
+
+### Requirement: 系统必须渲染图片元素
+
+系统 SHALL 将 YAML 中的图片元素转换为 HTML `
` 标签,使用固定 DPI 进行尺寸转换。
+
+#### Scenario: 渲染本地图片
+
+- **WHEN** 元素定义为 `{type: image, src: "images/logo.png", box: [2, 3, 4, 3]}`
+- **THEN** 系统生成 `
` 标签,src 为图片的文件路径,位置为 (192px, 288px),尺寸为 384x288 像素
+
+#### Scenario: 处理相对路径
+
+- **WHEN** 图片 src 使用相对路径 `"assets/logo.png"`
+- **THEN** 系统基于 YAML 文件所在目录解析相对路径
+
+#### Scenario: 图片不存在时显示占位符
+
+- **WHEN** 图片文件不存在
+- **THEN** 系统显示占位符或错误提示,而不是崩溃
diff --git a/openspec/changes/archive/2026-03-04-remove-image-fit-modes/tasks.md b/openspec/changes/archive/2026-03-04-remove-image-fit-modes/tasks.md
new file mode 100644
index 0000000..3723f3b
--- /dev/null
+++ b/openspec/changes/archive/2026-03-04-remove-image-fit-modes/tasks.md
@@ -0,0 +1,67 @@
+# 移除图片适配模式功能 - 任务清单
+
+## 1. 删除工具和验证器模块
+
+- [x] 1.1 删除 `utils/image_utils.py` 文件
+- [x] 1.2 删除 `validators/image_config.py` 文件
+- [x] 1.3 更新 `validators/validator.py`,移除对 image_config 的引用
+
+## 2. 修改核心元素定义
+
+- [x] 2.1 更新 `core/elements.py` 的 ImageElement 类,移除 fit 字段
+- [x] 2.2 更新 `core/elements.py` 的 ImageElement 类,移除 background 字段
+- [x] 2.3 简化 `core/elements.py` 的 ImageElement.validate() 方法,移除 fit/background 验证逻辑
+
+## 3. 修改演示文稿类
+
+- [x] 3.1 更新 `core/presentation.py` 的 Presentation 类,移除 self.dpi = metadata.get("dpi", 96)
+
+## 4. 修改 PPTX 渲染器
+
+- [x] 4.1 更新 `renderers/pptx_renderer.py`,移除 `from PIL import Image` 导入
+- [x] 4.2 更新 `renderers/pptx_renderer.py`,移除 `from utils.image_utils import ...` 导入
+- [x] 4.3 更新 `renderers/pptx_renderer.py` 的 PptxGenerator.__init__(),移除 dpi 参数
+- [x] 4.4 重写 `renderers/pptx_renderer.py` 的 _render_image() 方法,恢复简单实现
+
+## 5. 修改 HTML 渲染器
+
+- [x] 5.1 更新 `renderers/html_renderer.py` 的 HtmlRenderer.__init__(),移除 dpi 参数,硬编码 self.dpi = 96
+- [x] 5.2 简化 `renderers/html_renderer.py` 的 render_image() 方法,移除 fit 模式映射逻辑
+- [x] 5.3 简化 `renderers/html_renderer.py` 的 render_image() 方法,移除 background 容器逻辑
+
+## 6. 修改入口文件
+
+- [x] 6.1 更新 `yaml2pptx.py`,移除传递 dpi 参数到 PptxGenerator
+- [x] 6.2 更新 `preview/server.py`,移除传递 dpi 参数到 HtmlRenderer
+
+## 7. 删除测试文件
+
+- [x] 7.1 删除 `tests/unit/test_image_utils.py`
+- [x] 7.2 删除 `tests/unit/test_validators/test_image_config.py`
+- [x] 7.3 删除 `tests/integration/test_image_fit_modes.py`
+- [x] 7.4 检查并更新 `tests/fixtures/create_test_images.py`(如果有其他依赖则保留,否则删除)
+- [x] 7.5 检查并更新 `tests/integration/test_rendering_flow.py`,移除相关测试
+
+## 8. 删除规格文件
+
+- [x] 8.1 删除 `openspec/specs/image-fit-modes/` 整个目录
+
+## 9. 更新主规格文件
+
+- [x] 9.1 更新 `openspec/specs/element-rendering/spec.md`,移除 fit 和 background 相关规格
+- [x] 9.2 更新 `openspec/specs/html-rendering/spec.md`,移除 fit 和 background 相关规格
+
+## 10. 更新用户文档
+
+- [x] 10.1 更新 `README.md`,移除图片适配模式章节
+- [x] 10.2 更新 `README.md`,移除 metadata.dpi 配置说明
+- [x] 10.3 更新 `README_DEV.md`,移除图片处理架构说明
+- [x] 10.4 更新 `README_DEV.md`,移除 Pillow 依赖说明(改为保留用于备用)
+
+## 11. 验证和测试
+
+- [x] 11.1 运行 `uv run pytest`,确保所有测试通过
+- [x] 11.2 使用 Grep 搜索代码库,确认没有遗留的 image_utils 或 image_config 引用
+- [x] 11.3 使用 Grep 搜索代码库,确认没有遗留的 fit 或 background 参数引用(在图片元素上下文中)
+- [x] 11.4 运行 `uv run python yaml2pptx.py` 测试基本转换功能
+- [x] 11.5 运行 `uv run python preview/server.py` 测试预览功能
diff --git a/openspec/specs/element-rendering/spec.md b/openspec/specs/element-rendering/spec.md
index e68f67f..380c5d9 100644
--- a/openspec/specs/element-rendering/spec.md
+++ b/openspec/specs/element-rendering/spec.md
@@ -54,61 +54,23 @@ Element rendering系统负责将 YAML 中定义的各类元素(文本、图片
### Requirement: 系统必须支持图片元素渲染
-系统 SHALL 将 YAML 中定义的图片元素渲染为 PPTX 图片对象,支持 `fit` 和 `background` 参数控制图片适配模式。
+系统 SHALL 将 YAML 中定义的图片元素渲染为 PPTX 图片对象。
#### 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` 参数,否则验证失败。
diff --git a/openspec/specs/html-rendering/spec.md b/openspec/specs/html-rendering/spec.md
index a9d0f4b..cd0c7a4 100644
--- a/openspec/specs/html-rendering/spec.md
+++ b/openspec/specs/html-rendering/spec.md
@@ -133,45 +133,12 @@ HTML Rendering 系统负责将 YAML 中定义的各类元素(文本、图片
### Requirement: 系统必须渲染图片元素
-系统 SHALL 将 YAML 中的图片元素转换为 HTML `
` 标签,支持 `fit` 和 `background` 参数,使用 CSS object-fit 实现适配模式。
+系统 SHALL 将 YAML 中的图片元素转换为 HTML `
` 标签,使用固定 DPI 进行尺寸转换。
#### Scenario: 渲染本地图片
- **WHEN** 元素定义为 `{type: image, src: "images/logo.png", box: [2, 3, 4, 3]}`
- **THEN** 系统生成 `
` 标签,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: 处理相对路径
@@ -183,18 +150,6 @@ HTML Rendering 系统负责将 YAML 中定义的各类元素(文本、图片
- **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` 参数,否则验证失败。
diff --git a/openspec/specs/image-fit-modes/spec.md b/openspec/specs/image-fit-modes/spec.md
deleted file mode 100644
index 31b6e68..0000000
--- a/openspec/specs/image-fit-modes/spec.md
+++ /dev/null
@@ -1,201 +0,0 @@
-# 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** 裁剪操作不损失图片质量
diff --git a/preview/server.py b/preview/server.py
index 8532ede..c90477d 100644
--- a/preview/server.py
+++ b/preview/server.py
@@ -148,7 +148,7 @@ def generate_preview_html(yaml_file, template_dir):
"""生成完整的预览 HTML 页面"""
try:
pres = Presentation(yaml_file, template_dir)
- renderer = HtmlRenderer(pres.dpi)
+ renderer = HtmlRenderer()
slides_html = ""
for i, slide_data in enumerate(pres.data.get('slides', [])):
diff --git a/renderers/html_renderer.py b/renderers/html_renderer.py
index 76cdf64..6b88032 100644
--- a/renderers/html_renderer.py
+++ b/renderers/html_renderer.py
@@ -11,14 +11,11 @@ from core.elements import TextElement, ImageElement, ShapeElement, TableElement
class HtmlRenderer:
"""HTML 渲染器,将元素渲染为 HTML"""
- def __init__(self, dpi=96):
+ def __init__(self):
"""
初始化 HTML 渲染器
-
- Args:
- dpi: DPI 配置,默认 96
"""
- self.dpi = dpi
+ self.dpi = 96 # 硬编码 Web 标准 DPI
def render_slide(self, slide_data, index, base_path):
"""
@@ -171,46 +168,12 @@ class HtmlRenderer:
"""
img_path = Path(base_path) / elem.src if base_path else Path(elem.src)
- # 获取 fit 模式,默认为 stretch
- fit = elem.fit if elem.fit else 'stretch'
-
- # fit 模式到 CSS object-fit 的映射
- object_fit_map = {
- 'stretch': 'fill',
- 'contain': 'contain',
- 'cover': 'cover',
- 'center': 'none'
- }
- object_fit = object_fit_map.get(fit, 'fill')
-
# 基础样式
style = f"""
left: {elem.box[0] * self.dpi}px;
top: {elem.box[1] * self.dpi}px;
width: {elem.box[2] * self.dpi}px;
height: {elem.box[3] * self.dpi}px;
- object-fit: {object_fit};
- object-position: center;
"""
- # 如果有背景色,需要创建包装容器
- if elem.background:
- container_style = f"""
- position: absolute;
- left: {elem.box[0] * self.dpi}px;
- top: {elem.box[1] * self.dpi}px;
- width: {elem.box[2] * self.dpi}px;
- height: {elem.box[3] * self.dpi}px;
- background-color: {elem.background};
- """
- img_style = f"""
- width: 100%;
- height: 100%;
- object-fit: {object_fit};
- object-position: center;
- """
- return f'''
0
-
- def test_invalid_fit_value(self):
- """测试无效的 fit 值"""
- issues = validate_fit_value("invalid")
- assert len(issues) > 0
- assert issues[0].level == "ERROR"
-
- issues = validate_fit_value("fill")
- assert len(issues) > 0
-
- issues = validate_fit_value("scale")
- assert len(issues) > 0
-
- def test_case_sensitive(self):
- """测试大小写敏感"""
- issues = validate_fit_value("STRETCH")
- assert len(issues) > 0
-
- issues = validate_fit_value("Contain")
- assert len(issues) > 0
-
-
-class TestValidateBackgroundColor:
- """测试背景色验证"""
-
- def test_valid_hex_colors(self):
- """测试有效的十六进制颜色"""
- assert validate_background_color("#ffffff") == []
- assert validate_background_color("#000000") == []
- assert validate_background_color("#ff0000") == []
- assert validate_background_color("#4a90e2") == []
-
- def test_valid_short_hex_colors(self):
- """测试有效的短格式十六进制颜色"""
- assert validate_background_color("#fff") == []
- assert validate_background_color("#000") == []
- assert validate_background_color("#f00") == []
-
- def test_none_background(self):
- """测试 None 值(透明)"""
- # None 会导致 TypeError,应该返回错误
- issues = validate_background_color(None)
- assert len(issues) > 0
-
- def test_invalid_colors(self):
- """测试无效的颜色格式"""
- issues = validate_background_color("white")
- assert len(issues) > 0
-
- issues = validate_background_color("rgb(255,255,255)")
- assert len(issues) > 0
-
- issues = validate_background_color("#gggggg")
- assert len(issues) > 0
-
- issues = validate_background_color("#ff")
- assert len(issues) > 0
-
- issues = validate_background_color("ffffff")
- assert len(issues) > 0
-
-
-class TestValidateDpiValue:
- """测试 DPI 值验证"""
-
- def test_valid_dpi_values(self):
- """测试有效的 DPI 值"""
- assert validate_dpi_value(72) == []
- assert validate_dpi_value(96) == []
- assert validate_dpi_value(150) == []
- assert validate_dpi_value(300) == []
-
- def test_boundary_dpi_values(self):
- """测试边界 DPI 值"""
- # 1 和 1200 超出建议范围,会返回 WARNING
- issues = validate_dpi_value(1)
- assert len(issues) > 0
- assert issues[0].level == "WARNING"
-
- issues = validate_dpi_value(1200)
- assert len(issues) > 0
- assert issues[0].level == "WARNING"
-
- def test_invalid_dpi_values(self):
- """测试无效的 DPI 值"""
- # 0 和负数会返回 WARNING
- issues = validate_dpi_value(0)
- assert len(issues) > 0
-
- issues = validate_dpi_value(-1)
- assert len(issues) > 0
-
- issues = validate_dpi_value(1201)
- assert len(issues) > 0
-
- issues = validate_dpi_value(2000)
- assert len(issues) > 0
-
- def test_non_integer_dpi(self):
- """测试非整数 DPI 值"""
- # 浮点数 DPI 可能被接受(取决于实现)
- # 字符串和 None 应该返回错误
- issues = validate_dpi_value("96")
- assert len(issues) > 0
-
- issues = validate_dpi_value(None)
- assert len(issues) > 0
diff --git a/utils/image_utils.py b/utils/image_utils.py
deleted file mode 100644
index 18b7259..0000000
--- a/utils/image_utils.py
+++ /dev/null
@@ -1,149 +0,0 @@
-"""
-图片处理工具模块
-
-提供图片适配模式处理、像素与英寸转换等功能。
-"""
-
-from PIL import Image, ImageOps
-from typing import Tuple, Optional
-
-
-def inches_to_pixels(inches: float, dpi: int = 96) -> float:
- """
- 将英寸转换为像素
-
- Args:
- inches: 英寸值
- dpi: DPI(每英寸像素数),默认 96
-
- Returns:
- 像素值
- """
- return inches * dpi
-
-
-def pixels_to_inches(pixels: float, dpi: int = 96) -> float:
- """
- 将像素转换为英寸
-
- Args:
- pixels: 像素值
- dpi: DPI(每英寸像素数),默认 96
-
- Returns:
- 英寸值
- """
- return pixels / dpi
-
-
-def create_canvas_with_background(size: Tuple[int, int], background_color: str) -> Image.Image:
- """
- 创建带背景色的画布
-
- Args:
- size: 画布尺寸 (width, height)
- background_color: 背景颜色(#RRGGBB 或 #RGB 格式)
-
- Returns:
- PIL Image 对象
- """
- # 验证颜色格式
- import re
- if not isinstance(background_color, str):
- raise ValueError(f"无效的颜色格式: {background_color}")
- pattern = r'^#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})$'
- if not re.match(pattern, background_color):
- raise ValueError(f"无效的颜色格式: {background_color}")
-
- # 创建 RGB 模式的画布
- canvas = Image.new('RGB', size, background_color)
- return canvas
-
-
-def apply_fit_mode(
- img: Image.Image,
- box_size: Tuple[int, int],
- fit: Optional[str] = None,
- background: Optional[str] = None
-) -> Image.Image:
- """
- 应用图片适配模式
-
- Args:
- img: PIL Image 对象
- box_size: 目标尺寸 (width, height) 像素
- fit: 适配模式(stretch, contain, cover, center),默认 stretch
- background: 背景颜色(#RRGGBB 或 #RGB 格式),仅对 contain 和 center 模式有效
-
- Returns:
- 处理后的 PIL Image 对象
- """
- if fit is None or fit == 'stretch':
- # stretch 模式:直接拉伸到目标尺寸
- return img.resize(box_size, Image.Resampling.LANCZOS)
-
- elif fit == 'contain':
- # contain 模式:保持宽高比,完整显示在 box 内
- # 如果图片比 box 小,保持原始尺寸;如果比 box 大,等比缩小
- img_width, img_height = img.size
- box_width, box_height = box_size
-
- # 检查图片是否需要缩小
- if img_width <= box_width and img_height <= box_height:
- # 图片比 box 小,保持原始尺寸
- result = img
- else:
- # 图片比 box 大,使用 contain 缩小
- result = ImageOps.contain(img, box_size, Image.Resampling.LANCZOS)
-
- # 如果指定了背景色,创建画布并居中粘贴
- if background:
- canvas = create_canvas_with_background(box_size, background)
- # 计算居中位置
- offset_x = (box_size[0] - result.width) // 2
- offset_y = (box_size[1] - result.height) // 2
- canvas.paste(result, (offset_x, offset_y))
- return canvas
-
- return result
-
- elif fit == 'cover':
- # cover 模式:保持宽高比,填满 box,裁剪超出部分
- result = ImageOps.cover(img, box_size, Image.Resampling.LANCZOS)
- # ImageOps.cover 可能返回比 box_size 大的图片,需要裁剪到精确尺寸
- if result.size != box_size:
- # 从中心裁剪到目标尺寸
- left = (result.width - box_size[0]) // 2
- top = (result.height - box_size[1]) // 2
- result = result.crop((left, top, left + box_size[0], top + box_size[1]))
- return result
-
- elif fit == 'center':
- # center 模式:不缩放,居中显示,超出部分裁剪
- img_width, img_height = img.size
- box_width, box_height = box_size
-
- # 如果图片比 box 大,需要裁剪
- if img_width > box_width or img_height > box_height:
- # 计算裁剪区域(从中心裁剪)
- left = max(0, (img_width - box_width) // 2)
- top = max(0, (img_height - box_height) // 2)
- right = min(img_width, left + box_width)
- bottom = min(img_height, top + box_height)
- result = img.crop((left, top, right, bottom))
- else:
- result = img
-
- # 如果指定了背景色,创建画布并居中粘贴
- if background:
- canvas = create_canvas_with_background(box_size, background)
- # 计算居中位置
- offset_x = (box_size[0] - result.width) // 2
- offset_y = (box_size[1] - result.height) // 2
- canvas.paste(result, (offset_x, offset_y))
- return canvas
-
- return result
-
- else:
- raise ValueError(f"不支持的 fit 模式: {fit}")
diff --git a/validators/image_config.py b/validators/image_config.py
deleted file mode 100644
index 2bc8293..0000000
--- a/validators/image_config.py
+++ /dev/null
@@ -1,101 +0,0 @@
-"""
-图片配置验证器
-
-验证图片元素的 fit、background、dpi 等参数。
-"""
-
-from typing import List
-from validators.result import ValidationIssue
-import re
-
-
-def validate_fit_value(fit: str) -> List[ValidationIssue]:
- """
- 验证 fit 参数值
-
- Args:
- fit: fit 参数值
-
- Returns:
- 验证问题列表
- """
- issues = []
- valid_fits = ['stretch', 'contain', 'cover', 'center']
-
- if fit not in valid_fits:
- issues.append(ValidationIssue(
- level="ERROR",
- message=f"无效的 fit 值: {fit} (支持: {', '.join(valid_fits)})",
- location="",
- code="INVALID_FIT_VALUE"
- ))
-
- return issues
-
-
-def validate_background_color(color: str) -> List[ValidationIssue]:
- """
- 验证背景颜色格式
-
- Args:
- color: 颜色字符串
-
- Returns:
- 验证问题列表
- """
- issues = []
-
- # 检查类型
- if not isinstance(color, str):
- issues.append(ValidationIssue(
- level="ERROR",
- message=f"无效的背景颜色格式: {color} (应为 #RRGGBB 或 #RGB)",
- location="",
- code="INVALID_COLOR_FORMAT"
- ))
- return issues
-
- pattern = r'^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$'
-
- if not re.match(pattern, color):
- issues.append(ValidationIssue(
- level="ERROR",
- message=f"无效的背景颜色格式: {color} (应为 #RRGGBB 或 #RGB)",
- location="",
- code="INVALID_COLOR_FORMAT"
- ))
-
- return issues
-
-
-def validate_dpi_value(dpi: int) -> List[ValidationIssue]:
- """
- 验证 DPI 值
-
- Args:
- dpi: DPI 值
-
- Returns:
- 验证问题列表
- """
- issues = []
-
- # 检查类型
- if not isinstance(dpi, int):
- issues.append(ValidationIssue(
- level="ERROR",
- message=f"DPI 值必须是整数: {dpi}",
- location="",
- code="INVALID_DPI_TYPE"
- ))
- return issues
-
- if dpi < 72 or dpi > 300:
- issues.append(ValidationIssue(
- level="WARNING",
- message=f"DPI 值可能不合适: {dpi} (建议范围: 72-300)",
- location="",
- code="DPI_OUT_OF_RANGE"
- ))
-
- return issues
diff --git a/validators/validator.py b/validators/validator.py
index 67183f4..1a2a09e 100644
--- a/validators/validator.py
+++ b/validators/validator.py
@@ -9,7 +9,6 @@ from loaders.yaml_loader import load_yaml_file, validate_presentation_yaml, YAML
from validators.result import ValidationResult, ValidationIssue
from validators.geometry import GeometryValidator
from validators.resource import ResourceValidator
-from validators.image_config import validate_dpi_value
from core.elements import create_element
@@ -71,12 +70,6 @@ class Validator:
size_str = data.get("metadata", {}).get("size", "16:9")
slide_width, slide_height = self.SLIDE_SIZES.get(size_str, (10, 5.625))
- # 验证 DPI 配置
- dpi = data.get("metadata", {}).get("dpi")
- if dpi is not None:
- dpi_issues = validate_dpi_value(dpi)
- self._categorize_issues(dpi_issues, errors, warnings, infos)
-
# 初始化子验证器
geometry_validator = GeometryValidator(slide_width, slide_height)
resource_validator = ResourceValidator(
diff --git a/yaml2pptx.py b/yaml2pptx.py
index cbdc89a..28784bd 100644
--- a/yaml2pptx.py
+++ b/yaml2pptx.py
@@ -194,7 +194,7 @@ def handle_convert(args):
# 2. 创建 PPTX 生成器
log_info(f"创建演示文稿 ({pres.size})...")
- generator = PptxGenerator(pres.size, pres.dpi)
+ generator = PptxGenerator(pres.size)
# 3. 渲染所有幻灯片
slides_data = pres.data.get('slides', [])