release: 发布 v0.6.1 DOCX 视觉一致性修复
新增通用 CSS 到 OOXML 翻译修复,统一字体、字距、精确行距、段落、列表、表格、引用、代码块与行内代码连续性,不引入按主题 ID 分支。 新增 MdTP Mono 并统一 Serif、Sans、Mono 三字体包的 Chromium 与 DOCX 使用链;字体声明、嵌入部件和 Word/WPS 实际采用均进入硬门禁。 重建封面整页及正文语义块视觉差分,14 套主题、纵横两个方向、五组页边距共 140 个真实场景全部通过,阻断失败和诊断失败均为零。 源码服务、Docker Web API 与实际安装 Desktop 的 red-briefing 导出均包含 5 个字体部件;Word/WPS 原生渲染和逐页复核通过。修复 Docker 构建上下文与运行层复用软链接,并完善 v0.6.1 版本、发行说明和发布归集。 验证:npm test(116 个文件、616 项测试)、npm run typecheck、npm run build、git diff --check 全部通过。Desktop 安装器与 ZIP、Docker v0.6.1 镜像已生成;Windows 产物仍为未签名内部发行。
This commit is contained in:
+2
-1
@@ -110,7 +110,7 @@ RUN if [ "${REUSE_PLAYWRIGHT_RUNTIME}" = "1" ]; then \
|
||||
fi
|
||||
|
||||
COPY --from=pandoc-runtime /opt/pandoc/3.9.0.2 /opt/pandoc/3.9.0.2
|
||||
RUN ln -s /opt/pandoc/3.9.0.2/bin/pandoc /usr/local/bin/pandoc \
|
||||
RUN ln -sfn /opt/pandoc/3.9.0.2/bin/pandoc /usr/local/bin/pandoc \
|
||||
&& test "$(pandoc --version | head -n 1)" = "pandoc 3.9.0.2"
|
||||
|
||||
WORKDIR /app
|
||||
@@ -167,6 +167,7 @@ ENV HOST=127.0.0.1 \
|
||||
|
||||
RUN test -f /app/.local/font-packs/mdtp-serif-sc/1.0.0/font-pack.json
|
||||
RUN test -f /app/.local/font-packs/mdtp-sans-sc/1.0.0/font-pack.json
|
||||
RUN test -f /app/.local/font-packs/mdtp-mono/1.0.0/font-pack.json
|
||||
|
||||
USER pwuser
|
||||
|
||||
|
||||
+13
-15
@@ -3,7 +3,7 @@
|
||||
本目录提供单容器部署:Nginx 在 `8080` 端口提供前端,并将
|
||||
`/api/` 代理到同容器内的 Fastify;Fastify 复用固定版本
|
||||
Playwright Chromium 生成 PDF,并使用固定 Pandoc `3.9.0.2` 生成
|
||||
可编辑 DOCX。可选字体包只从宿主机只读挂载,不进入应用镜像。
|
||||
可编辑 DOCX。发行镜像固定内置受校验字体包,不依赖宿主机字体挂载。
|
||||
|
||||
## 启动
|
||||
|
||||
@@ -38,7 +38,7 @@ docker compose -f deploy\compose.yaml down
|
||||
可以直接指定版本化镜像名称构建:
|
||||
|
||||
```powershell
|
||||
$env:MD_TO_PDF_IMAGE = 'yixiong/md-to-pdf:v0.6.0'
|
||||
$env:MD_TO_PDF_IMAGE = 'yixiong/md-to-pdf:v0.6.1'
|
||||
docker compose -f deploy\compose.yaml build
|
||||
```
|
||||
|
||||
@@ -48,13 +48,13 @@ docker compose -f deploy\compose.yaml build
|
||||
已经保留同版本系列的已验证镜像,可在内网或软件源较慢时复用其运行层:
|
||||
|
||||
```powershell
|
||||
$env:MD_TO_PDF_IMAGE = 'yixiong/md-to-pdf:v0.6.0'
|
||||
$env:MD_TO_PDF_RUNTIME_BASE_IMAGE = 'yixiong/md-to-pdf:v0.4.5'
|
||||
$env:MD_TO_PDF_IMAGE = 'yixiong/md-to-pdf:v0.6.1'
|
||||
$env:MD_TO_PDF_RUNTIME_BASE_IMAGE = 'yixiong/md-to-pdf:v0.6.0'
|
||||
$env:MD_TO_PDF_REUSE_PLAYWRIGHT_RUNTIME = '1'
|
||||
docker compose -f deploy\compose.yaml build
|
||||
```
|
||||
|
||||
复用模式会校验基础镜像中存在 Chromium,再覆盖 v0.6.0 应用代码和生产
|
||||
复用模式会校验基础镜像中存在 Chromium,再覆盖 v0.6.1 应用代码和生产
|
||||
依赖。正式跨机器构建仍建议使用默认完整路径。
|
||||
|
||||
## 主题挂载
|
||||
@@ -76,7 +76,7 @@ docker compose -f deploy\compose.yaml up -d
|
||||
|
||||
## 内置字体包
|
||||
|
||||
v0.6.0 的发行镜像固定将受校验字体包内置到
|
||||
v0.6.1 的发行镜像固定将受校验字体包内置到
|
||||
`/app/.local/font-packs`。Compose 不挂载宿主机字体目录,因此部署端
|
||||
无需额外复制字体,Preview、PDF 和 DOCX 可以离线使用同一套原生资产。
|
||||
|
||||
@@ -84,11 +84,9 @@ v0.6.0 的发行镜像固定将受校验字体包内置到
|
||||
|
||||
```text
|
||||
<字体包根目录>/
|
||||
└── mdtp-serif-sc/
|
||||
└── 1.0.0/
|
||||
├── font-pack.json
|
||||
├── LICENSE.txt
|
||||
└── fonts/
|
||||
├── mdtp-serif-sc/1.0.0/
|
||||
├── mdtp-sans-sc/1.0.0/
|
||||
└── mdtp-mono/1.0.0/
|
||||
```
|
||||
|
||||
从源码构建镜像前,先从仓库外冻结源生成字体包,再构建镜像:
|
||||
@@ -98,15 +96,15 @@ npm run build:font-pack
|
||||
docker compose -f deploy\compose.yaml up -d
|
||||
```
|
||||
|
||||
字体二进制仍不提交到 Git。Docker 构建上下文只放行本次生成的
|
||||
`output/font-packs/root`,镜像构建会在缺少清单时立即失败。运行时注册器
|
||||
字体二进制通过 Git LFS 固定在仓库中。Docker 构建上下文只放行构建所需
|
||||
源码与本次生成的 `output/font-packs/root`,镜像构建会在缺少清单时立即失败。运行时注册器
|
||||
仍会复核许可证、文件类型、路径边界、容量和 SHA-256。更换字体包内容时
|
||||
必须提升字体包 SemVer 并重新构建镜像,不能覆盖同一个版本。
|
||||
|
||||
发布前执行真实内置字体门禁:
|
||||
|
||||
```powershell
|
||||
$env:MD_TO_PDF_IMAGE = 'yixiong/md-to-pdf:v0.6.0'
|
||||
$env:MD_TO_PDF_IMAGE = 'yixiong/md-to-pdf:v0.6.1'
|
||||
npm run verify:docker-font-pack
|
||||
```
|
||||
|
||||
@@ -124,7 +122,7 @@ npm run verify:docker-font-pack
|
||||
| `MD_TO_PDF_REUSE_PLAYWRIGHT_RUNTIME` | `0` | 是否复用基础镜像中的 Chromium |
|
||||
| `MD_TO_PDF_PORT` | `8080` | 宿主机监听端口 |
|
||||
| `MD_TO_PDF_THEME_DIR` | `../.local/themes` | 宿主机主题目录 |
|
||||
| `MD_TO_PDF_APP_VERSION` | `0.6.0` | 字体包兼容性判断使用的应用版本 |
|
||||
| `MD_TO_PDF_APP_VERSION` | `0.6.1` | 字体包兼容性判断使用的应用版本 |
|
||||
| `PDF_CONCURRENCY` | `1` | 同时执行的 PDF 任务数 |
|
||||
| `PDF_MAX_QUEUE` | `4` | 等待队列上限 |
|
||||
| `PDF_TIMEOUT_MS` | `120000` | 单次 PDF 生成超时 |
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ services:
|
||||
PDF_CONCURRENCY: "${PDF_CONCURRENCY:-1}"
|
||||
PDF_MAX_QUEUE: "${PDF_MAX_QUEUE:-4}"
|
||||
PDF_TIMEOUT_MS: "${PDF_TIMEOUT_MS:-120000}"
|
||||
APP_VERSION: "${MD_TO_PDF_APP_VERSION:-0.6.0}"
|
||||
APP_VERSION: "${MD_TO_PDF_APP_VERSION:-0.6.1}"
|
||||
FONT_PACK_ROOT: /app/.local/font-packs
|
||||
volumes:
|
||||
- "${MD_TO_PDF_THEME_DIR:-../.local/themes}:/app/.local/themes:ro"
|
||||
|
||||
@@ -17,7 +17,8 @@ const fontPackRoot = path.resolve(
|
||||
);
|
||||
const expectedFontPacks = [
|
||||
{ id: "mdtp-serif-sc", version: "1.0.0" },
|
||||
{ id: "mdtp-sans-sc", version: "1.0.0" }
|
||||
{ id: "mdtp-sans-sc", version: "1.0.0" },
|
||||
{ id: "mdtp-mono", version: "1.0.0" }
|
||||
];
|
||||
|
||||
function assert(condition, message) {
|
||||
@@ -101,7 +102,7 @@ async function exportDocx(origin) {
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
markdown:
|
||||
"---\ntitle: Docker 内置字体包验收\n---\n\n# Docker 内置字体包验收\n\n这是一段可编辑中文正文。",
|
||||
"---\ntitle: Docker 内置字体包验收\n---\n\n# Docker 内置字体包验收\n\n这是一段包含行内代码 `const value = 1` 的可编辑中文正文,代码前后不得换行。",
|
||||
fileName: "Docker 内置字体包验收.md",
|
||||
language: "zh-CN",
|
||||
resources: [],
|
||||
@@ -174,7 +175,7 @@ const environment = {
|
||||
...buildEnvironment,
|
||||
MD_TO_PDF_PORT: String(port),
|
||||
MD_TO_PDF_THEME_DIR: themeRoot,
|
||||
MD_TO_PDF_APP_VERSION: "0.6.0"
|
||||
MD_TO_PDF_APP_VERSION: "0.6.1"
|
||||
};
|
||||
const compose = [
|
||||
"compose",
|
||||
@@ -214,10 +215,13 @@ try {
|
||||
);
|
||||
const css = await cssResponse.text();
|
||||
assert(cssResponse.ok, "主题 CSS 请求失败");
|
||||
assert(
|
||||
css.includes("md-to-pdf-font-packs:mdtp-serif-sc@1.0.0"),
|
||||
"Docker 内置字体包未进入主题 CSS"
|
||||
);
|
||||
assert(css.includes("md-to-pdf-font-packs:"), "主题 CSS 缺少字体包标记");
|
||||
for (const pack of expectedFontPacks) {
|
||||
assert(
|
||||
css.includes(`${pack.id}@${pack.version}`),
|
||||
`Docker 内置字体包未进入主题 CSS:${pack.id}@${pack.version}`
|
||||
);
|
||||
}
|
||||
const assetPath = css.match(
|
||||
/(\/api\/font-packs\/[^"'()\s]+\/web\?v=[a-f0-9]+)/u
|
||||
)?.[1];
|
||||
@@ -241,6 +245,17 @@ try {
|
||||
assert(fontParts.length >= 2, `DOCX 字体部件不足:${fontParts.length}`);
|
||||
const fontTable = strFromU8(entries["word/fontTable.xml"]);
|
||||
assert(fontTable.includes("MdTP Serif SC"), "字体包未进入 DOCX 字体表");
|
||||
assert(fontTable.includes("MdTP Mono"), "等宽字体包未进入 DOCX 字体表");
|
||||
const documentXml = strFromU8(entries["word/document.xml"]);
|
||||
const inlineCodeParagraph = documentXml
|
||||
.match(/<w:p(?:\s[^>]*)?>[\s\S]*?<\/w:p>/gu)
|
||||
?.find(
|
||||
(paragraph) =>
|
||||
paragraph.includes("这是一段包含行内代码") &&
|
||||
paragraph.includes("const value = 1") &&
|
||||
paragraph.includes("代码前后不得换行")
|
||||
);
|
||||
assert(inlineCodeParagraph, "行内代码及前后正文未保持在同一 Word 段落");
|
||||
|
||||
process.stdout.write(
|
||||
`${JSON.stringify(
|
||||
|
||||
Reference in New Issue
Block a user