Files
cjk-autospace/docs/index.src.html
T
admin f75382c9b5 docs: changelog 2026-06-13 標點擠壓句界例外 + index 連續擠壓行為說明補齊
新增 changelog 2026-06-13 條目(句末點號接開類保留右空白;。「/?「 全角式與開明式
皆 1.5em、對稱於 。」;例外僅限句末點號+開類,,「 與 」「/》《 仍 1em;半角式不
回歸;唯一改動點 jiyaAdjacencyPass.squeeze() 之 stopBeforeOpen 守衛)+md-twin 同步。

index 連續擠壓 callout 與 md-twin 補句界例外,使行為說明最新且完整。changelog/index
HTML 經 build.sh 重建。
2026-06-13 15:41:42 +08:00

271 lines
29 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>聚珍(Juzhen</title>
<meta name="generated-at" content="2026-06-02">
<meta name="source" content="cjk-autospace · 使用文檔">
<!-- RUNTIME_CSS -->
</head>
<body data-toc="1">
<header class="page">
<div class="meta">
<h1>聚珍(Juzhen</h1>
<p class="subtitle">現代簡/繁中文網頁排版套件——Han.css 之現代化重寫,複製乾淨、依 lang 全角/開明切換、justify 句末標點單份間距、跨瀏覽器一致。</p>
<p class="timestamp"><time>2026-06-02</time> · <span>cjk-autospace · 使用文檔</span></p>
</div>
<div class="actions">
<!-- ACTIONS -->
</div>
</header>
<main>
<!-- BODY -->
<p class="lede">聚珍(Juzhen)是現代簡/繁中文網頁排版套件,Han.css 之現代化重寫,沿其功能版圖,並針對四點作調整:複製乾淨(中西間隙不入剪貼板)、依 <code>lang</code> 全角/開明自動切換、<code>justify</code> 下句末標點只佔一份間距、跨瀏覽器一致。倉庫名仍為 <code>cjk-autospace</code>;公開 API 為 <code>createJuzhen</code> /全域 <code>Juzhen</code>;注入之自訂元素前綴 <code>jz-</code></p>
<p>本版(2.x)為從零重寫。v1 之 <code>createCjkAutospace</code> 相容層<strong>已移除</strong>(最後一批 v1 消費端已遷移 v2 API);v1 原始碼保存於 <code>v1-autospace</code> 分支。設計契約見 <code>ARCHITECTURE.md</code></p>
<h2>安裝與構建</h2>
<pre><code>npm install
npm run build # esbuild → dist/juzhen.{mjs,iife.js,css}tsc → dist/index.d.ts
npm test # jsdom DOM 正確性測試</code></pre>
<p>產物(<code>dist/</code>,已入庫):<code>juzhen.mjs</code>ESM,供打包器/TypeScript)、<code>juzhen.iife.js</code>IIFE,全域 <code>Juzhen</code>,原生 <code>&lt;script&gt;</code> 直跑)、<code>juzhen.css</code>(合併樣式,<strong>必須引入</strong>)、<code>index.d.ts</code>(型別宣告)。</p>
<h2>用法</h2>
<h3>原生 <code>&lt;script&gt;</code></h3>
<pre><code>&lt;link rel="stylesheet" href="dist/juzhen.css"&gt;
&lt;script src="dist/juzhen.iife.js"&gt;&lt;/script&gt;
&lt;script&gt;
const jz = Juzhen.createJuzhen({ scope: { include: ".prose" } });
jz.render(); // 對 document.body 套用(僅命中 .prose 子樹)
// jz.revert(); // 還原
&lt;/script&gt;</code></pre>
<h3>ESMTypeScript</h3>
<pre><code>import { createJuzhen } from "cjk-autospace";
import "cjk-autospace/css";
createJuzhen({ lang: { default: "zh-Hant" } }).render(document.querySelector("main"));</code></pre>
<div class="callout info"><span class="label">JS 與 CSS 缺一不可</span> JS 只注入 <code>jz-*</code> 結構,所有視覺尺寸(間隙寬度、擠壓量、<code>justify</code> 原子化、字體堆疊)都在 <code>juzhen.css</code>。缺 CSS 則全部失效。</div>
<h2>已實作功能</h2>
<table>
<thead><tr><th>功能</th><th>說明</th></tr></thead>
<tbody>
<tr><td>中西間隙 <code>spacing</code></td><td>CJK↔西文邊界,<strong>margin 模型</strong>:包裹左側字、以 <code>margin-right</code> 留隙,不注入空白字元 → <code>textContent</code> 完全不變、複製緊湊、<code>justify</code> 不暴增、行首乾淨;行末由 <code>gapTrim</code> layout pass 去隙以齊右。含跨樣式邊界與 pill/隔離元素邊界(<code>code</code><code>kbd</code><code>.katex</code><code>mark</code>,兩端補隙口徑一致;pill 緊鄰標點時穿越 <code>jz-jinze</code> 仍補間隙)。贅餘作者空格一律正規化:CJK↔西文吸收為受管間隙、CJK↔CJK(含跨 inline 標籤)剝除不留隙。</td></tr>
<tr><td>長英文詞 <code>longWord</code></td><td><code>[A-Za-z]{6,}</code> 包入 <code>&lt;span lang="en"&gt;</code>,配合消費端 CSS <code>hyphens:auto</code> 原生斷詞(DOM 無連字符 → 複製不污染)。</td></tr>
<tr><td>標點擠壓 <code>jiya</code></td><td>標點 charify 為 <code>jz-char</code> <code>bd-*</code> class。三機制:baseline 政策(全角式不擠/開明式擠句內點號+括號引號/半角式全擠)+行內連續擠壓+行端擠壓。半形預設以字型 OpenType <code>halt</code> 渲染;缺 <code>halt</code> 字型可切 <code>margin</code> 後備模式(見下)。</td></tr>
<tr><td>禁則 <code>jinze</code></td><td>行首行末避頭尾:閉類/點號綁前一字(不置行首)、開類綁後一字(不置行末);只隔離邊界詞元,長引文中段仍可斷。邊界字落於<strong>透明</strong>行內樣式元素(<code>strong</code><code>em</code> 等)內時<strong>克隆切分</strong>只綁邊界詞元(避免整串入 <code>nowrap</code> 致行末整體掉行),克隆標 <code>data-jz-kind="splitoff"</code><code>revert</code> 時併回;pill/隔離/avoid 不可切、<strong>整元素綁定</strong>——pill(如行內公式 <code>.katex</code>)作為原子鄰字單位整體綁入 <code>jz-jinze</code>,絕不下潛肢解(見「行內元素三分類」)。亦為 <code>justify</code> 綁定載體。</td></tr>
<tr><td>垂懸字避免 <code>orphan</code></td><td>段末行 CJK 實義字 ≥ N(預設 2;標點不計)。方案 C(斷行層無測量):段末末 N 實義字+尾隨標點包入 <code>jz-orphan</code><code>white-space:nowrap</code>),整體不可斷 → 末行恒含之 ⇒ 末行 ≥ N。邊界字落於行內元素內時同樣<strong>克隆切分</strong>(避免長元素溢出容器)。<strong>預設開</strong>;段落級。</td></tr>
<tr><td><s>標點懸掛 <code>hanging</code></s></td><td><strong>已移除</strong>:純 CSS 機制為機制級死路(寬度守恆矛盾——墨色出去多少版心內就空多少,CSS 接觸不到斷行器)。原生 <code>hanging-punctuation</code> 為唯一正解但僅 Safari 支援、且與 inline-block 原子互斥。<code>hanging</code> 選項保留為 no-op(無效果)。詳見 ARCHITECTURE §6.5.6。</td></tr>
<tr><td>功能分級 <code>level</code></td><td>文本級(間隙、擠壓——任意文本適用)vs 段落級(禁則、垂懸字、長詞——需行/段布局)。塊級預設全功能;<code>data-jz-level="text"</code> 子樹或 <code>level:{text:"選擇器"}</code>(標題/按鈕)僅跑文本級。</td></tr>
<tr><td>justify 單份間距</td><td>每個 <code>jz-char</code><code>inline-block</code> 原子,句末標點只貢獻一份對齊伸縮量。</td></tr>
</tbody>
</table>
<p>後續(架構已預留,不返工):標點修正(簡↔繁)、著重號、注音/Ruby。</p>
<h2>選項</h2>
<pre><code>createJuzhen({
lang: {
default: "zh-Hant", // 無 lang 祖先時之預設語言標籤
style: "kaiming", // 全域擠壓預設:quanjiaokaimingbanjiao
policy: { "zh-Hant": "quanjiao" }, // 逐 lang 覆寫(最高優先)
},
scope: { root: document.body, include: ".prose", avoid: "blockquote.raw" },
spacing: true,
longWord: { minLength: 6, lang: "en" },
jiya: true, // 或物件:{ halfWidth: "halt"|"margin", bias }
jinze: true,
orphan: true, // 垂懸字避免(預設開);或 { chars: 2 }false 關
// hanging:已移除、無效果(純 CSS 機制級死路,詳見 §6.5.6)
level: { text: "h1,h2,h3,button,.ui" }, // 此選擇器子樹僅跑文本級功能
justifyAtoms: true, // 分頁器下設 false 改 inline(見下)
blocks: [ { selector: ".code-comment", features: [ "spacing" ] } ],
});</code></pre>
<p>擠壓政策優先序:<strong>逐 lang <code>policy</code> 全域 <code>style</code> 內建預設</strong>(繁→全角、簡→開明)。</p>
<h3>分塊功能選擇(HTML 屬性,就近繼承)</h3>
<pre><code>&lt;p data-juzhen="spacing jiya"&gt;&lt;/p&gt; &lt;!-- 白名單:僅此二功能 --&gt;
&lt;p data-juzhen-off="jiya"&gt;&lt;/p&gt; &lt;!-- 黑名單:停用 jiya,其餘繼承 --&gt;
&lt;div lang="zh-Hans"&gt;&lt;/div&gt; &lt;!-- lang 驅動全角/開明 --&gt;</code></pre>
<h2>標點半形:<code>halt</code> 預設與 <code>margin</code> 後備</h2>
<p>開明/半角式把標點收成「半形」,有兩種渲染機制:</p>
<table>
<thead><tr><th>機制</th><th>設定</th><th>原理</th><th>適用</th></tr></thead>
<tbody>
<tr><td><code>halt</code>(預設)</td><td><code>jiya: true</code></td><td>字型 OpenType <code>halt</code>,由字型按字形正確定位、不重疊</td><td>字型含 <code>halt</code>Noto CJK/思源系列)</td></tr>
<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"><span class="label">句末標點與連續挤压</span> 句末點號為 <code>。.!?</code>——含<strong>全角句點 <code></code></strong>U+FF0E,異於表意句號 <code></code> U+3002Noto 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>
<p>模式由 <strong><code>data-jz-halfwidth</code> 屬性所在子樹</strong>決定(CSS 後代選擇器),故粒度極靈活:</p>
<ul>
<li><strong>全域</strong><code>jiya: { halfWidth: "margin" }</code><code>render()</code> 於處理 root 自動掛屬性。</li>
<li><strong>逐區塊/逐語言</strong>:在 HTML 任一祖先手寫 <code>data-jz-halfwidth="margin"</code>(或 <code>"halt"</code>),即令該子樹用對應機制,無需另建實例。常見如「全頁 <code>halt</code>,某用無 <code>halt</code> 字型之區塊掛 <code>margin</code>」,或反之(<code>margin</code> 頁中嵌一塊 <code>halt</code> 島)。</li>
</ul>
<pre><code>&lt;!-- 全頁 halt(預設),此塊改用無 halt 字型 → 局部 margin 後備 --&gt;
&lt;section data-jz-halfwidth="margin" style="font-family: '方正书宋_GBK'"&gt;&lt;/section&gt;</code></pre>
<h3>機制與字形偏側表(<code>margin</code> 模式專用,可覆寫)</h3>
<p><code>margin</code> 模式把標點 <code>jz-char</code> 之 inline-block 寬收為 <strong>0.5em(真半形 advance</strong>,字身(<code>jz-inner</code>)以 <code>overflow</code> 溢出該盒,再依<strong>墨色偏側</strong>左移、使墨色落於 0.5em 盒內、空白溢向外側——即以 CSS 盒模型重現 <code>halt</code> 之「收字身寬+按字形定位」。相鄰兩標點各 0.5em、合佔 1em,無鄰接疊加或單側重疊問題。</p>
<p>偏側依每字墨色中心。聚珍內建<strong>參考表</strong>(依方正書宋 GBK 實測之 glyph ink 位):簡體點號 <code>,、。;:!?</code> 偏左;繁體點號居中;彎引號 <code>“”‘’</code>、角括號 <code>「」</code>、尖括號 <code>〈〉</code> 墨色強偏一側(開→<code>right</code>、閉→<code>left</code>);圓/方/書名等括號 <code>()【】《》…</code> 墨色近中→<code>center</code>。可逐 <code>lang</code> 逐字覆寫:</p>
<pre><code>createJuzhen({
jiya: {
halfWidth: "margin",
bias: {
"zh-Hans": { "": "center", "": "right" }, // 覆寫參考預設
"zh-Hant": { "。": "center" },
},
},
});
// 偏側值(決定字身於 0.5em 盒內之左移):
// left 墨偏左 → 不移(字身左對齊)
// center 墨居中 → 左移 0.25em
// right 墨偏右 → 左移 0.5em(右半入盒)
// full 無可壓縮空白 → 不收、維持全形</code></pre>
<h3>字型適配工作流(margin 模式配字)</h3>
<p>換用新字型時,依下列步驟確認半形渲染:</p>
<ol>
<li><strong><code>halt</code></strong><code>python3 tools/measure-bias.py &lt;font.ttf&gt;</code>(需 <code>pip install fonttools</code>)。首行報該字型是否含 OpenType <code>halt</code>。有則直接用預設(halt 模式),到此為止;無則用 <code>margin</code> 後備,續下。</li>
<li><strong>取偏側建議</strong>:同一工具量各標點 glyph 之墨色中心、印出建議偏側表與可貼入 <code>jiya.bias</code> 之 JSON。</li>
<li><strong>套用</strong><code>createJuzhen({ jiya: { halfWidth: "margin", bias: &lt;貼入 JSON&gt; } })</code>;多數宋體系 GBK 字型與內建參考表相符,未必需覆寫。</li>
<li><strong>目視微調</strong>:與原文對照逐字檢查——標點與鄰字<strong>重疊</strong>則偏側往「不移」方向調(<code>right→center→left</code>);標點與內容<strong>仍有空隙</strong>則往「多移」方向調(<code>left→center→right</code>);某標點無可壓空白則設 <code>full</code></li>
<li><strong>記錄</strong>:把該字型之 <code>bias</code> 覆寫存入專案設定,作為該字型之適配結果。</li>
</ol>
<p>偏側是「字身於 0.5em 盒內左移多少」的三檔量化(0/0.25/0.5em);量測值近門檻時以目視為準。<code>halt</code> 字型由字型精確定位,無此配字需求——<code>margin</code> 配字僅為缺 <code>halt</code> 字型之務實近似。</p>
<h2>CSS 基礎設定(消費端須知)</h2>
<p>JS 只注入 <code>jz-*</code> 結構,<strong>所有視覺尺寸在 <code>juzhen.css</code></strong>。消費端須備妥:</p>
<ol>
<li><strong>引入樣式(必須)</strong><code>&lt;link rel="stylesheet" href="dist/juzhen.css"&gt;</code>(或 ESM <code>import "cjk-autospace/css"</code>)。</li>
<li><strong>設定 <code>lang</code>(強烈建議)</strong>:內容容器標 <code>lang="zh-Hans"</code><code>"zh-Hant"</code><code>"ja"</code>,驅動全角/開明政策與字形 <code>locl</code></li>
<li><strong>作用域類</strong>:若設 <code>scope.include: ".juzhen"</code>,須給內容容器加對應 class<code>juzhen.css</code> 之 normalize 亦收斂於此域。</li>
<li><strong>長英文詞斷字</strong>:須由消費端 CSS 提供 <code>hyphens: auto</code>(含 <code>-webkit-hyphens</code>)。</li>
<li><strong><code>justify</code>(選用)</strong>:欲兩端對齊則自設 <code>text-align: justify</code>;句末標點單份間距與行末去隙由聚珍處理。</li>
<li><strong>字型由消費端負責</strong>:聚珍<strong></strong>內建任何 <code>font-family</code>/字型堆疊——逐語言字型完全由消費端 CSS 決定(推薦範式:逐 <code>lang</code> 設於 <code>.juzhen</code> 或帶 <code>lang</code> 之容器,<strong></strong>直接設 <code>jz-inner</code>)。聚珍僅以 <code>jz-* { font: inherit }</code> 使原子繼承消費端字型、不強加,並顯式保留 <code>locl</code>(不改寫 <code>lang</code>、不關閉 locl,故正確標 <code>lang</code> + 用支援 locl 之字型即正常)。半形擠壓依字型 OpenType <code>halt</code>;缺 <code>halt</code> 之字型須用 <code>jiya.halfWidth:"margin"</code> +按字型以 <code>jiya.bias</code> 微調。</li>
<li><strong>自動處理</strong><code>juzhen.css</code> 已於 <code>.juzhen</code> 範圍內以 <code>@supports</code> 關閉 Blink 原生 <code>text-spacing-trim</code><code>text-autospace</code>,並令注入間隙 <code>user-select: none</code>(複製乾淨)。</li>
<li><strong>段落屬性照常設、原子透明</strong>:聚珍把標點拆成 <code>inline-block</code> 原子,但已確保原子對消費端段落樣式透明——段落可正常設 <code>text-indent</code>(首行縮排)、<code>hanging-punctuation</code><code>text-decoration</code>(下劃線)等。庫內已歸零會被原子重複套用之繼承屬性、並令原子承接段落裝飾,故首行縮排正常、下劃線連續不斷,<strong>無需任何額外配置</strong></li>
</ol>
<h2>行內元素三分類:透明/隔離/整體</h2>
<p>行內元素對排版有且僅有三種口徑,<strong>所有遍歷原語共用同一判定</strong>(中西間隙、擠壓相鄰、禁則綁定、垂懸字、克隆切分皆一致);判定<strong>以元素為準、非標籤名</strong>——KaTeX 全樹皆 <code>&lt;span&gt;</code>,但 <code>.katex</code> 命中 <code>pillSelector</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>isolateSelector</code>(預設 <code>"mark"</code><code>""</code> 停用)</td><td>斷 run/斷相鄰/不可切分;<strong>兩端</strong><code>jz-hws</code> 間隙</td><td><strong>照常處理</strong>(自成脈絡)</td><td>帶背景色之 <code>&lt;mark&gt;</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>
<div class="callout"><span class="label">建議</span> 行內公式/夾注引用等獨立行內單位宜列為 pill:<code>pillSelector: "code, kbd, .katex, .cite-ref"</code>——兩側補隙、吸收作者空格、整體綁定,統一處理。</div>
<h2>分頁器(Paged.js 等)相容:<code>justifyAtoms</code></h2>
<p>預設 <code>jz-char</code><code>jz-jinze</code><code>inline-block</code>,作 <code>justify</code> 之單一對齊原子。但<strong>分頁器</strong>(如 Paged.js)之對齊引擎會把每個 inline-block 邊界當伸縮點、與 CJK inter-ideograph 疊加,於窄欄混排時字距失控、整段散架。此時設 <code>justifyAtoms: false</code>——<code>jz-char</code><code>jz-jinze</code><code>display:inline</code>(半形 <code>halt</code> 與禁則 <code>nowrap</code> 綁定均保留)。常規流式版面無需設定。</p>
<div class="callout warn"><span class="label">注意</span> <code>margin</code> 後備半形依賴 <code>inline-block</code><code>width:0.5em</code>),故 <code>justifyAtoms:false</code><code>margin</code> 模式不可兼得——分頁器場景請用含 <code>halt</code> 之字型。</div>
<h2>自定義字符分類:<code>charClass</code></h2>
<p>每個字符之分類(中文/西文、全寬標點各子類)抽象為<strong>規則集</strong>:內置默認即聚珍分類表,消費端經 <code>options.charClass</code> <strong>擴充</strong>,以覆寫 <code>spacing</code><code>jiya</code><code>jinze</code> 對特定字符之判定。語義為<strong>追加</strong>——把當前未分類字符或新符號歸入對應類。</p>
<pre><code>createJuzhen({
charClass: {
cjk: "", // 追加為中文:與西文邊界補間隙、禁則/justify 視為單字
western: "★☆", // 追加為西文(半寬標點亦歸此):與 CJK 邊界補間隙
biaodian: { // 追加為全寬標點,按子類:jiya charify/擠壓、禁則避頭尾
stop: "", // 句末(。!?類) pause 句內(,、;:類)
open: "", close: "", middle: "", liga: "",
},
},
});</code></pre>
<p><strong>典型用例</strong>:英文全角句號 <code></code>U+FF0E,異於中文句號 <code></code> U+3002)預設<strong>未分類</strong>——不 charify、不挤壓、與 CJK 間不補隙。以 <code>charClass:{ biaodian:{ stop:"" } }</code> 歸為全寬句末標點,即比照 <code></code> 處理(開明式挤半形、全角式佔全形、行首避頭尾)。</p>
<div class="callout"><span class="label">要點</span> <strong>向下兼容</strong>:不傳 <code>charClass</code> 時分類與行為與內置完全一致、且各實例隔離(覆寫不污染默認)。<strong>追加語義(v1</strong>:擴充內置類,不支援重分類/移除內置字符。結構性 ASCII(字母數字/運算符)為固定集、不開放。<code>margin</code> 模式下新增開/閉括號之墨色偏側可一併以 <code>jiya.bias</code> 指定。</div>
<h2>v1 向下相容 — 已移除</h2>
<p>v1 相容層(<code>createCjkAutospace</code> 導出、IIFE 之全域別名、<code>autospaceClass</code> 選項)<strong>已隨最後一批 v1 消費端遷移完成而移除</strong>。請改用 <code>createJuzhen(options).render(root)</code> v2 APIv1 原始碼保存於 <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> 基線)須於 ChromeSafariFirefox 三引擎目視核對——jsdom 僅驗證 DOM 結構正確性。</p>
</main>
<footer class="page">
<p>本頁為聚珍(Juzhen)使用文檔。所有文檔操作均於 docs 分支進行;main 僅存一份未追蹤之本地鏡像。MIT 授權;借鑑 Han.css/Heti 之思路與分類表,成品自含。</p>
</footer>
<!-- RUNTIME_SPRITE -->
<script type="text/markdown" id="md-source">
# 聚珍Juzhen
現代簡繁中文網頁排版套件Han.css 之現代化重寫複製乾淨 `lang` 全角開明切換`justify` 句末標點單份間距跨瀏覽器一致公開 API `createJuzhen` 全域 `Juzhen`自訂元素前綴 `jz-`
## 用法
```html
<link rel="stylesheet" href="dist/juzhen.css">
<script src="dist/juzhen.iife.js"></script>
<script>
Juzhen.createJuzhen({ scope: { include: ".prose" } }).render();
</script>
```
JS 只注入 `jz-*` 結構,所有視覺尺寸在 juzhen.css,缺一不可。
## 已實作功能
- spacing 中西間隙(margin 模型,copy-clean;含 pill/隔離元素邊界與作者空格正規化)【文本級】
- longWord 長英文詞包裝(`<span lang>` + hyphens:auto)【段落級】
- jiya 標點擠壓(全角/開明/半角;半形 halt 預設、margin 後備)【文本級】
- jinze 禁則(避頭尾、justify 綁定;透明元素克隆切分最小綁定,pill/隔離/avoid 整元素綁定)【段落級】
- orphan 垂懸字避免(方案 C,末行 CJK 實義字 ≥ N;預設開 chars=2)【段落級】
- hanging 標點懸掛:已移除(純 CSS 機制級死路:寬度守恆矛盾,CSS 接觸不到斷行器;原生 hanging-punctuation 為唯一正解但僅 Safari 支援、且與 inline-block 原子互斥;選項保留為 no-op;詳見 §6.5.6
- level 功能分級(文本級 vs 段落級;data-jz-level="text"level:{text:選擇器} 標記標題等僅跑文本級)
- justify 句末標點單份間距(jz-char inline-block 原子)
## 標點半形:halt 預設與 margin 後備
- halt(預設,`jiya: true`):字型 OpenType halt 定位,需字型支援。
- margin`jiya: { halfWidth: "margin" }`):width:0.5em+字身依墨色偏側溢出定位,以 CSS 重現 halt;供缺 halt 字型(如方正書宋 GBK,僅含 vert)。
- 模式由 `data-jz-halfwidth` 子樹屬性決定,可逐區塊/lang。
- 偏側 inkBias 給 left/center/right/full,可 `jiya.bias` 逐 lang 逐字覆寫。
- 句末點號 。.!?(含全角句點 . U+FF0E,異於 。 U+3002):全角式與開明式句末皆保持全角 1em,banjiao 挤半。
- 連續標點:對每一標點獨立判斷,若其有空白之側緊鄰另一標點即收半。,「 全角式與開明式皆 1em;。」 全角式 1.5em/開明式 1em;透明穿越 strong/em 等行內邊界。句界例外:句末點號接開類(。「/?「)保留句末點號右空白、僅收開類 → 全角式與開明式皆 1.5em(對稱於 。」);僅限句末點號+開類,,「 與 」「/》《 仍 1em。半角式句末點號 baseline 已半、仍 1em(不回歸)。
### 字型適配工作流
1. `python3 tools/measure-bias.py <font.ttf>` 驗 halt、量偏側。
2. 有 halt → 用預設;無 halt → margin 後備+偏側表。
3. 套用 jiya.bias,瀏覽器目視微調(重疊→往不移;有隙→往多移)。
## CSS 基礎設定
引入 juzhen.css(必須);設 langscope classhyphens:autotext-align:justify(選用)。字型由消費端負責:聚珍不內建 font-family,逐語言字型自設於 .juzhen 或帶 lang 之容器(含 halt 字型,缺則 margin 後備+bias);聚珍以 font:inherit 繼承消費端字型、顯式保留 locl(正確標 lang + 支援 locl 之字型即正常)。段落屬性照常設、原子透明:text-indenthanging-punctuationtext-decoration 等已由庫內統一處理(歸零過套用、令原子承接段落裝飾),首行縮排正常、下劃線連續,無需額外配置。
## 行內元素三分類
行內元素對排版三種口徑,所有遍歷原語共用同一判定(以元素為準、非標籤名):透明(styleInlines——穿越、run 延續、可克隆切分);隔離(isolateSelector,預設 "mark"——邊界斷 run/斷相鄰/不可切分、兩端補隙,內部照常處理);整體(pillSelector——內部對一切 pass 不可見,禁則時作為原子鄰字單位整體綁入 jz-jinze)。avoidscope.avoidskipAttributedata-jz-skip)以自身判定,命中即邊界。行內公式/夾注引用宜列為 pill`pillSelector: "code, kbd, .katex, .cite-ref"`。
## 分頁器相容
`justifyAtoms: false` → jz-char/jz-jinze 改 display:inline,相容 Paged.js。margin 後備與 justifyAtoms:false 不可兼得。
## 自定義字符分類 charClass
字符分類(中/西文、全寬標點各子類)抽象為規則集,內置默認,消費端經 options.charClass 追加擴充,覆寫 spacingjiyajinze 之判定。`charClass: { cjk?, western?, biaodian?:{stop,pause,open,close,middle,liga} }`。例:英文全角句號 .(U+FF0E)預設未分類,以 biaodian.stop:"" 歸為全寬句末標點即比照 。處理。向下兼容(不傳則行為不變、實例隔離);v1 為追加語義,不重分類內置;結構性 ASCII 不開放。
## v1 相容 — 已移除
v1 相容層(createCjkAutospaceIIFE 全域別名/autospaceClass)已隨最後一批 v1 消費端遷移完成而移除;請改用 createJuzhen(options).render(root)。v1 原始碼保存於 v1-autospace 分支。
MIT。借鑑 Han.css/Heti 思路與分類表,成品自含。
</script>
<!-- RUNTIME_JS -->
</body>
</html>