Name | photoframe JSON |
Version |
0.1.4
JSON |
| download |
home_page | None |
Summary | A CLI tool for adding watermarks to photos with lossless output and Chinese font support |
upload_time | 2025-07-27 03:33:58 |
maintainer | None |
docs_url | None |
author | None |
requires_python | >=3.12 |
license | MIT |
keywords |
cli
exif
image
photo
watermark
|
VCS |
 |
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
# Watermarker
一个基于 Python 的 CLI 工具,用于为照片添加水印并输出无损照片。支持多种模板和自动方向检测。
## 特性
- 🖼️ **多格式支持**: 支持 JPEG, PNG, TIFF, HEIC 等常见图片格式
- 🏷️ **多种模板**: 日期水印、宝宝年龄、相机参数等
- 🔄 **自动方向**: 自动检测横竖拍并调整水印位置
- 📊 **EXIF 提取**: 智能提取拍摄日期、相机信息等元数据
- 💯 **无损输出**: 保持原始图片质量和元数据
- ⚙️ **高度可定制**: 支持位置、透明度、字体大小等自定义
## 安装
本地开发使用:
```bash
git clone <repository-url>
cd watermarker
uv sync
uv run watermarker --help
```
## 使用方法
### 基础用法
```bash
# 添加日期水印
uv run watermarker add photo.jpg
# 指定输出文件
uv run watermarker add photo.jpg --output photo_with_date.jpg
# 预览模式(不保存文件)
uv run watermarker add photo.jpg --dry-run
```
### 模板选项
#### 1. 日期模板(默认)
```bash
# 基础日期水印
uv run watermarker add photo.jpg --template date
# 自定义位置和透明度
uv run watermarker add photo.jpg --template date --position bottom-left --opacity 0.6
```
#### 2. 宝宝年龄模板
```bash
# 显示宝宝年龄(需要出生日期)
uv run watermarker add photos/1.jpg --template baby --baby-birth-date 2024-01-15
# 结果示例: "2024.05.15 · 4个月3天"
```
#### 3. 相机参数模板
```bash
# 显示相机参数(类似徕卡风格)
uv run watermarker add photo.jpg --template camera
# 结果示例: "LEICA Q2 · 28mm f/1.4 ISO100 1/60s"
```
### 高级选项
```bash
# 自定义文本
uv run watermarker add photo.jpg --custom-text "我的照片"
# 调整字体大小和颜色
uv run watermarker add photo.jpg --font-size 1.5 --color white
# 设置固定边距(像素)
uv run watermarker add photo.jpg --margin 60
# 详细输出
uv run watermarker add photo.jpg --verbose
```
### 查看图片信息
```bash
# 显示图片的 EXIF 信息
uv run watermarker info photo.jpg
```
## 参数说明
| 参数 | 描述 | 默认值 |
|------|------|--------|
| `--template, -t` | 模板类型 (date/baby/camera) | date |
| `--output, -o` | 输出文件路径 | 自动生成 |
| `--position` | 水印位置 (bottom-right/bottom-left/bottom-center) | bottom-right |
| `--opacity` | 透明度 (0.0-1.0) | 0.8 |
| `--font-size` | 字体大小倍数 | 1.0 |
| `--color` | 文本颜色 | white |
| `--margin` | 固定边距像素 (10-200) | 40 |
| `--baby-birth-date` | 宝宝出生日期 (YYYY-MM-DD) | - |
| `--custom-text` | 自定义水印文本 | - |
| `--dry-run` | 预览模式,不保存文件 | false |
| `--verbose, -v` | 显示详细信息 | false |
## 支持的图片格式
- JPEG (.jpg, .jpeg)
- PNG (.png)
- TIFF (.tiff, .tif)
- BMP (.bmp)
- WebP (.webp)
## 项目结构
```
watermarker/
├── src/watermarker/
│ ├── cli.py # CLI 接口
│ ├── core/
│ │ ├── processor.py # 图像处理
│ │ ├── exif_reader.py # EXIF 读取
│ │ └── utils.py # 工具函数
│ └── templates/
│ ├── base.py # 基础模板
│ ├── date.py # 日期模板
│ ├── baby.py # 宝宝年龄模板
│ └── camera.py # 相机参数模板
├── tests/ # 测试文件
├── DESIGN.md # 技术设计文档
└── README.md
```
## 示例输出
### 日期模板
- 简单日期: `2024.05.15`
### 宝宝年龄模板
- 月龄显示: `2024.05.15 · 4个月3天`
- 岁数显示: `2024.05.15 · 2岁3个月`
### 相机参数模板
- 徕卡风格: `LEICA Q2 · 28mm f/1.4 ISO100 1/60s`
- 紧凑风格: `Canon EOS R5 | 85mm | f/2.8, 1/200s, ISO400`
## 开发
### 运行测试
```bash
uv run pytest
```
### 代码格式化
```bash
uv run black src/
uv run isort src/
```
## 许可证
MIT License
## 贡献
欢迎提交 Issue 和 Pull Request!
## 更新日志
### v0.1.0
- 初始版本
- 支持日期、宝宝年龄、相机参数三种模板
- 支持多种图片格式
- 无损输出功能
Raw data
{
"_id": null,
"home_page": null,
"name": "photoframe",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.12",
"maintainer_email": null,
"keywords": "cli, exif, image, photo, watermark",
"author": null,
"author_email": "aleksichen <your-email@example.com>",
"download_url": "https://files.pythonhosted.org/packages/a7/e7/4f007d3d89d71c75583e219ae47f604d29f37c1ae02496458490c8ad6727/photoframe-0.1.4.tar.gz",
"platform": null,
"description": "# Watermarker\n\n\u4e00\u4e2a\u57fa\u4e8e Python \u7684 CLI \u5de5\u5177\uff0c\u7528\u4e8e\u4e3a\u7167\u7247\u6dfb\u52a0\u6c34\u5370\u5e76\u8f93\u51fa\u65e0\u635f\u7167\u7247\u3002\u652f\u6301\u591a\u79cd\u6a21\u677f\u548c\u81ea\u52a8\u65b9\u5411\u68c0\u6d4b\u3002\n\n## \u7279\u6027\n\n- \ud83d\uddbc\ufe0f **\u591a\u683c\u5f0f\u652f\u6301**: \u652f\u6301 JPEG, PNG, TIFF, HEIC \u7b49\u5e38\u89c1\u56fe\u7247\u683c\u5f0f\n- \ud83c\udff7\ufe0f **\u591a\u79cd\u6a21\u677f**: \u65e5\u671f\u6c34\u5370\u3001\u5b9d\u5b9d\u5e74\u9f84\u3001\u76f8\u673a\u53c2\u6570\u7b49\n- \ud83d\udd04 **\u81ea\u52a8\u65b9\u5411**: \u81ea\u52a8\u68c0\u6d4b\u6a2a\u7ad6\u62cd\u5e76\u8c03\u6574\u6c34\u5370\u4f4d\u7f6e\n- \ud83d\udcca **EXIF \u63d0\u53d6**: \u667a\u80fd\u63d0\u53d6\u62cd\u6444\u65e5\u671f\u3001\u76f8\u673a\u4fe1\u606f\u7b49\u5143\u6570\u636e\n- \ud83d\udcaf **\u65e0\u635f\u8f93\u51fa**: \u4fdd\u6301\u539f\u59cb\u56fe\u7247\u8d28\u91cf\u548c\u5143\u6570\u636e\n- \u2699\ufe0f **\u9ad8\u5ea6\u53ef\u5b9a\u5236**: \u652f\u6301\u4f4d\u7f6e\u3001\u900f\u660e\u5ea6\u3001\u5b57\u4f53\u5927\u5c0f\u7b49\u81ea\u5b9a\u4e49\n\n## \u5b89\u88c5\n\n\u672c\u5730\u5f00\u53d1\u4f7f\u7528:\n```bash\ngit clone <repository-url>\ncd watermarker\nuv sync\nuv run watermarker --help\n```\n\n## \u4f7f\u7528\u65b9\u6cd5\n\n### \u57fa\u7840\u7528\u6cd5\n\n```bash\n# \u6dfb\u52a0\u65e5\u671f\u6c34\u5370\nuv run watermarker add photo.jpg\n\n# \u6307\u5b9a\u8f93\u51fa\u6587\u4ef6\nuv run watermarker add photo.jpg --output photo_with_date.jpg\n\n# \u9884\u89c8\u6a21\u5f0f\uff08\u4e0d\u4fdd\u5b58\u6587\u4ef6\uff09\nuv run watermarker add photo.jpg --dry-run\n```\n\n### \u6a21\u677f\u9009\u9879\n\n#### 1. \u65e5\u671f\u6a21\u677f\uff08\u9ed8\u8ba4\uff09\n```bash\n# \u57fa\u7840\u65e5\u671f\u6c34\u5370\nuv run watermarker add photo.jpg --template date\n\n# \u81ea\u5b9a\u4e49\u4f4d\u7f6e\u548c\u900f\u660e\u5ea6\nuv run watermarker add photo.jpg --template date --position bottom-left --opacity 0.6\n```\n\n#### 2. \u5b9d\u5b9d\u5e74\u9f84\u6a21\u677f\n```bash\n# \u663e\u793a\u5b9d\u5b9d\u5e74\u9f84\uff08\u9700\u8981\u51fa\u751f\u65e5\u671f\uff09\nuv run watermarker add photos/1.jpg --template baby --baby-birth-date 2024-01-15\n\n# \u7ed3\u679c\u793a\u4f8b: \"2024.05.15 \u00b7 4\u4e2a\u67083\u5929\"\n```\n\n#### 3. \u76f8\u673a\u53c2\u6570\u6a21\u677f\n```bash\n# \u663e\u793a\u76f8\u673a\u53c2\u6570\uff08\u7c7b\u4f3c\u5f95\u5361\u98ce\u683c\uff09\nuv run watermarker add photo.jpg --template camera\n\n# \u7ed3\u679c\u793a\u4f8b: \"LEICA Q2 \u00b7 28mm f/1.4 ISO100 1/60s\"\n```\n\n### \u9ad8\u7ea7\u9009\u9879\n\n```bash\n# \u81ea\u5b9a\u4e49\u6587\u672c\nuv run watermarker add photo.jpg --custom-text \"\u6211\u7684\u7167\u7247\"\n\n# \u8c03\u6574\u5b57\u4f53\u5927\u5c0f\u548c\u989c\u8272\nuv run watermarker add photo.jpg --font-size 1.5 --color white\n\n# \u8bbe\u7f6e\u56fa\u5b9a\u8fb9\u8ddd\uff08\u50cf\u7d20\uff09\nuv run watermarker add photo.jpg --margin 60\n\n# \u8be6\u7ec6\u8f93\u51fa\nuv run watermarker add photo.jpg --verbose\n```\n\n### \u67e5\u770b\u56fe\u7247\u4fe1\u606f\n\n```bash\n# \u663e\u793a\u56fe\u7247\u7684 EXIF \u4fe1\u606f\nuv run watermarker info photo.jpg\n```\n\n## \u53c2\u6570\u8bf4\u660e\n\n| \u53c2\u6570 | \u63cf\u8ff0 | \u9ed8\u8ba4\u503c |\n|------|------|--------|\n| `--template, -t` | \u6a21\u677f\u7c7b\u578b (date/baby/camera) | date |\n| `--output, -o` | \u8f93\u51fa\u6587\u4ef6\u8def\u5f84 | \u81ea\u52a8\u751f\u6210 |\n| `--position` | \u6c34\u5370\u4f4d\u7f6e (bottom-right/bottom-left/bottom-center) | bottom-right |\n| `--opacity` | \u900f\u660e\u5ea6 (0.0-1.0) | 0.8 |\n| `--font-size` | \u5b57\u4f53\u5927\u5c0f\u500d\u6570 | 1.0 |\n| `--color` | \u6587\u672c\u989c\u8272 | white |\n| `--margin` | \u56fa\u5b9a\u8fb9\u8ddd\u50cf\u7d20 (10-200) | 40 |\n| `--baby-birth-date` | \u5b9d\u5b9d\u51fa\u751f\u65e5\u671f (YYYY-MM-DD) | - |\n| `--custom-text` | \u81ea\u5b9a\u4e49\u6c34\u5370\u6587\u672c | - |\n| `--dry-run` | \u9884\u89c8\u6a21\u5f0f\uff0c\u4e0d\u4fdd\u5b58\u6587\u4ef6 | false |\n| `--verbose, -v` | \u663e\u793a\u8be6\u7ec6\u4fe1\u606f | false |\n\n## \u652f\u6301\u7684\u56fe\u7247\u683c\u5f0f\n\n- JPEG (.jpg, .jpeg)\n- PNG (.png)\n- TIFF (.tiff, .tif)\n- BMP (.bmp)\n- WebP (.webp)\n\n## \u9879\u76ee\u7ed3\u6784\n\n```\nwatermarker/\n\u251c\u2500\u2500 src/watermarker/\n\u2502 \u251c\u2500\u2500 cli.py # CLI \u63a5\u53e3\n\u2502 \u251c\u2500\u2500 core/\n\u2502 \u2502 \u251c\u2500\u2500 processor.py # \u56fe\u50cf\u5904\u7406\n\u2502 \u2502 \u251c\u2500\u2500 exif_reader.py # EXIF \u8bfb\u53d6\n\u2502 \u2502 \u2514\u2500\u2500 utils.py # \u5de5\u5177\u51fd\u6570\n\u2502 \u2514\u2500\u2500 templates/\n\u2502 \u251c\u2500\u2500 base.py # \u57fa\u7840\u6a21\u677f\n\u2502 \u251c\u2500\u2500 date.py # \u65e5\u671f\u6a21\u677f\n\u2502 \u251c\u2500\u2500 baby.py # \u5b9d\u5b9d\u5e74\u9f84\u6a21\u677f\n\u2502 \u2514\u2500\u2500 camera.py # \u76f8\u673a\u53c2\u6570\u6a21\u677f\n\u251c\u2500\u2500 tests/ # \u6d4b\u8bd5\u6587\u4ef6\n\u251c\u2500\u2500 DESIGN.md # \u6280\u672f\u8bbe\u8ba1\u6587\u6863\n\u2514\u2500\u2500 README.md\n```\n\n## \u793a\u4f8b\u8f93\u51fa\n\n### \u65e5\u671f\u6a21\u677f\n- \u7b80\u5355\u65e5\u671f: `2024.05.15`\n\n### \u5b9d\u5b9d\u5e74\u9f84\u6a21\u677f\n- \u6708\u9f84\u663e\u793a: `2024.05.15 \u00b7 4\u4e2a\u67083\u5929`\n- \u5c81\u6570\u663e\u793a: `2024.05.15 \u00b7 2\u5c813\u4e2a\u6708`\n\n### \u76f8\u673a\u53c2\u6570\u6a21\u677f\n- \u5f95\u5361\u98ce\u683c: `LEICA Q2 \u00b7 28mm f/1.4 ISO100 1/60s`\n- \u7d27\u51d1\u98ce\u683c: `Canon EOS R5 | 85mm | f/2.8, 1/200s, ISO400`\n\n## \u5f00\u53d1\n\n### \u8fd0\u884c\u6d4b\u8bd5\n```bash\nuv run pytest\n```\n\n### \u4ee3\u7801\u683c\u5f0f\u5316\n```bash\nuv run black src/\nuv run isort src/\n```\n\n## \u8bb8\u53ef\u8bc1\n\nMIT License\n\n## \u8d21\u732e\n\n\u6b22\u8fce\u63d0\u4ea4 Issue \u548c Pull Request\uff01\n\n## \u66f4\u65b0\u65e5\u5fd7\n\n### v0.1.0\n- \u521d\u59cb\u7248\u672c\n- \u652f\u6301\u65e5\u671f\u3001\u5b9d\u5b9d\u5e74\u9f84\u3001\u76f8\u673a\u53c2\u6570\u4e09\u79cd\u6a21\u677f\n- \u652f\u6301\u591a\u79cd\u56fe\u7247\u683c\u5f0f\n- \u65e0\u635f\u8f93\u51fa\u529f\u80fd",
"bugtrack_url": null,
"license": "MIT",
"summary": "A CLI tool for adding watermarks to photos with lossless output and Chinese font support",
"version": "0.1.4",
"project_urls": {
"Homepage": "https://github.com/aleksichen/watermarker",
"Issues": "https://github.com/aleksichen/watermarker/issues",
"Repository": "https://github.com/aleksichen/watermarker.git"
},
"split_keywords": [
"cli",
" exif",
" image",
" photo",
" watermark"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "a70bf43cd73f62558fe49e0103b2fe093c44015f2b03f87824fb978e17e9ea8e",
"md5": "0ca2dd29711583c32fb2fb8bc27fdf79",
"sha256": "7506b7b8b48a5c39d4c4e784cf0d48e5d39739e23fe46033958ce87a6940b31f"
},
"downloads": -1,
"filename": "photoframe-0.1.4-py3-none-any.whl",
"has_sig": false,
"md5_digest": "0ca2dd29711583c32fb2fb8bc27fdf79",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.12",
"size": 23343,
"upload_time": "2025-07-27T03:33:47",
"upload_time_iso_8601": "2025-07-27T03:33:47.699667Z",
"url": "https://files.pythonhosted.org/packages/a7/0b/f43cd73f62558fe49e0103b2fe093c44015f2b03f87824fb978e17e9ea8e/photoframe-0.1.4-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "a7e74f007d3d89d71c75583e219ae47f604d29f37c1ae02496458490c8ad6727",
"md5": "a123ec9d82b44c27a1e3ab110de33ffc",
"sha256": "6401164c75fd38b5b8d0856ab71c339a615e785bffbb0efc76ee4be3d8e308a2"
},
"downloads": -1,
"filename": "photoframe-0.1.4.tar.gz",
"has_sig": false,
"md5_digest": "a123ec9d82b44c27a1e3ab110de33ffc",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.12",
"size": 4408892,
"upload_time": "2025-07-27T03:33:58",
"upload_time_iso_8601": "2025-07-27T03:33:58.069322Z",
"url": "https://files.pythonhosted.org/packages/a7/e7/4f007d3d89d71c75583e219ae47f604d29f37c1ae02496458490c8ad6727/photoframe-0.1.4.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-07-27 03:33:58",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "aleksichen",
"github_project": "watermarker",
"github_not_found": true,
"lcname": "photoframe"
}