mirror of
https://github.com/ZhuLinsen/daily_stock_analysis
synced 2026-09-20 02:43:35 +08:00
fix: 修复 Linux 分享图中文缺失 (#2270)
* fix: render CJK text in Linux share images * fix(review-feedback-2270): 补到 PR 附件或评论里,和本次渲染修复说明保持一致 * fix: preserve report language contract in share images
This commit is contained in:
@@ -34,6 +34,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
gosu \
|
||||
wkhtmltopdf \
|
||||
fontconfig \
|
||||
fonts-noto-cjk \
|
||||
libjpeg62-turbo \
|
||||
libxrender1 \
|
||||
libxext6 \
|
||||
|
||||
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
- [修复] Linux/Docker 分享图补齐 Noto CJK 字体与中韩文字体栈,避免 PNG 只显示数字和英文、中文或韩文内容消失。
|
||||
<!-- 新条目格式:- [类型] 描述(类型取值:新功能/改进/修复/文档/测试/chore)-->
|
||||
<!-- 每条独立一行追加到本段末尾,无需分类标题,合并时冲突最小 -->
|
||||
|
||||
|
||||
@@ -1269,7 +1269,7 @@ PUSHOVER_API_TOKEN=your_api_token
|
||||
1. **imgkit**:已包含在 `requirements.txt`,执行 `pip install -r requirements.txt` 时会自动安装
|
||||
2. **wkhtmltopdf**(默认引擎):系统级依赖,需手动安装:
|
||||
- **macOS**:`brew install wkhtmltopdf`
|
||||
- **Debian/Ubuntu**:`apt install wkhtmltopdf`
|
||||
- **Debian/Ubuntu**:`apt install wkhtmltopdf fonts-noto-cjk`(用于正确渲染项目支持的中文和韩文报告)
|
||||
3. **markdown-to-file**(可选,emoji 支持更好):`npm i -g markdown-to-file`,并设置 `MD2IMG_ENGINE=markdown-to-file`
|
||||
4. **Playwright**(可选,适合 Web 服务):在 `apps/dsa-web` 执行 `npm ci` 和 `npx playwright install chromium`,并设置 `MD2IMG_ENGINE=playwright`
|
||||
|
||||
|
||||
@@ -126,7 +126,7 @@ Go to your forked repo → `Settings` → `Secrets and variables` → `Actions`
|
||||
| `SAVE_CONTEXT_SNAPSHOT` | Whether to persist analysis-history `context_snapshot`; defaults to `true`. Set to `false` or use `--no-context-snapshot` to stop persisting the full snapshot | Optional |
|
||||
| `MARKDOWN_TO_IMAGE_CHANNELS` | Notification channels that receive report images: telegram,wechat,custom,email,slack | Optional |
|
||||
| `MARKDOWN_TO_IMAGE_MAX_CHARS` | Skip image conversion above this Markdown length (default 15000) | Optional |
|
||||
| `MD2IMG_ENGINE` | Image renderer: `wkhtmltoimage` (default), `markdown-to-file`, or `playwright` | Optional |
|
||||
| `MD2IMG_ENGINE` | Image renderer: `wkhtmltoimage` (default), `markdown-to-file`, or `playwright`. Debian/Ubuntu deployments using `wkhtmltoimage` should install both `wkhtmltopdf` and `fonts-noto-cjk` so supported Chinese and Korean report text renders correctly | Optional |
|
||||
| `SHARE_IMAGE_XIAOHONGSHU_URL` | Xiaohongshu profile URL shown in share images; empty disables the link | Optional |
|
||||
| `SHARE_IMAGE_XIAOHONGSHU_HANDLE` | Xiaohongshu nickname shown in share images; when all Xiaohongshu settings are empty, uses bundled nickname `@霸天土小豆` | Optional |
|
||||
| `SHARE_IMAGE_XIAOHONGSHU_QR_PATH` | QR image path, absolute or relative to the project root; when all Xiaohongshu settings are empty, uses the bundled QR | Optional |
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
|
||||
`MARKDOWN_TO_IMAGE_CHANNELS`、`MD2IMG_ENGINE`、`MARKDOWN_TO_IMAGE_MAX_CHARS` 继续控制哪些通知渠道转图、使用哪个引擎以及最大输入长度。转换失败时仍回退为文本通知。
|
||||
|
||||
分享图需要运行环境提供对应语言字体。官方 Docker 镜像已内置 Noto CJK 字体;Debian/Ubuntu 源码部署使用默认 `wkhtmltoimage` 引擎时,应安装 `wkhtmltopdf fonts-noto-cjk`。如果只安装转图工具而缺少 CJK 字体,中文或韩文可能在 PNG 中消失,只剩数字、英文和边框。
|
||||
`fonts-noto-cjk` 是 Debian 字体包名,不代表新增日文报告语言。项目的报告输出仍只支持 `REPORT_LANGUAGE=zh|en|ko`;日股个股和日本市场复盘中的日文原生名称由通用 Noto CJK fallback 覆盖,页面语言仍跟随所选报告语言,不会根据 `7203.T` 或 `region=jp` 切换成未支持的 `ja` 输出。
|
||||
|
||||
小红书品牌使用以下可选配置。全部留空时展示仓库内置二维码及昵称 `@霸天土小豆`;配置任一自定义值后仅使用这组自定义品牌信息,避免把自定义账号与默认二维码混合:
|
||||
|
||||
```dotenv
|
||||
|
||||
@@ -365,6 +365,12 @@ def _poster_text(language: str, key: str) -> str:
|
||||
return _POSTER_TEXT.get(language, _POSTER_TEXT["zh"]).get(key, _POSTER_TEXT["zh"].get(key, key))
|
||||
|
||||
|
||||
def _poster_html_language(language: str) -> str:
|
||||
"""Map the supported report language, not the market region, to HTML lang."""
|
||||
|
||||
return {"zh": "zh-CN", "en": "en", "ko": "ko"}.get(language, "zh-CN")
|
||||
|
||||
|
||||
def _poster_label(language: str, label: str) -> str:
|
||||
translated = _POSTER_LABELS.get(language, {}).get(label)
|
||||
if translated:
|
||||
@@ -2111,7 +2117,7 @@ def build_share_image_html(
|
||||
poster_branding = branding or ShareImageBranding()
|
||||
|
||||
return f"""<!DOCTYPE html>
|
||||
<html lang="{'en' if language == 'en' else 'ko' if language == 'ko' else 'zh-CN'}">
|
||||
<html lang="{_poster_html_language(language)}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=1080, initial-scale=1">
|
||||
@@ -2119,7 +2125,9 @@ def build_share_image_html(
|
||||
<style>
|
||||
* {{ box-sizing: border-box; }}
|
||||
html, body {{ margin: 0; width: 1080px; background: #eef4fd; }}
|
||||
body {{ color: #081b40; font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", "PingFang SC", "Microsoft YaHei", Arial, sans-serif; font-size: 22px; line-height: 1.5; -webkit-font-smoothing: antialiased; }}
|
||||
body {{ color: #081b40; font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", "Segoe UI", "Noto Sans CJK SC", "Noto Sans CJK KR", Arial, sans-serif; font-size: 22px; line-height: 1.5; -webkit-font-smoothing: antialiased; }}
|
||||
html[lang="zh-CN"] body {{ font-family: "Noto Sans CJK SC", "Noto Sans SC", "PingFang SC", "Microsoft YaHei", -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif; }}
|
||||
html[lang="ko"] body {{ font-family: "Noto Sans CJK KR", "Noto Sans KR", "Apple SD Gothic Neo", "Malgun Gothic", -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif; }}
|
||||
.poster {{ width: 1080px; padding: 38px 34px 24px; border: 1px solid #aebdd4; border-radius: 28px; background: radial-gradient(circle at 92% 6%, rgba(48,123,255,.15), transparent 260px), linear-gradient(180deg,#fff 0%,#fbfdff 78%,#eef5ff 100%); }}
|
||||
.poster-header {{ display: table; width: 100%; margin-bottom: 28px; }}
|
||||
.brand, .meta {{ display: table-cell; vertical-align: middle; }}
|
||||
|
||||
@@ -1967,3 +1967,60 @@ def test_desktop_backend_build_scripts_bundle_share_image_assets():
|
||||
for relative_path in ("scripts/build-backend.ps1", "scripts/build-backend-macos.sh"):
|
||||
content = (root / relative_path).read_text(encoding="utf-8")
|
||||
assert "src/assets/share_image" in content
|
||||
|
||||
|
||||
def test_share_image_declares_supported_cjk_fonts_and_docker_installs_them():
|
||||
html = build_share_image_html(
|
||||
"# 贵州茅台 600519 分析报告\n\n## 核心判断\n\n- 趋势偏多\n",
|
||||
generated_on=date(2026, 8, 24),
|
||||
)
|
||||
|
||||
assert 'html[lang="zh-CN"] body' in html
|
||||
assert '"Noto Sans CJK SC"' in html
|
||||
assert 'html[lang="ko"] body' in html
|
||||
assert '"Noto Sans CJK KR"' in html
|
||||
|
||||
dockerfile = (
|
||||
Path(__file__).resolve().parents[1] / "docker" / "Dockerfile"
|
||||
).read_text(encoding="utf-8")
|
||||
assert "fonts-noto-cjk \\" in dockerfile
|
||||
|
||||
|
||||
def test_japanese_stock_share_image_uses_english_report_cjk_fallback():
|
||||
html = build_share_image_html(
|
||||
"# トヨタ自動車 7203.T Analysis Report\n\n## Core Conclusion\n\n- Bullish trend.\n",
|
||||
generated_on=date(2026, 8, 24),
|
||||
structured_payload={
|
||||
"code": "7203.T",
|
||||
"name": "トヨタ自動車",
|
||||
"report_language": "en",
|
||||
},
|
||||
)
|
||||
|
||||
assert 'class="poster stock"' in html
|
||||
assert '<html lang="en">' in html
|
||||
assert '"Segoe UI", "Noto Sans CJK SC", "Noto Sans CJK KR"' in html
|
||||
assert '"Noto Sans CJK SC"' in html
|
||||
assert "トヨタ自動車" in html
|
||||
|
||||
|
||||
def test_japanese_market_share_image_uses_korean_report_font_contract():
|
||||
html = build_share_image_html(
|
||||
"# 日股市场复盘\n\n## 主要指数\n\n- 日経平均株価上涨。\n",
|
||||
generated_on=date(2026, 8, 24),
|
||||
structured_payload={
|
||||
"kind": "market_review",
|
||||
"region": "jp",
|
||||
"report_language": "ko",
|
||||
"title": "일본 시장 리뷰",
|
||||
"indices": [
|
||||
{"name": "日経平均株価", "current": 42123.45, "change_pct": 0.8},
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
assert 'class="poster market"' in html
|
||||
assert '<html lang="ko">' in html
|
||||
assert 'html[lang="ko"] body' in html
|
||||
assert '"Noto Sans CJK KR"' in html
|
||||
assert "日経平均株価" in html
|
||||
|
||||
Reference in New Issue
Block a user