docs: changelog 2026-07-07 專案整理(階段一/二)+ index 三分類分層/avoid 七源/demo/19.7em
新增 changelog 2026-07-07「專案整理」條目(架構一致性:透明判定單一真相源+跨透明邊界避頭尾 +avoid 排除歸一;確認缺陷修復+清理;復驗後 lineedge 非缺陷/五處幂等不做之定性;文檔重整) +md-twin 同步。 index 同步:三分類「共用同一判定」改分層表述(isTransparent 為邏輯流單一真相源+未知行內採 嚴格白名單邊界;canSplit/lineedge 為獨立層)、透明列標「未列=邊界」、avoid 改七源+isAvoided 組合入口+skipTags 不含 code/kbd;修 19.5em→19.7em(與 main 一致);目視驗證補 demo/resize-check 與 demo/showcase 引用。changelog/index HTML 經 build.sh 重建。
This commit is contained in:
+134
-51
@@ -1,4 +1,4 @@
|
||||
<!-- built with theme=dracula, mode=report, at=2026-06-13T07:41:09Z -->
|
||||
<!-- built with theme=dracula, mode=report, at=2026-07-07T13:21:19Z -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-Hant">
|
||||
<head>
|
||||
@@ -706,6 +706,14 @@ hr.line
|
||||
}
|
||||
hr.line::before { content: none; }
|
||||
|
||||
.lede
|
||||
{
|
||||
/* 首字下沉為 float,本段行數不足以填滿下沉字高度時,浮標會探出段底,
|
||||
令次段首數行繞排、向右擠貼下沉字。將本段下沿托至下沉字下沿(高度=
|
||||
4.2em × line-height 0.85 + 頂距 8px,與下方 ::first-letter 同步),
|
||||
使次段恆起於下沉字之下。本段自然行高足夠時 min-height 不生效,無副作用。 */
|
||||
min-height: calc(4.2em * 0.85 + 8px);
|
||||
}
|
||||
.lede::first-letter
|
||||
{
|
||||
font-family: var(--font-serif);
|
||||
@@ -2323,9 +2331,10 @@ main > *:nth-child(n+6) { animation-delay: 0.46s; }
|
||||
|
||||
|
||||
/* ---------- Mode: report — Print / PDF ----------
|
||||
Dynamic single-page PDF: runtime.js setupDynamicPrintPage() measures
|
||||
content height before print and injects @page sized to fit. Slides
|
||||
mode uses per-slide pagination (page-break-after: always) instead. */
|
||||
Dynamic single-page PDF: report/behavior.js setupDynamicPrintPage()
|
||||
measures the printed height (off-screen iframe at the print column width)
|
||||
and injects @page sized to fit. Slides mode uses per-slide pagination
|
||||
(page-break-after: always) instead. */
|
||||
|
||||
/* ---------- Print ---------- */
|
||||
/* ---------- Print / PDF export ----------
|
||||
@@ -2357,9 +2366,9 @@ main > *:nth-child(n+6) { animation-delay: 0.46s; }
|
||||
@page
|
||||
{
|
||||
/* A4 fallback. Real page size is set dynamically at runtime by
|
||||
`setupDynamicPrintPage()` in runtime.js — it measures the rendered
|
||||
height before print and injects a fresh `@page` rule sized to fit
|
||||
content exactly.
|
||||
`setupDynamicPrintPage()` in report/behavior.js — it measures the
|
||||
printed height (off-screen iframe at the print column width) and
|
||||
injects a fresh `@page` rule sized to fit content exactly.
|
||||
|
||||
Margin: 0 here. The real margin lives in `body` padding (see the
|
||||
@media print rule below), which the user's print dialog cannot
|
||||
@@ -2609,7 +2618,7 @@ createJuzhen({ lang: { default: "zh-Hant" } }).render(document.querySelector("ma
|
||||
<tr><td><code>margin</code>(後備)</td><td><code>jiya: { halfWidth: "margin" }</code></td><td><code>width:0.5em</code> +字身依墨色偏側溢出定位(以 CSS 重現 halt)</td><td>字型<strong>缺</strong> <code>halt</code>(多數系統字型、GBK 老字型如方正書宋 GBK、PingFang)</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="callout warn"><span class="label">為何需要後備</span> <code>halt</code> 缺席時開明式擠壓「完全無效」(標點仍佔全形)。實測<strong>方正書宋 GBK 僅含 <code>vert</code>、無 <code>halt</code></strong> → halt 模式不擠;切 <code>margin</code> 後備後(開明式 zh-Hans),同句行寬 <code>24.0em → 19.5em</code>、單一句內點號 <code>1.0em → 0.5em</code>,半形生效。</div>
|
||||
<div class="callout warn"><span class="label">為何需要後備</span> <code>halt</code> 缺席時開明式擠壓「完全無效」(標點仍佔全形)。實測<strong>方正書宋 GBK 僅含 <code>vert</code>、無 <code>halt</code></strong> → halt 模式不擠;切 <code>margin</code> 後備後(開明式 zh-Hans),同句行寬 <code>24.0em → 19.7em</code>、單一句內點號 <code>1.0em → 0.5em</code>,半形生效。</div>
|
||||
<div class="callout"><span class="label">句末標點與連續挤压</span> 句末點號為 <code>。.!?</code>——含<strong>全角句點 <code>.</code></strong>(U+FF0E,異於表意句號 <code>。</code> U+3002,Noto CJK 有 <code>halt</code>);全角式與開明式句末皆<strong>保持全角 1em</strong>,banjiao 挤半。<strong>連續標點</strong>對每一標點獨立判斷——若其有空白之側緊鄰另一標點即收半形:故 <code>,「</code> <strong>全角式與開明式皆 1em</strong>、<code>。」</code> <strong>全角式 1.5em/開明式 1em</strong>;並透明穿越 <code>strong</code>/<code>em</code> 等行內邊界。<strong>句界例外</strong>:句末點號接開類(<code>。「</code>/<code>?「</code>)為句界,保留句末點號右側空白、僅收開類左側 → 全角式與開明式皆 <strong>1.5em</strong>(對稱於 <code>。」</code>);例外僅限句末點號+開類,句內點號 <code>,「</code> 與雙括號 <code>」「</code>/<code>》《</code> 仍兩側皆收 1em。半角式句末點號由 baseline 已半形,故仍 1em(不回歸全寬)。</div>
|
||||
|
||||
<h3>激活與粒度</h3>
|
||||
@@ -2664,16 +2673,16 @@ createJuzhen({ lang: { default: "zh-Hant" } }).render(document.querySelector("ma
|
||||
</ol>
|
||||
|
||||
<h2>行內元素三分類:透明/隔離/整體</h2>
|
||||
<p>行內元素對排版有且僅有三種口徑,<strong>所有遍歷原語共用同一判定</strong>(中西間隙、擠壓相鄰、禁則綁定、垂懸字、克隆切分皆一致);判定<strong>以元素為準、非標籤名</strong>——KaTeX 全樹皆 <code><span></code>,但 <code>.katex</code> 命中 <code>pillSelector</code> 即為整體邊界。</p>
|
||||
<p>行內元素對排版有且僅有三種口徑。<strong>邏輯文本流之遍歷原語共用單一判定</strong> <code>isTransparent</code>(中西間隙、擠壓相鄰、禁則綁定、垂懸字);<strong>未在 <code>styleInlines</code> 之未知行內標籤(自訂元素等)採嚴格白名單、一律當邊界</strong>,透明須顯式登記。判定<strong>以元素為準、非標籤名</strong>——KaTeX 全樹皆 <code><span></code>,但 <code>.katex</code> 命中 <code>pillSelector</code> 即為整體邊界。結構克隆切分 <code>canSplit</code>、行端物理版面 <code>lineedge</code> 為正當之獨立層(邊界各異,見架構)。</p>
|
||||
<table>
|
||||
<thead><tr><th>分類</th><th>選擇器</th><th>邊界</th><th>內部</th><th>典型</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td><strong>透明</strong></td><td><code>styleInlines</code></td><td>穿越——間隙/相鄰/run 皆延續</td><td>照常處理;禁則可<strong>克隆切分</strong>(最小綁定)</td><td><code>strong</code>/<code>em</code>/<code>a</code>/<code>span</code></td></tr>
|
||||
<tr><td><strong>透明</strong></td><td><code>styleInlines</code>(<strong>未列之行內標籤=邊界</strong>)</td><td>穿越——間隙/相鄰/run 皆延續</td><td>照常處理;禁則可<strong>克隆切分</strong>(最小綁定)</td><td><code>strong</code>/<code>em</code>/<code>a</code>/<code>span</code></td></tr>
|
||||
<tr><td><strong>隔離</strong></td><td><code>isolateSelector</code>(預設 <code>"mark"</code>;<code>""</code> 停用)</td><td>斷 run/斷相鄰/不可切分;<strong>兩端</strong>補 <code>jz-hws</code> 間隙</td><td><strong>照常處理</strong>(自成脈絡)</td><td>帶背景色之 <code><mark></code>、badge 類</td></tr>
|
||||
<tr><td><strong>整體</strong></td><td><code>pillSelector</code>(預設 <code>"code, kbd"</code>)</td><td>同隔離</td><td><strong>完全不可見</strong>——不 charify、不插間隙、不切分、不下潛</td><td><code>code</code>/<code>kbd</code>、行內公式 <code>.katex</code>、引用標 <code>.cite-ref</code></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>禁則遇 pill(<code>…$公式$,</code>):整個 pill 作為原子鄰字單位<strong>整體綁入</strong> <code>jz-jinze</code>(公式本不可斷,無額外代價);隔離元素同樣整綁(切分背景盒有可見接縫)。<code>scope.avoid</code>/<code>skipAttribute</code>/<code>data-jz-skip</code> 以<strong>自身</strong>判定:命中元素自身即邊界、內外皆不處理。</p>
|
||||
<p>禁則遇 pill(<code>…$公式$,</code>):整個 pill 作為原子鄰字單位<strong>整體綁入</strong> <code>jz-jinze</code>(公式本不可斷,無額外代價);隔離元素同樣整綁(切分背景盒有可見接縫)。<strong>avoid 七源</strong>(<code>isSkipped</code>/<code>skipTags</code>/SVG/<code>contentEditable</code>/<code>data-jz-skip</code>/<code>skipAttribute</code>/<code>scope.avoid</code>)以<strong>自身</strong>判定、命中即邊界;一次性檢查用組合入口 <code>isAvoided</code>(自身或祖先)。<strong><code>skipTags</code> 預設不含 <code>code</code>/<code>kbd</code></strong>——二者由 <code>pillSelector</code> 以 pill 涵蓋(三分類互斥)。</p>
|
||||
<div class="callout"><span class="label">建議</span> 行內公式/夾注引用等獨立行內單位宜列為 pill:<code>pillSelector: "code, kbd, .katex, .cite-ref"</code>——兩側補隙、吸收作者空格、整體綁定,統一處理。</div>
|
||||
|
||||
<h2>分頁器(Paged.js 等)相容:<code>justifyAtoms</code></h2>
|
||||
@@ -2699,7 +2708,7 @@ createJuzhen({ lang: { default: "zh-Hant" } }).render(document.querySelector("ma
|
||||
<p>v1 相容層(<code>createCjkAutospace</code> 導出、IIFE 之全域別名、<code>autospaceClass</code> 選項)<strong>已隨最後一批 v1 消費端遷移完成而移除</strong>。請改用 <code>createJuzhen(options).render(root)</code> v2 API;v1 原始碼保存於 <code>v1-autospace</code> 分支。</p>
|
||||
|
||||
<h2>目視驗證</h2>
|
||||
<p><code>demo/index.html</code> 載入 <code>dist/</code> 產物,簡繁並列、可切 <code>justify</code>、附 copy-clean 測試區。<code>demo/fallback.html</code> 以方正書宋 GBK(無 <code>halt</code>)對照 <code>halt</code> 失效 vs <code>margin</code> 後備生效。版面相關結論(justify 單份間距、擠壓視覺量、行端、<code>inline-block</code> 基線)須於 Chrome/Safari/Firefox 三引擎目視核對——jsdom 僅驗證 DOM 結構正確性。</p>
|
||||
<p><code>demo/index.html</code> 載入 <code>dist/</code> 產物,簡繁並列、可切 <code>justify</code>、附 copy-clean 測試區。<code>demo/fallback.html</code> 以方正書宋 GBK(無 <code>halt</code>)對照 <code>halt</code> 失效 vs <code>margin</code> 後備生效。<code>demo/resize-check.html</code> 為可拖曳容器檢查頁(驗中西間隙在各寬度恆穩、行末去隙、pill 跨行右隙)。<code>demo/showcase.html</code> 以定寬文本框並列,逐場景演示功能分級與垂懸字。版面相關結論(justify 單份間距、擠壓視覺量、行端、<code>inline-block</code> 基線、間隙隨寬度之穩定性)須於 Chrome/Safari/Firefox 三引擎目視核對——jsdom 僅驗證 DOM 結構正確性。</p>
|
||||
|
||||
</main>
|
||||
|
||||
@@ -2774,7 +2783,7 @@ JS 只注入 `jz-*` 結構,所有視覺尺寸在 juzhen.css,缺一不可。
|
||||
|
||||
## 行內元素三分類
|
||||
|
||||
行內元素對排版三種口徑,所有遍歷原語共用同一判定(以元素為準、非標籤名):透明(styleInlines——穿越、run 延續、可克隆切分);隔離(isolateSelector,預設 "mark"——邊界斷 run/斷相鄰/不可切分、兩端補隙,內部照常處理);整體(pillSelector——內部對一切 pass 不可見,禁則時作為原子鄰字單位整體綁入 jz-jinze)。avoid(scope.avoid/skipAttribute/data-jz-skip)以自身判定,命中即邊界。行內公式/夾注引用宜列為 pill:`pillSelector: "code, kbd, .katex, .cite-ref"`。
|
||||
行內元素對排版三種口徑,邏輯文本流之遍歷原語共用單一判定 isTransparent(以元素為準、非標籤名);未在 styleInlines 之未知行內標籤採嚴格白名單、一律當邊界(透明須顯式登記):透明(styleInlines——穿越、run 延續、可克隆切分);隔離(isolateSelector,預設 "mark"——邊界斷 run/斷相鄰/不可切分、兩端補隙,內部照常處理);整體(pillSelector——內部對一切 pass 不可見,禁則時作為原子鄰字單位整體綁入 jz-jinze)。結構克隆切分 canSplit、行端物理版面 lineedge 為正當之獨立層。avoid 七源(isSkipped/skipTags/SVG/contentEditable/data-jz-skip/skipAttribute/scope.avoid)以自身判定、命中即邊界(一次性用組合 isAvoided);skipTags 預設不含 code/kbd(由 pill 涵蓋、三分類互斥)。行內公式/夾注引用宜列為 pill:`pillSelector: "code, kbd, .katex, .cite-ref"`。
|
||||
|
||||
## 分頁器相容
|
||||
|
||||
@@ -3652,9 +3661,12 @@ var Juzhen = (() => {
|
||||
if (!finder.featureEnabledFor(L, PASS)) {
|
||||
return;
|
||||
}
|
||||
const lSides = blankSides(bdClassOf(L));
|
||||
const rSides = blankSides(bdClassOf(R));
|
||||
if (lSides.right) {
|
||||
const lClass = bdClassOf(L);
|
||||
const rClass = bdClassOf(R);
|
||||
const lSides = blankSides(lClass);
|
||||
const rSides = blankSides(rClass);
|
||||
const stopBeforeOpen = lClass === "bd-stop" && rClass === "bd-open";
|
||||
if (lSides.right && !stopBeforeOpen) {
|
||||
L.classList.add("jz-half");
|
||||
}
|
||||
if (rSides.left) {
|
||||
@@ -5092,7 +5104,9 @@ window.__JUZHEN_PUNCT__="quanjiao";window.__JUZHEN_JUSTIFY_ATOMS__=true;window._
|
||||
.lede/.marginalia/.references/.appendix……)不命中、維持全功能。最近祖先勝,
|
||||
故標題與 chrome 容器一旦命中,其後代(如 .slide-title 內的 .section-name/
|
||||
.slide-dot、.title-page 內的作者欄)一併降級。選擇器涵蓋三模式;某模式不存在之
|
||||
class 自然不命中。 */
|
||||
class 自然不命中。**切記只列「非段落 chrome 本身」,切勿納入包裹正文的容器**
|
||||
(如 .deck-stage/.deck/.cols-2)——否則「最近祖先勝」會把整個子樹正文一併降級、
|
||||
喪失禁則/垂懸/長詞(曾誤納 .deck-stage 致 slides 全 deck 正文無禁則、標點落行首)。 */
|
||||
level: {
|
||||
text: [
|
||||
"h1, h2, h3, h4, h5, h6", // 全部標題(含 deck-/section-/slide-/tp-/toc-title)
|
||||
@@ -5100,7 +5114,7 @@ window.__JUZHEN_PUNCT__="quanjiao";window.__JUZHEN_JUSTIFY_ATOMS__=true;window._
|
||||
".subtitle, .section-subtitle, .deck-subtitle", // 副標題
|
||||
".meta, .timestamp, .actions, .page", // report chrome
|
||||
".cover-block, .slide-foot, .toc-list", // slides 封面/頁尾/目錄條目
|
||||
".deck-nav, .deck-counter, .deck-author, .deck-institute, .deck-date, .deck-actions, .deck-stage",
|
||||
".deck-nav, .deck-counter, .deck-author, .deck-institute, .deck-date, .deck-actions",
|
||||
".title-page, .toc-page, .simple-title", // academic 前置 chrome(含 tp-/st- 欄位)
|
||||
].join(", "),
|
||||
},
|
||||
@@ -5131,9 +5145,10 @@ window.__JUZHEN_PUNCT__="quanjiao";window.__JUZHEN_JUSTIFY_ATOMS__=true;window._
|
||||
renderMath();
|
||||
wrapFlexTitleText();
|
||||
/* 一般模式於 init 立即排版。__JUZHEN_DEFER__(academic 由 build.sh 設)令此處
|
||||
跳過,改由該模式 behavior.js 於 Paged.js preview() 完成後對成品頁呼叫
|
||||
__juzhenRun__——關鍵:Paged.js chunker 對 inline-block 原子量測失準(分頁前
|
||||
排版會致窄欄散架),故先以純文字分頁、再於定版頁上加排版原子。 */
|
||||
跳過,改由該模式 behavior.js 在編號/目錄/正文標記完成後、Paged.js preview()
|
||||
**之前**手動呼叫 __juzhenRun__——使 Paged.js 直接以聚珍後的最終幾何分頁(每頁
|
||||
自然填滿、末行齊滿、無需分頁後斷縫修復)。整頁寬單欄下 chunker 正確量度聚珍
|
||||
inline-block 原子,無散架。 */
|
||||
if (!window.__JUZHEN_DEFER__) { runJuzhen(); }
|
||||
|
||||
/* ========== Feature: code-block — behavior =================================
|
||||
@@ -6042,30 +6057,28 @@ if (!window.__copyDelegated__)
|
||||
|
||||
|
||||
/* ---------- Dynamic print page sizing ----------
|
||||
Measure the document's rendered height and inject an @page rule
|
||||
sized to fit content exactly. Produces a single-page PDF on "Save
|
||||
as PDF" with no trailing blank space.
|
||||
Measure the document's true printed height and inject an @page rule
|
||||
sized to fit it exactly — a single-page PDF on "Save as PDF" with no
|
||||
trailing blank space and no overflow onto a second page.
|
||||
|
||||
Page margin: 0. Real margins live in `body` padding (runtime.css
|
||||
@media print). That keeps content width stable regardless of the
|
||||
print dialog's Margins selector — and stable width is what makes
|
||||
the screen-to-print height heuristic reliable.
|
||||
Measurement: an off-screen iframe at the print page width (210mm) with
|
||||
the document's own @media print rules un-gated reproduces the printed
|
||||
layout; its content height is the answer (see measurePrintMm). This is
|
||||
window-independent.
|
||||
|
||||
Page-height heuristic: with horizontal padding fixed (1cm each
|
||||
side), print rendering height roughly matches screen rendering
|
||||
height. Ratio = 1.0 (was 0.85 — too aggressive an under-estimate;
|
||||
caused tail clipping). The 50mm buffer absorbs the residual
|
||||
variance plus whatever vertical margin a dialog like Chrome's
|
||||
Default might add on top.
|
||||
Earlier this used `documentElement.scrollHeight` × a ratio. That broke
|
||||
because report has NO max-width (content width = 100vw−4em): on a wide
|
||||
window the screen wraps less and measures short, but print always lays
|
||||
out at the fixed 190mm column and wraps more, so the page came out too
|
||||
short and content overflowed. The wider the window, the worse it got.
|
||||
|
||||
Vertical padding (body padding-top 1.5cm + padding-bottom 1cm =
|
||||
25mm total) is reserved inside the page so the breathing room
|
||||
above the title and below the footer is preserved.
|
||||
Page margin: 0. Real margins live in `body` padding (the @media print
|
||||
rule), so they survive the print dialog's "Margins: None" and stay
|
||||
inside the measured height.
|
||||
|
||||
Escape hatch: set `window.disablePrintAutoSize = true` (in the
|
||||
DevTools console or before page load) to disable. The injected
|
||||
rule is cleared and printing falls back to the static A4 in
|
||||
runtime.css (multi-page). */
|
||||
Escape hatch: set `window.disablePrintAutoSize = true` (DevTools console
|
||||
or before load) to clear the injected rule and fall back to the static
|
||||
A4 @page in runtime.css (multi-page). */
|
||||
function setupDynamicPrintPage()
|
||||
{
|
||||
let styleEl = null;
|
||||
@@ -6079,25 +6092,95 @@ if (!window.__copyDelegated__)
|
||||
}
|
||||
return styleEl;
|
||||
}
|
||||
/* Collect this document's own `@media print` rules as unconditional
|
||||
CSS, so an off-screen iframe can reproduce the printed-page layout. */
|
||||
function collectPrintCss()
|
||||
{
|
||||
let css = "";
|
||||
Array.from(document.styleSheets).forEach((sheet) =>
|
||||
{
|
||||
let rules;
|
||||
try { rules = sheet.cssRules; }
|
||||
catch (e) { return; } /* cross-origin sheet — unreadable */
|
||||
Array.from(rules).forEach((rule) =>
|
||||
{
|
||||
if (rule.type === CSSRule.MEDIA_RULE && /print/i.test(rule.media.mediaText))
|
||||
{
|
||||
Array.from(rule.cssRules).forEach((inner) => { css += inner.cssText + "\n"; });
|
||||
}
|
||||
});
|
||||
});
|
||||
return css;
|
||||
}
|
||||
|
||||
/* Measure the true printed height: clone the already-rendered DOM (KaTeX
|
||||
+ juzhen baked in) into a hidden iframe sized to the print page width
|
||||
(210mm) with the print rules un-gated, then read its content height.
|
||||
innerHTML insertion never runs <script>, so the runtime IIFE / KaTeX /
|
||||
juzhen do NOT re-execute. scrollHeight already includes the print
|
||||
`body` padding (the un-gated @media print rule). Resolves to mm.
|
||||
|
||||
Why an iframe and not `documentElement.scrollHeight`: report mode has
|
||||
NO max-width — on-screen content width = 100vw−4em, so screen height
|
||||
tracks the window and diverged from the fixed 190mm print column
|
||||
(wide windows under-sized the page → overflow). The iframe pins the
|
||||
measurement to the real print width, making it window-independent. */
|
||||
function measurePrintMm()
|
||||
{
|
||||
return new Promise((resolve) =>
|
||||
{
|
||||
const iframe = document.createElement("iframe");
|
||||
iframe.setAttribute("aria-hidden", "true");
|
||||
iframe.style.cssText =
|
||||
"position:fixed;left:-10000px;top:0;width:210mm;height:50px;"
|
||||
+ "border:0;visibility:hidden;";
|
||||
document.body.appendChild(iframe);
|
||||
const idoc = iframe.contentDocument;
|
||||
idoc.open();
|
||||
idoc.write("<!DOCTYPE html><html><head></head><body></body></html>");
|
||||
idoc.close();
|
||||
idoc.documentElement.lang = document.documentElement.lang;
|
||||
idoc.head.innerHTML = document.head.innerHTML;
|
||||
idoc.body.innerHTML = document.body.innerHTML;
|
||||
const ps = idoc.createElement("style");
|
||||
ps.textContent = collectPrintCss();
|
||||
idoc.head.appendChild(ps);
|
||||
|
||||
function done()
|
||||
{
|
||||
const h = Math.max(idoc.documentElement.scrollHeight, idoc.body.scrollHeight);
|
||||
const mm = Math.ceil(h * 25.4 / 96);
|
||||
iframe.remove();
|
||||
resolve(mm);
|
||||
}
|
||||
/* Wait for the iframe's fonts (same @font-face, HTTP-cached from
|
||||
the parent) so glyph metrics match; rAF lets layout settle. */
|
||||
const fontsReady = (idoc.fonts && idoc.fonts.ready) || Promise.resolve();
|
||||
fontsReady.then(() => requestAnimationFrame(() => requestAnimationFrame(done)));
|
||||
});
|
||||
}
|
||||
|
||||
function update()
|
||||
{
|
||||
const el = ensureStyle();
|
||||
if (window.disablePrintAutoSize)
|
||||
{
|
||||
el.textContent = ""; /* fall back to runtime.css @page */
|
||||
el.textContent = ""; /* fall back to runtime.css static @page */
|
||||
return;
|
||||
}
|
||||
const PX_TO_MM = 25.4 / 96;
|
||||
const RATIO = 1.0;
|
||||
const PAD_V_MM = 25; /* body padding-top 15 + padding-bottom 10 */
|
||||
const BUFFER_MM = 0; /* absorbs dialog margin + heuristic variance */
|
||||
const screenH = document.documentElement.scrollHeight;
|
||||
const printContentH = Math.ceil(screenH * PX_TO_MM * RATIO);
|
||||
const pageH = printContentH + PAD_V_MM + BUFFER_MM;
|
||||
el.textContent = "@page { size: 210mm " + pageH + "mm; margin: 0; }";
|
||||
measurePrintMm().then((contentMm) =>
|
||||
{
|
||||
if (window.disablePrintAutoSize) { el.textContent = ""; return; }
|
||||
const BUFFER_MM = 10; /* dialog margin + sub-mm rounding slack */
|
||||
const pageH = contentMm + BUFFER_MM;
|
||||
el.textContent = "@page { size: 210mm " + pageH + "mm; margin: 0; }";
|
||||
});
|
||||
}
|
||||
update();
|
||||
window.addEventListener("resize", update);
|
||||
/* Print column width is fixed (190mm), so page height no longer depends
|
||||
on the window — no resize re-measure. Re-measure before printing to
|
||||
pick up dynamically expanded/collapsed content (async; the init rule
|
||||
already covers static content for the current print). */
|
||||
window.addEventListener("beforeprint", update);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user