Spaces:
Running
Running
fix(pdf): tagged PDF显式关闭+容器CJK字体换静态TrueType——PDF体积3倍竞品根因修复(2026-10-04)——①server.js page.pdf显式tagged:false+outline:false:Puppeteer24 parsePDFOptions把tagged默认翻true(node_modules/puppeteer-core/common/util.js),每文字运行产StructElem(436轮NotebookLM实测63,171个5.65MB,xref 79,817);②Dockerfile弃装fonts-noto-cjk(CFF-in-OTC无法子集→全部汉字逐页Type3,745字典2.4MB CharProcs),改构建期从google/fonts(commit固定a85815a)下载SC/TC/JP/KR/HK变量字体+fonttools实例化400/700静态TrueType装系统(--update-name-table防family名带Thin致fontconfig miss;--no-install-recommends下需显式ca-certificates+fontconfig;fonttools走python3 -m入口);emoji字体保留(位图Type3残余0.02MB,视觉优先);真实容器端到端:同会话HTML POST实测26.66MB→8.69MB(-67%)低于竞品9.41MB,xref→3,182,Type3→45,图片5.19MB→0.69MB与竞品同量级,文本保真98.15%字符级一致,嵌入字体全TTF子集;本机pdf-test(17861)/pdf-prod(7860)已重建验证字节级一致;12平台API契约零改动,新旧插件全兼容;证据链XWXExporter/temp/test/notebooklm/2026-10-04/与解决方案API/111;HF Space待用户手动push部署
Browse files- Dockerfile +33 -2
- server.js +9 -0
Dockerfile
CHANGED
|
@@ -1,16 +1,47 @@
|
|
| 1 |
# Use Node.js 20 on Debian Bookworm (Chromium 149, fixes SVG context-stroke support)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
FROM node:20-bookworm-slim
|
| 3 |
|
| 4 |
-
# Install Chrome Dependencies
|
| 5 |
RUN apt-get update && apt-get install -y \
|
| 6 |
chromium \
|
| 7 |
-
fonts-noto-cjk \
|
| 8 |
fonts-noto-core \
|
| 9 |
fonts-noto-color-emoji \
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
libxss1 \
|
| 11 |
--no-install-recommends \
|
| 12 |
&& rm -rf /var/lib/apt/lists/*
|
| 13 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
# Set working directory
|
| 15 |
WORKDIR /app
|
| 16 |
|
|
|
|
| 1 |
# Use Node.js 20 on Debian Bookworm (Chromium 149, fixes SVG context-stroke support)
|
| 2 |
+
#
|
| 3 |
+
# ── CJK 字体策略(2026-10-04 PDF 体积根因修复,勿回退到 fonts-noto-cjk)──
|
| 4 |
+
# Debian 的 fonts-noto-cjk 是 CFF 轮廓的 OTC 集合封装,Skia/PDF 无法对它做
|
| 5 |
+
# 子集嵌入,print-to-PDF 时每个 CJK 字形都退化为 Type3 逐页画(436 轮会话实测:
|
| 6 |
+
# 745 个 Type3 字体字典 + 2.4MB CharProcs + 每页重复字形,韩文/康熙部首同样中招;
|
| 7 |
+
# 变量字体也触发 Type3,已实测)。TrueType 轮廓的静态实例则正常以 TTF 子集嵌入,
|
| 8 |
+
# 全文档共享一份。证据链见 XWXExporter 仓库 temp/test/notebooklm/2026-10-04/。
|
| 9 |
+
# 因此:构建期从 google/fonts 下载变量字体(commit 固定可复现),用 fontTools
|
| 10 |
+
# 实例化 400/700 两个 weight 装为系统字体;不装 fonts-noto-cjk。
|
| 11 |
+
# 插件 document-export-css.js 的字体栈已按语言对齐静态家族名
|
| 12 |
+
# (Noto Sans SC/TC/JP/KR/HK 优先于 CJK OTC 名)。
|
| 13 |
+
# emoji 仍用 fonts-noto-color-emoji(CBDT 位图,少量 Type3 残留,视觉优先)。
|
| 14 |
FROM node:20-bookworm-slim
|
| 15 |
|
|
|
|
| 16 |
RUN apt-get update && apt-get install -y \
|
| 17 |
chromium \
|
|
|
|
| 18 |
fonts-noto-core \
|
| 19 |
fonts-noto-color-emoji \
|
| 20 |
+
python3-fonttools \
|
| 21 |
+
fontconfig \
|
| 22 |
+
curl \
|
| 23 |
+
ca-certificates \
|
| 24 |
libxss1 \
|
| 25 |
--no-install-recommends \
|
| 26 |
&& rm -rf /var/lib/apt/lists/*
|
| 27 |
|
| 28 |
+
# google/fonts 仓库 commit 固定(2026-10-04 取)
|
| 29 |
+
ARG NOTO_FONTS_COMMIT=a85815a42757630ce188fdad368c2dfc444d4773
|
| 30 |
+
RUN set -eux; \
|
| 31 |
+
mkdir -p /usr/local/share/fonts/noto-sans-static; \
|
| 32 |
+
for fam in notosanssc:NotoSansSC notosanstc:NotoSansTC notosansjp:NotoSansJP notosanskr:NotoSansKR notosanshk:NotoSansHK; do \
|
| 33 |
+
dir="${fam%%:*}"; stem="${fam##*:}"; \
|
| 34 |
+
curl -fsSL -o "/tmp/${stem}-Var.ttf" \
|
| 35 |
+
"https://raw.githubusercontent.com/google/fonts/${NOTO_FONTS_COMMIT}/ofl/${dir}/${stem}%5Bwght%5D.ttf"; \
|
| 36 |
+
for w in 400 700; do \
|
| 37 |
+
python3 -m fontTools.varLib.instancer --update-name-table \
|
| 38 |
+
-o "/usr/local/share/fonts/noto-sans-static/${stem}-${w}.ttf" \
|
| 39 |
+
"/tmp/${stem}-Var.ttf" "wght=${w}"; \
|
| 40 |
+
done; \
|
| 41 |
+
rm -f "/tmp/${stem}-Var.ttf"; \
|
| 42 |
+
done; \
|
| 43 |
+
fc-cache -f
|
| 44 |
+
|
| 45 |
# Set working directory
|
| 46 |
WORKDIR /app
|
| 47 |
|
server.js
CHANGED
|
@@ -545,10 +545,19 @@ app.post('/api/generate_pdf', async (req, res) => {
|
|
| 545 |
// timeout: 0 = 禁用 page.pdf() 内部超时(默认 30s)
|
| 546 |
// 参考: https://stackoverflow.com/questions/69436420
|
| 547 |
// 应用层用 Promise.race 做超时控制
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 548 |
const pdfPromise = page.pdf({
|
| 549 |
format: 'A4',
|
| 550 |
printBackground: true,
|
| 551 |
preferCSSPageSize: true,
|
|
|
|
|
|
|
| 552 |
displayHeaderFooter: true,
|
| 553 |
headerTemplate: '<div></div>',
|
| 554 |
footerTemplate: `
|
|
|
|
| 545 |
// timeout: 0 = 禁用 page.pdf() 内部超时(默认 30s)
|
| 546 |
// 参考: https://stackoverflow.com/questions/69436420
|
| 547 |
// 应用层用 Promise.race 做超时控制
|
| 548 |
+
//
|
| 549 |
+
// tagged/outline 必须显式关闭:Puppeteer v24 起 parsePDFOptions 把 tagged 默认
|
| 550 |
+
// 翻成 true(node_modules/puppeteer-core/common/util.js),每段文字运行都会生成
|
| 551 |
+
// 一个 StructElem 结构元素(436 轮 NotebookLM 实测 6.3 万个对象 ≈ 5.7MB + xref
|
| 552 |
+
// 膨胀),这是 2026-10 PDF 体积为竞品 3 倍的三大根因之一(temp/test/notebooklm/
|
| 553 |
+
// 2026-10-04 实测关掉省 7.3MB/32MB)。无障碍标签对下载交付物无价值,outline
|
| 554 |
+
// 隐含 tagged,一并锁死,防止未来 puppeteer 升级再翻转默认值。
|
| 555 |
const pdfPromise = page.pdf({
|
| 556 |
format: 'A4',
|
| 557 |
printBackground: true,
|
| 558 |
preferCSSPageSize: true,
|
| 559 |
+
tagged: false,
|
| 560 |
+
outline: false,
|
| 561 |
displayHeaderFooter: true,
|
| 562 |
headerTemplate: '<div></div>',
|
| 563 |
footerTemplate: `
|