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:
@@ -7,8 +7,10 @@ import type {
|
||||
import type { ResolvedDocxThemeStyle } from "./style-presets.js";
|
||||
import {
|
||||
DOCX_SLOT_WORD_STYLE_BINDINGS,
|
||||
PANDOC_SYNTAX_STYLE_IDS,
|
||||
collectDocxTokenFonts,
|
||||
createDocxTokenSlotMap,
|
||||
resolvePandocSyntaxStyleSlot,
|
||||
resolveTokenFonts
|
||||
} from "./token-style-map.js";
|
||||
import {
|
||||
@@ -64,7 +66,8 @@ function setRunStyle(
|
||||
"iCs",
|
||||
"u",
|
||||
"shd",
|
||||
"spacing"
|
||||
"spacing",
|
||||
"kern"
|
||||
);
|
||||
if (options.fonts) {
|
||||
setFonts(parent, options.fonts);
|
||||
@@ -106,6 +109,9 @@ function setRunStyle(
|
||||
"w:val": wordCharacterSpacingTwips(options.sizePt)
|
||||
});
|
||||
}
|
||||
appendElement(parent, WORD_NAMESPACE, "w:kern", {
|
||||
"w:val": "2"
|
||||
});
|
||||
}
|
||||
|
||||
function setParagraphSpacing(
|
||||
@@ -200,8 +206,16 @@ function fallbackFontsForSlot(
|
||||
if (slot.startsWith("heading-") || slot.endsWith("-title")) {
|
||||
return fallback.headings.fonts;
|
||||
}
|
||||
if (slot === "inline-code" || slot === "code-block") {
|
||||
return fallback.code.fonts;
|
||||
if (
|
||||
slot === "inline-code" ||
|
||||
slot === "code-block" ||
|
||||
slot === "code-block-text" ||
|
||||
slot.startsWith("code-token-")
|
||||
) {
|
||||
return {
|
||||
...fallback.code.fonts,
|
||||
eastAsia: fallback.body.fonts.eastAsia
|
||||
};
|
||||
}
|
||||
if (
|
||||
slot === "table" ||
|
||||
@@ -232,7 +246,12 @@ function fallbackFontSizeForSlot(
|
||||
if (slot.endsWith("-title")) {
|
||||
return fallback.headings.sizesPt[0];
|
||||
}
|
||||
if (slot === "inline-code" || slot === "code-block") {
|
||||
if (
|
||||
slot === "inline-code" ||
|
||||
slot === "code-block" ||
|
||||
slot === "code-block-text" ||
|
||||
slot.startsWith("code-token-")
|
||||
) {
|
||||
return fallback.code.sizePt;
|
||||
}
|
||||
if (
|
||||
@@ -251,10 +270,17 @@ function fallbackFontSizeForSlot(
|
||||
function applyTokenRunStyle(
|
||||
parent: XmlElement,
|
||||
token: DocxSlotStyleToken,
|
||||
fallbackFonts: DocxFontFamily
|
||||
fallbackFonts: DocxFontFamily,
|
||||
preserveFallbackEastAsia = false
|
||||
) {
|
||||
removeDirectChildren(parent, WORD_NAMESPACE, "kern");
|
||||
appendElement(parent, WORD_NAMESPACE, "w:kern", {
|
||||
"w:val": "2"
|
||||
});
|
||||
if (token.fontCandidates.length) {
|
||||
setFonts(parent, resolveTokenFonts(token, fallbackFonts));
|
||||
setFonts(parent, resolveTokenFonts(token, fallbackFonts, {
|
||||
preserveFallbackEastAsia
|
||||
}));
|
||||
}
|
||||
if (token.fontSizePt !== undefined) {
|
||||
removeDirectChildren(parent, WORD_NAMESPACE, "sz", "szCs");
|
||||
@@ -1176,17 +1202,77 @@ function applyTokenStyles(
|
||||
binding.styleId === "SourceCode"
|
||||
);
|
||||
}
|
||||
const runToken = binding.styleId === "SourceCode"
|
||||
? slots.get("code-block-text")?.style ?? entry.style
|
||||
: entry.style;
|
||||
applyTokenRunStyle(
|
||||
ensureDirectElement(
|
||||
target,
|
||||
WORD_NAMESPACE,
|
||||
"w:rPr"
|
||||
),
|
||||
entry.style,
|
||||
fallbackFontsForSlot(slot, fallback)
|
||||
runToken,
|
||||
fallbackFontsForSlot(slot, fallback),
|
||||
binding.styleId === "SourceCode" ||
|
||||
slot === "inline-code" ||
|
||||
slot === "code-block" ||
|
||||
slot === "code-block-text" ||
|
||||
slot.startsWith("code-token-")
|
||||
);
|
||||
}
|
||||
}
|
||||
const codeTextToken =
|
||||
slots.get("code-block-text")?.style ??
|
||||
slots.get("code-block")?.style;
|
||||
if (codeTextToken) {
|
||||
const baseStyleId = "MdSourceCodeChar";
|
||||
const baseStyle = ensureStyle(
|
||||
styles,
|
||||
baseStyleId,
|
||||
"character",
|
||||
"BodyTextChar"
|
||||
);
|
||||
applyTokenRunStyle(
|
||||
ensureDirectElement(baseStyle, WORD_NAMESPACE, "w:rPr"),
|
||||
codeTextToken,
|
||||
{
|
||||
...fallback.code.fonts,
|
||||
eastAsia: fallback.body.fonts.eastAsia
|
||||
},
|
||||
true
|
||||
);
|
||||
for (const styleId of PANDOC_SYNTAX_STYLE_IDS) {
|
||||
const target = ensureStyle(
|
||||
styles,
|
||||
styleId,
|
||||
"character",
|
||||
baseStyleId
|
||||
);
|
||||
const basedOn = firstDirectChild(
|
||||
target,
|
||||
WORD_NAMESPACE,
|
||||
"basedOn"
|
||||
);
|
||||
if (basedOn) {
|
||||
setWordAttribute(basedOn, "val", baseStyleId);
|
||||
}
|
||||
const syntaxSlot = resolvePandocSyntaxStyleSlot(styleId);
|
||||
const syntaxToken = syntaxSlot
|
||||
? slots.get(syntaxSlot)?.style
|
||||
: undefined;
|
||||
if (syntaxToken) {
|
||||
applyTokenRunStyle(
|
||||
ensureDirectElement(target, WORD_NAMESPACE, "w:rPr"),
|
||||
syntaxToken,
|
||||
{
|
||||
...fallback.code.fonts,
|
||||
eastAsia: fallback.body.fonts.eastAsia
|
||||
},
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
applyTableTokenStyles(styles, slots, fallback);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user