2890549f44
統一「行內元素對排版透明」原則在各遍歷原語之口徑,修復兩個下游 Bug 及三項同根隱患。
Bug1(pill 前標點誤加間隙):
isTransparentInline 補 JZ-CHAR/JZ-INNER。`漢字。<code>` 經禁則綁定後,
adjacentLogicalChar 之 descend 遇不透明 jz-char 既不進入亦不終止、而跳過它越過
標點取到「字」誤補 jz-hws;透明後回傳標點「。」(非 pill 鄰字)→ 不補隙,與
collectRuns 既有透明遞迴對齊。
Bug2(行內樣式元素整體掉行):
新增 core/split.ts 之 isolateBoundaryToken/splitElementAt——沿祖先鏈逐層把邊界
詞元剝入淺克隆(保留標籤/class/style/lang、去 id;標 data-jz-kind="splitoff"),
jinze/orphan 改最小綁定:`<strong>長串</strong>,` 只綁末字+逗號(非整串入 nowrap
inline-block 致行末整體掉行);段末長 strong 不再整綁溢出容器。revertPass 增延後
階段把 splitoff 併回原元素(先解包 wrap、後併 splitoff),render→revert 還原一致。
純樣式標籤白名單可切,a/abbr/ruby/帶 id 退化整綁。
審計(同根隱患):
T4 lineedge edgeRect 攀爬集改由 options.finder.styleInlines 派生(棄硬編碼子集,
免遺漏 ins/del/sub 等致行端誤判)。
T5 jiyaAdjacency 遇 pill(pillMatches)斷相鄰、不穿越(內容對排版不可見)。
T6 spacing processPills 補 per-node 功能閘(data-juzhen-off=spacing 區域內不補隙)。
文檔:README 切片表(spacing/jinze/orphan 三行)+ARCHITECTURE §4.1 透明性原則、
§4.4 splitoff 還原、§6.4 行內元素最小綁定、§5 jz-orphan 詞彙。
測試 90→102(Bug1×4、Bug2×6、T5/T6×2)。headless Chrome 實證 Bug2:段末 strong
最小切分、orphan 寬 60px 不溢出版心。
388 lines
23 KiB
Markdown
388 lines
23 KiB
Markdown
# 聚珍(Juzhen)
|
||
|
||
> 現代簡/繁中文網頁排版套件。Han.css 之現代化重寫,沿其功能版圖,並針對
|
||
> 數種情況作調整:**複製乾淨**(中西間隙不入剪貼板)、**依 `lang` 全角/開明
|
||
> 自動切換**、**`justify` 下句末標點只佔一份間距**、**跨瀏覽器一致**
|
||
> (Chrome/Edge/Safari/WebKit/Firefox)。
|
||
|
||
倉庫名仍為 `cjk-autospace`;公開 API 為 `createJuzhen` / 全域 `Juzhen`;注入之
|
||
自訂元素前綴 `jz-`。完整設計見 [`ARCHITECTURE.md`](./ARCHITECTURE.md)。
|
||
|
||
> 本版(2.x)為從零重寫。v1 之 `createCjkAutospace` API 由相容層保留(見下),
|
||
> 既有消費端不需改碼即可運行;v1 原始碼保存於 `v1-autospace` 分支。
|
||
|
||
---
|
||
|
||
## 安裝與構建
|
||
|
||
```sh
|
||
npm install
|
||
npm run build # esbuild → dist/juzhen.{mjs,iife.js,css};tsc → dist/index.d.ts
|
||
npm test # jsdom DOM 正確性測試
|
||
```
|
||
|
||
產物(`dist/`,已入庫):
|
||
|
||
| 檔 | 用途 |
|
||
|---|---|
|
||
| `dist/juzhen.mjs` | ESM,供打包器/TypeScript |
|
||
| `dist/juzhen.iife.js` | IIFE,全域 `Juzhen`,原生 `<script>` 直跑 |
|
||
| `dist/juzhen.css` | 合併樣式(**必須引入**,視覺尺寸全在此) |
|
||
| `dist/index.d.ts` | TypeScript 型別宣告 |
|
||
|
||
---
|
||
|
||
## 用法
|
||
|
||
### 原生 `<script>`
|
||
|
||
```html
|
||
<link rel="stylesheet" href="dist/juzhen.css">
|
||
<script src="dist/juzhen.iife.js"></script>
|
||
<script>
|
||
const jz = Juzhen.createJuzhen({ scope: { include: ".prose" } });
|
||
jz.render(); // 對 document.body 套用(僅命中 .prose 子樹)
|
||
// jz.revert(); // 還原
|
||
</script>
|
||
```
|
||
|
||
### ESM/TypeScript
|
||
|
||
```ts
|
||
import { createJuzhen } from "cjk-autospace";
|
||
import "cjk-autospace/css";
|
||
|
||
const jz = createJuzhen({ lang: { default: "zh-Hant" } });
|
||
jz.render(document.querySelector("main"));
|
||
```
|
||
|
||
**JS 與 CSS 缺一不可**:JS 只注入 `jz-*` 結構,所有視覺尺寸(間隙寬度、擠壓量、
|
||
`justify` 原子化、字體堆疊)都在 `juzhen.css`。
|
||
|
||
---
|
||
|
||
## 切片 1 已實作功能
|
||
|
||
| 功能 | 說明 |
|
||
|---|---|
|
||
| **中西間隙** `spacing` | CJK↔西文邊界插入 copy-clean 之 `jz-hws`(`user-select:none`,剪貼板跳過)。「西文」涵蓋拉丁(含擴充 A/B,拼音聲調 `ā ǎ ē`)、希臘、西里爾、數字、貨幣(`€ ₩ ₿`)、數學運算符等;CJK 標點(`。,「」()……` 等)不誤判為西文、不生隙。含跨樣式行內邊界與 `code`/`kbd` pill 邊界(pill 緊鄰標點時穿越 `jz-jinze` 仍補間隙;pill 跨行右隙、間隙落行末之自我應驗皆已修)。**pill 之邏輯鄰字為標點時(如 `漢字。<code>`)不誤加間隙**(標點非 pill 鄰字;`jz-char`/`jz-inner` 透明穿越,與 `collectRuns` 一致)。贅餘作者空格一律正規化:CJK↔西文吸收為受管間隙、CJK↔CJK(含跨 inline 標籤)剝除不留隙。 |
|
||
| **長英文詞** `longWord` | `[A-Za-z]{6,}` 包入 `<span lang="en">`,配合 CSS `hyphens:auto` 原生斷詞(DOM 無連字符 → 複製不污染)。 |
|
||
| **標點擠壓** `jiya` | 標點 charify 為 `jz-char`+`bd-*` class。三機制:①baseline 政策(全角式不擠/開明式擠句內點號+括號引號/半角式全擠);②行內連續擠壓(相鄰標點**雙側獨立**收——各標點若其有空白之側緊鄰另一標點即收半,故 `,「` 全角式與開明式皆 1em、`。」` 全角式 1.5em/開明式 1em;透明穿越 `strong`/`em` 等行內邊界);③行端擠壓(`lineedge` layout pass)。半形預設以字型 `halt` 渲染(按字形正確定位,居中字形如 `!?`/繁體 `。,` 不重疊;不依賴 `text-spacing-trim`)。**字型缺 `halt` 時可切 `margin` 後備模式**(見下節)。 |
|
||
| **禁則** `jinze` | 行首行末避頭尾:閉類/點號綁前一字(不置行首)、開類綁後一字(不置行末),只隔離邊界字故長引文中段仍可斷。亦為 `justify` 綁定載體。邊界字落於行內樣式元素(`strong`/`em`/`span` 等)內時**克隆切分**只綁邊界詞元(避免整個元素入 `nowrap` 致行末整體掉行、上行留巨大空隙),切出之克隆標 `data-jz-kind="splitoff"`、`revert` 時自動併回原元素;不可切元素(`a`/`abbr`/`ruby`/帶 `id`)退化為整元素綁定。 |
|
||
| **垂懸字避免** `orphan` | 段末行 CJK 實義字 ≥ N(預設 2;標點/符號不計)。方案 C(斷行層、無測量):把段末「第 N 個實義字起至段末」包入 `jz-orphan`(`white-space:nowrap`),整體不可斷 → 末行恒含之 ⇒ 末行實義字 ≥ N,任意寬度/字型成立。單行段無害。邊界字落於行內元素內時同樣**克隆切分**(只綁末 N 字、避免整個長元素入 `nowrap` 溢出容器;機制同禁則)。**預設開**(`orphan:false` 關,`{chars:n}` 調)。段落級功能。 |
|
||
| ~~**標點懸掛** `hanging`~~ | **已評估停用**(§6.5.6):純負 margin 機制不可行(墨色不出版心/滿凸拉升後字重疊/resize 重排錯亂),原生 `hanging-punctuation` 為正解但 Chrome 不支援。`hanging` 選項仍被接受但**無任何效果**;程式碼休眠待原生支援成熟。 |
|
||
| **功能分級** `level` | 功能分文本級(中西間隙、標點擠壓——任意文本片段適用)與段落級(禁則、垂懸字、懸掛、長詞——需行/段布局)。塊級元素預設全功能;`data-jz-level="text"` 子樹或選項 `level:{text:"選擇器"}`(如標題/按鈕)僅跑文本級。 |
|
||
| **justify 單份間距** | 每個 `jz-char` 為 `inline-block` 原子項,句末標點只貢獻一份對齊伸縮量。 |
|
||
|
||
後續(架構已預留,不返工):標點修正(簡↔繁)、著重號、注音/Ruby。
|
||
|
||
---
|
||
|
||
## 選項
|
||
|
||
```ts
|
||
createJuzhen({
|
||
lang: {
|
||
default: "zh-Hant", // 無 lang 祖先時之預設
|
||
policy: { "zh-Hant": "quanjiao", "zh-Hans": "kaiming" }, // 全角/開明/半角
|
||
},
|
||
scope: {
|
||
root: document.body, // 處理根;render(root) 可逐次覆寫
|
||
include: ".prose", // 啟用選擇器(opt-in):僅處理命中子樹
|
||
avoid: "blockquote.raw", // 追加排除選擇器
|
||
},
|
||
spacing: true,
|
||
longWord: { minLength: 6, lang: "en" },
|
||
jiya: true, // 或物件:{ halfWidth: "halt" | "margin", bias }
|
||
jinze: true,
|
||
orphan: true, // 垂懸字避免(預設開);或 { chars: 2 };false 關
|
||
// hanging:已停用、無效果(見「標點懸掛」一節)
|
||
level: { text: "h1,h2,h3,button,.ui" }, // 此選擇器子樹僅跑文本級功能(見下)
|
||
justifyAtoms: true, // jz-char/jz-jinze 以 inline-block 作對齊原子;
|
||
// 分頁器(Paged.js)下設 false 改 inline(見下)
|
||
blocks: [ // 分塊功能選擇
|
||
{ selector: ".code-comment", features: [ "spacing" ] },
|
||
],
|
||
});
|
||
```
|
||
|
||
### 分頁器(Paged.js 等)相容:`justifyAtoms`
|
||
|
||
預設 `jz-char`/`jz-jinze` 為 `inline-block`,作 `justify` 之單一對齊原子(句末標點
|
||
單份間距)。但**分頁器**(如 Paged.js)之對齊引擎會把每個 inline-block 邊界當伸縮點、
|
||
與 CJK inter-ideograph 疊加,於窄欄混排時字距失控、整段散架。此時設
|
||
`justifyAtoms: false`——`jz-char`/`jz-jinze` 改 `display:inline`(半形 `halt` 與禁則
|
||
`nowrap` 綁定均保留)。常規流式版面無需設定。
|
||
|
||
> 注意:`margin` 後備半形依賴 `inline-block`(`width:0.5em`),故
|
||
> `justifyAtoms:false` 與 `margin` 模式不可兼得——分頁器場景請用含 `halt` 之字型。
|
||
|
||
### 分塊功能選擇(HTML 屬性,就近繼承)
|
||
|
||
```html
|
||
<p data-juzhen="spacing jiya">…</p> <!-- 白名單:僅此二功能 -->
|
||
<p data-juzhen-off="jiya">…</p> <!-- 黑名單:停用 jiya,其餘繼承 -->
|
||
<div lang="zh-Hans">…</div> <!-- lang 驅動全角/開明 -->
|
||
```
|
||
|
||
### 功能分級:文本級 vs 段落級(`level`/`data-jz-level`)
|
||
|
||
功能分兩級:
|
||
|
||
- **文本級**(中西間隙 `spacing`、標點擠壓 `jiya`):僅依字符鄰接,**任意文本片段**(單行
|
||
標題、按鈕、單一行內元素)皆適用且想要。
|
||
- **段落級**(禁則 `jinze`、垂懸字 `orphan`、標點懸掛 `hanging`、長詞 `longWord`):依賴
|
||
行/段布局(換行、行端、段末),對單行/非段落文本無意義甚至有害。
|
||
|
||
塊級元素(`p`/`div`/`li`/`h1`…)**預設段落級(全功能)**。把標題/按鈕/UI 文字標為
|
||
**文本級**(要間隙與標點半形、不要懸掛/避孤字/避頭尾):
|
||
|
||
```html
|
||
<h1 data-jz-level="text">標題:只跑中西間隙與標點擠壓</h1>
|
||
<p data-jz-level="paragraph">…</p> <!-- 顯式全功能(巢狀於 text 子樹內可覆寫,最近祖先勝) -->
|
||
```
|
||
|
||
```ts
|
||
createJuzhen({ level: { text: "h1,h2,h3,button,.ui" } }); // 選擇器一鍵標記
|
||
```
|
||
|
||
### 標點懸掛(`hanging`)— 已評估停用
|
||
|
||
原計劃之標點懸掛(行端標點凸出版心)經 headless + 真實瀏覽器實測**確認不可行、已全面停用**。
|
||
`hanging` 選項仍被接受(API 相容)但**無任何效果**——縱使下游以 `hanging:true`/`data-juzhen`/
|
||
`blocks` 等任何方式要求,亦不會啟用(行端恒 squeeze;三重防逃逸:`lineEnd` 鎖死、`modeForChar`
|
||
降級、`applyEdge` 總開關)。
|
||
|
||
**為何不可行**(純負 `margin` 機制):
|
||
|
||
1. 句號等標點墨色偏字框左下角;安全凸出量(< 1 個 CJK 字寬 1em)只把空白半框推出版心、
|
||
**墨色仍在版心內**(視覺不懸掛)。
|
||
2. 凸出滿 1em 又**釋放一整字寬**、把下一字拉上本行、與懸掛標點**重疊**。
|
||
3. `ResizeObserver` 重排時,「自然佈局標記→施加 margin→佈局再變」致標記**錯亂**。
|
||
|
||
真正的解是原生 CSS `hanging-punctuation`,惟 **Chrome 至今不支援**(僅 Safari 較全),且與本庫
|
||
`inline-block` 對齊原子結構衝突。故 hang 之程式碼與 CSS **保留休眠**,俟原生支援成熟再議。
|
||
詳見 `ARCHITECTURE.md §6.5.6`。
|
||
|
||
---
|
||
|
||
## 標點半形:`halt` 預設與 `margin` 後備
|
||
|
||
開明/半角式把標點收成「半形」,有兩種渲染機制:
|
||
|
||
| 機制 | 設定 | 原理 | 適用 |
|
||
|---|---|---|---|
|
||
| **`halt`**(預設) | `jiya: true` | 字型 OpenType `halt`,由字型按字形正確定位、不重疊 | 字型含 `halt`(Noto CJK/思源系列) |
|
||
| **`margin`**(後備) | `jiya: { halfWidth: "margin" }` | `width:0.5em`+字身依墨色偏側溢出定位(以 CSS 重現 halt) | 字型**缺** `halt`(多數系統字型、GBK 老字型如方正書宋 GBK、PingFang) |
|
||
|
||
> 為何需要後備:`halt` 缺席時開明式擠壓「完全無效」(標點仍佔全形)。實測
|
||
> **方正書宋 GBK 僅含 `vert`、無 `halt`** → halt 模式不擠;切 `margin` 後備後,
|
||
> (開明式 zh-Hans)同句行寬 `24.0em → 19.7em`、單一句內點號 `1.0em → 0.5em`,半形生效。
|
||
|
||
> **句末標點含全角句點 `.`**:句末點號為 `。.!?`——除表意句號 `。`(U+3002)外納入
|
||
> 全角句點 `.`(U+FF0E,實測 Noto CJK 有 `halt`)。政策行為與 `。!?` 同:全角式與開明式
|
||
> 句末**保持全角 1em**;唯 banjiao(或與鄰標點連續被收)時 `halt` 收為半形 advance 0.5em。
|
||
> **`halt` 即字型之「挤壓意願」**:字型不提供某標點之 `halt`(其字形占滿字框、不宜挤半,
|
||
> 如某些字型之全角 `?`、`〈〉`)時,聚珍**信任字型、保持全角不強挤**(優雅降級);
|
||
> `margin` 後備僅用於**整體無 `halt`** 之字型,逐字型以 `jiya.bias` 調教。
|
||
|
||
### 激活與粒度
|
||
|
||
模式由 **`data-jz-halfwidth` 屬性所在子樹**決定(CSS 後代選擇器),故粒度極靈活:
|
||
|
||
- **全域**:`jiya: { halfWidth: "margin" }` → `render()` 於處理 root 自動掛屬性。
|
||
- **逐區塊/逐語言**:在 HTML 任一祖先手寫 `data-jz-halfwidth="margin"`(或
|
||
`"halt"`),即令該子樹用對應機制——無需另建實例。常見如「全頁 `halt`,某用
|
||
無 `halt` 字型之區塊掛 `margin`」,或反之(`margin` 頁中嵌一塊 `halt` 島)。
|
||
|
||
```html
|
||
<!-- 全頁 halt(預設),此塊改用無 halt 字型 → 局部 margin 後備 -->
|
||
<section data-jz-halfwidth="margin" style="font-family: '方正书宋_GBK'">…</section>
|
||
```
|
||
|
||
### 機制與字形偏側表(`margin` 模式專用,可覆寫)
|
||
|
||
`margin` 模式把標點 `jz-char` 之 inline-block 寬收為 **0.5em(真半形 advance)**,字身
|
||
(`jz-inner`)以 `overflow` 溢出該盒,再依**墨色偏側**左移、使墨色落於 0.5em 盒內、
|
||
空白溢向外側——即以 CSS 盒模型重現 `halt` 之「收字身寬+按字形定位」。相鄰兩標點各
|
||
0.5em、合佔 1em,無鄰接疊加或單側重疊問題。
|
||
|
||
偏側須知每字之墨色中心。聚珍內建**參考表**(依方正書宋 GBK 實測之 glyph ink 位):
|
||
簡體點號 `,、。;:!?` 偏左;繁體點號居中;彎引號 `“”‘’`、角括號 `「」`、尖括號
|
||
`〈〉` 墨色強偏一側(開→`right`、閉→`left`);圓/方/書名等括號 `()【】《》…` 墨色
|
||
近中→`center`。於 charify 標為 `jz-ink-{left,center,right}`。因字型字身分布各異,可逐
|
||
`lang` 逐字覆寫:
|
||
|
||
```ts
|
||
createJuzhen({
|
||
jiya: {
|
||
halfWidth: "margin",
|
||
bias: {
|
||
"zh-Hans": { ",": "center", "(": "right" }, // 覆寫參考預設
|
||
"zh-Hant": { "。": "center" },
|
||
},
|
||
},
|
||
});
|
||
// 偏側值(決定字身於 0.5em 盒內之左移):
|
||
// left 墨偏左 → 不移(字身左對齊)
|
||
// center 墨居中 → 左移 0.25em
|
||
// right 墨偏右 → 左移 0.5em(右半入盒)
|
||
// full 無可壓縮空白 → 不收、維持全形
|
||
```
|
||
|
||
> `halt` 模式不讀偏側表(由字型負責定位)。偏側表僅在 `margin` 後備模式生效。
|
||
> `demo/fallback.html` 用方正書宋 GBK 對照 halt 失效 vs margin 生效。配字工作流見下節。
|
||
|
||
### 字型適配工作流(margin 模式配字)
|
||
|
||
換用新字型時,依下列步驟確認半形渲染:
|
||
|
||
1. **驗 `halt`**:`python3 tools/measure-bias.py <font.ttf>`(需 `pip install fonttools`)。
|
||
首行報該字型是否含 OpenType `halt`。
|
||
- **有 `halt`** → 直接用預設(`jiya: true`,halt 模式),無需配字,到此為止。
|
||
- **無 `halt`**(多數系統字型、GBK 老字型)→ 用 `margin` 後備,續下。
|
||
2. **取偏側建議**:同一工具同時量各標點 glyph 之墨色中心、印出建議偏側表
|
||
(`left|center|right|full`)與可貼入 `jiya.bias` 之 JSON。
|
||
3. **套用**:`createJuzhen({ jiya: { halfWidth: "margin", bias: <貼入 JSON> } })`;
|
||
或全用內建參考表(多數宋體系 GBK 字型與參考表相符,未必需覆寫)。
|
||
4. **目視微調**:以 `demo/fallback.html`(或自有頁面)對照原文,逐字檢查——
|
||
- 標點與相鄰字**重疊** → 該字墨色比量測更偏該側,偏側往「不移」方向調
|
||
(`right`→`center`→`left`)。
|
||
- 標點與內容**仍有空隙、不夠半形** → 往「多移」方向調(`left`→`center`→`right`)。
|
||
- 某標點根本無可壓空白(字面已滿全形)→ 設 `full`,不收。
|
||
5. **記錄**:把該字型之 `bias` 覆寫存入專案設定,作為該字型之適配結果。
|
||
|
||
> 偏側是「字身於 0.5em 盒內左移多少」的三檔量化(0/0.25/0.5em),量測值近門檻
|
||
> (如 0.6 介於 center/right)時,以目視為準。`halt` 字型走 OpenType 由字型精確
|
||
> 定位,無此配字需求——`margin` 配字僅為缺 `halt` 字型之務實近似。
|
||
|
||
---
|
||
|
||
## CSS 基礎設定(消費端須知)
|
||
|
||
JS 只注入 `jz-*` 結構,**所有視覺尺寸在 `juzhen.css`**——故 CSS 不可或缺。消費端
|
||
須備妥下列幾項:
|
||
|
||
1. **引入樣式(必須)**:`<link rel="stylesheet" href="dist/juzhen.css">`(或 ESM
|
||
`import "cjk-autospace/css"`)。缺此則間隙/擠壓/justify 原子化全部失效。
|
||
2. **設定 `lang`(強烈建議)**:在內容容器標 `lang="zh-Hans"`/`"zh-Hant"`/`"ja"`,
|
||
驅動全角/開明政策與字形 `locl`。無 `lang` 時回退 `options.lang.default`。
|
||
3. **作用域類(用 `scope.include` 時)**:若設 `scope.include: ".juzhen"`,須給內容
|
||
容器加對應 class(如 `class="juzhen"`);`juzhen.css` 之 normalize 亦收斂於此域,
|
||
不外溢。
|
||
4. **長英文詞斷字**:`longWord` 包出 `<span lang="en">` 後,須由消費端 CSS 提供
|
||
`hyphens: auto`(含 `-webkit-hyphens`)才會原生斷詞顯示連字符。
|
||
5. **`justify`(選用)**:欲兩端對齊則自行設 `text-align: justify`;句末標點單份間距
|
||
與行末去隙由聚珍處理,無需額外設定。
|
||
6. **字型由消費端負責(聚珍不管字型)**:聚珍**不**內建任何 `font-family`/字型堆疊
|
||
——逐語言字型完全由消費端 CSS 決定。聚珍只確保原子(`jz-*`)以 `font: inherit`
|
||
繼承消費端字型、絕不強加,並顯式保留 `locl`(見下「字型與 locl」一節)。半形擠壓
|
||
依字型 OpenType `halt`:含 `halt` 之字型(Noto CJK/思源)預設即可;**缺 `halt`
|
||
之字型**(如方正書宋 GBK)須改用 `jiya.halfWidth:"margin"` 後備,並按字型以
|
||
`jiya.bias` 逐字微調墨色偏側(見「字型適配工作流」)。
|
||
7. **自動處理、無需消費端介入**:`juzhen.css` 已在 `.juzhen` 範圍內以 `@supports`
|
||
關閉 Blink 原生 `text-spacing-trim`/`text-autospace`(避免與託管擠壓重複作用),
|
||
並令注入間隙 `user-select: none`(複製乾淨)。
|
||
8. **段落屬性照常設、原子透明**:聚珍把標點拆成 inline-block 原子,但已確保這些原子對
|
||
消費端段落樣式透明——可在段落正常設 `text-indent`(首行縮排)、`hanging-punctuation`、
|
||
`text-decoration`(下劃線)等;庫內已歸零會被原子重複套用之繼承屬性、並令原子承接段落
|
||
裝飾,故首行縮排正常、下劃線連續不斷,無需任何額外配置(機制見 ARCHITECTURE「原子透明
|
||
原則」)。
|
||
|
||
---
|
||
|
||
## 字型與 locl(消費端設定)
|
||
|
||
聚珍**不管字型**;逐語言字型由你以 CSS 設定。推薦範式——逐 `lang` 設 `font-family`,
|
||
掛在 `.juzhen` 或帶 `lang` 之容器(**勿**直接設 `jz-inner`/`jz-char`,它們靠繼承取字型):
|
||
|
||
```css
|
||
.juzhen:lang(zh-Hans), .juzhen [lang]:lang(zh-Hans) { font-family: "Noto Serif SC", "Source Han Serif SC", serif; }
|
||
.juzhen:lang(zh-Hant), .juzhen [lang]:lang(zh-Hant) { font-family: "Noto Serif TC", "Source Han Serif TC", serif; }
|
||
.juzhen:lang(ja), .juzhen [lang]:lang(ja) { font-family: "Noto Serif JP", serif; }
|
||
```
|
||
|
||
要點:
|
||
|
||
- **字型須含 OpenType `halt`** 才能半形擠壓;否則用 `jiya.halfWidth:"margin"` + 按字型
|
||
以 `jiya.bias` 逐字微調墨色偏側(見「字型適配工作流」)。
|
||
- **locl(地域字形)**:泛 CJK 字型(思源/Noto CJK)以 `locl` 依 `lang` 選地域字形
|
||
(如「直/海/骨」之簡繁日差異)。聚珍**不改寫 `lang`、不關閉 locl**,故只要你 ①
|
||
正確標 `lang`、② 用支援 locl 之字型,locl 即正常。body 文本之 locl 全由你的字型+
|
||
`lang` 決定;標點(`jz-inner`)之 `halt` 已顯式併入 `locl`,地域標點字形不致失。
|
||
- 若只想全語言同一字型,直接 `.juzhen { font-family: … }` 即可(無需逐 `lang`)。
|
||
|
||
---
|
||
|
||
## 自定義字符分類(`charClass`)
|
||
|
||
每個字符的分類(中文/西文、全寬標點各子類)抽象為**規則集**:內置默認規則集即聚珍
|
||
的分類表,消費端可經 `options.charClass` **擴充**,以覆寫 `spacing`/`jiya`/`jinze`
|
||
等規則對特定字符之判定。語義為**追加**——把當前未分類之字符、或新符號歸入對應類。
|
||
|
||
```js
|
||
createJuzhen({
|
||
charClass: {
|
||
cjk: "〇", // 追加為中文(CJK):與西文邊界補中西間隙、禁則/justify 視為單字
|
||
western: "★☆", // 追加為西文(半寬標點亦歸此):與 CJK 邊界補間隙
|
||
biaodian: { // 追加為全寬標點,按子類(jiya charify/擠壓、禁則避頭尾)
|
||
stop: ".", // 句末(。!?類)
|
||
pause: "", // 句內(,、;:類)
|
||
open: "", close: "", // 開/閉括號引號
|
||
middle: "", liga: "", // 間隔號/省略破折
|
||
},
|
||
},
|
||
});
|
||
```
|
||
|
||
**典型用例**:英文全角句號 `.`(U+FF0E,有別於中文句號 `。` U+3002)預設**未分類**
|
||
——不被 charify、不挤壓、與 CJK 間不補隙。若內容用 `.` 作句末標點,以
|
||
`charClass: { biaodian: { stop: "." } }` 歸為全寬句末標點,即比照 `。` 處理(開明式挤半形、
|
||
全角式佔全形、行首避頭尾)。
|
||
|
||
要點:
|
||
|
||
- **向下兼容**:不傳 `charClass` 時,分類與行為與內置默認**完全一致**。
|
||
- **追加語義(v1)**:擴充內置類;**不**支援把內置字符「重分類/移除」(屬罕見需求,後續再議)。
|
||
勿把同一字符同時加入相衝之兩類。
|
||
- **結構性 ASCII**(字母數字 `A-Za-z0-9`、運算符 `+-*/` 等)為固定集、不開放覆寫——它們
|
||
服務於運算符/複合詞等內部規則,非「字符類型」。
|
||
- `margin` 後備模式下若新增開/閉括號,其墨色偏側未必命中內建參考表,可一併以
|
||
`jiya.bias` 指定(見「字型適配工作流」)。
|
||
|
||
---
|
||
|
||
## v1 向下相容
|
||
|
||
```js
|
||
// 既有消費端零改動:
|
||
const cjk = createCjkAutospace({ pillSelector: "code, kbd" });
|
||
cjk.apply(document.body);
|
||
```
|
||
|
||
IIFE 構建另掛 `globalThis.createCjkAutospace` 別名。**與 v1 之差異**(行為等價、
|
||
不要求逐位一致):
|
||
|
||
- 間隙標記由 `<span class="cjk-autospace">` 變為 `<jz-hws class="cjk-autospace">`
|
||
(仍帶原 class,既有 `.cjk-autospace{user-select:none}` CSS 仍命中)。
|
||
- 標點擠壓改由 `juzhen.css` 託管(須引入),預設仍以字型 `halt` 渲染半形——與 v1
|
||
消費端 `font-feature-settings:"halt"` 同機制;缺 `halt` 之字型可切 `margin` 後備。
|
||
|
||
---
|
||
|
||
## 目視驗證
|
||
|
||
`demo/index.html` 載入 `dist/` 產物,簡繁並列、可切 `justify`、附 copy-clean 測試區。
|
||
`demo/fallback.html` 以**方正書宋 GBK**(無 `halt`)對照 `halt` 模式失效 vs `margin`
|
||
後備生效。`demo/resize-check.html` 為**可拖曳容器**檢查頁:拖動方框寬度,驗證中西間隙在
|
||
各寬度下恆穩(含行末去隙之自我應驗、pill 跨行右隙、貨幣/拼音/西里爾外文補隙、全角省略號
|
||
與各式中文標點無誤隙),可切 `justify` 與「顯示間隙」描邊。版面相關結論(justify 單份間距、
|
||
擠壓視覺量、行端、`inline-block` 基線、間隙隨寬度之穩定性)須於 Chrome/Safari/Firefox
|
||
三引擎目視核對——jsdom 僅驗證 DOM 結構正確性。
|
||
|
||
---
|
||
|
||
## 授權
|
||
|
||
MIT。借鑑 Han.css/Heti 之思路與分類表(MIT),成品自含、不依賴其執行期程式碼。
|