feat: 垂懸字避免(方案 C,斷行層無測量,order 70)(§6.5.1)
段末行 CJK 實義字數 ≥ N(預設 2;標點/符號不計)。standalone 段落級 pass:
自段末向前數 N 個實義字,把「第 N 字起至段末」(含其間/尾隨標點)包入
<jz-orphan>(white-space:nowrap)。該整體不可斷 → 末行恒含之 ⇒ 末行實義字 ≥ N,
任意寬度/字型/瀏覽器成立,無需量測 layout。
要點:
- 邊界字定位跨 jz-jinze/行內元素攀爬:實義字落於行內元素內時綁定整個塊級
直接子(不切割 nowrap 群、revert 乾淨);僅裸文本直接子才精確切分(最小綁定)。
- 僅處理葉段落(無塊級後代);段落實義字 < N 不綁;單行段方案 C 本就無害。
- 選項 orphan?: boolean | { chars? };**預設開 chars=2**(§6.5.6 建議);
下游可 orphan:false 關閉。段落級,data-jz-level=text 子樹自動跳過。
- order=70:jinze(40)/spacing(50)/longWord(60) 後、layout pass(90/92) 前。
新增 _orphan.css(jz-orphan{white-space:nowrap});JzTag 加 jz-orphan。
9 項單元測試(末 N+尾標點、chars 選項、標點不計、實義字<N 不綁、跨 jinze、
關閉、冪等、revert、段落級閘)。共 85 測試全綠。
窄容器拖動「末行恒 ≥N」之 layout 驗證留待 demo(Step 4-5,瀏覽器)。
This commit is contained in:
+15
-2
@@ -13,9 +13,10 @@ import { jiyaAdjacencyPass, jiyaPass } from "./typeset/jiya.js";
|
||||
import { jinzePass } from "./typeset/jinze.js";
|
||||
import { gapTrimPass, lineEdgePass } from "./typeset/lineedge.js";
|
||||
import { longWordPass } from "./typeset/longword.js";
|
||||
import { orphanPass } from "./typeset/orphan.js";
|
||||
import { spacingPass } from "./typeset/spacing.js";
|
||||
import { resolveRuleset } from "./core/unicode.js";
|
||||
import type { BiasTable, JiyaOptions, JuzhenOptions, LongWordOptions, Pass, RenderContext, ResolvedOptions } from "./types.js";
|
||||
import type { BiasTable, JiyaOptions, JuzhenOptions, LongWordOptions, OrphanOptions, Pass, RenderContext, ResolvedOptions } from "./types.js";
|
||||
|
||||
export type { JuzhenOptions, ResolvedOptions, Pass };
|
||||
export type { CharClassOverride, BdSubclass, Ruleset, Bias, BiasTable } from "./types.js";
|
||||
@@ -41,7 +42,7 @@ const DEFAULT_SKIP_TAGS = [
|
||||
|
||||
const PASSES: Pass[] = [
|
||||
jiyaPass, jiyaAdjacencyPass, longWordPass, spacingPass, jinzePass,
|
||||
lineEdgePass, gapTrimPass,
|
||||
orphanPass, lineEdgePass, gapTrimPass,
|
||||
];
|
||||
|
||||
function feature(v: unknown, dflt: boolean): boolean
|
||||
@@ -58,6 +59,16 @@ function resolveLongWord(v: boolean | LongWordOptions | undefined): { minLength:
|
||||
return { minLength, lang };
|
||||
}
|
||||
|
||||
function resolveOrphan(v: boolean | OrphanOptions | undefined): { chars: number }
|
||||
{
|
||||
if (v && typeof v === "object")
|
||||
{
|
||||
const chars = typeof v.chars === "number" && v.chars >= 1 ? Math.floor(v.chars) : 2;
|
||||
return { chars };
|
||||
}
|
||||
return { chars: 2 };
|
||||
}
|
||||
|
||||
function resolveJiya(
|
||||
v: boolean | JiyaOptions | undefined,
|
||||
): { halfWidth: "halt" | "margin"; bias: BiasTable }
|
||||
@@ -97,12 +108,14 @@ export function normalizeOptions(opts: JuzhenOptions = {}): ResolvedOptions
|
||||
longWord: opts.longWord === false ? false : feature(opts.longWord, true),
|
||||
jiya: feature(opts.jiya, true),
|
||||
jinze: feature(opts.jinze, true),
|
||||
orphan: opts.orphan === false ? false : feature(opts.orphan, true),
|
||||
hanging: feature(opts.hanging, false),
|
||||
biaodian: feature(opts.biaodian, false),
|
||||
emphasis: feature(opts.emphasis, false),
|
||||
ruby: feature(opts.ruby, false),
|
||||
},
|
||||
longWord: resolveLongWord(opts.longWord),
|
||||
orphan: resolveOrphan(opts.orphan),
|
||||
jiyaConfig: resolveJiya(opts.jiya),
|
||||
blocks: Array.isArray(opts.blocks) ? opts.blocks.slice() : [],
|
||||
finder: {
|
||||
|
||||
Reference in New Issue
Block a user