754b00da42
段末行 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,瀏覽器)。
172 lines
6.6 KiB
TypeScript
172 lines
6.6 KiB
TypeScript
import type { Finder } from "./core/finder.js";
|
||
import type { LangClass, LocaleConfig, PunctStyle } from "./core/locale.js";
|
||
import type { Bias, BiasTable, BdSubclass, CharClassOverride, Ruleset } from "./core/unicode.js";
|
||
export type { LangClass, PunctStyle, Bias, BiasTable, BdSubclass, CharClassOverride, Ruleset };
|
||
/** 長英文詞包裝設定。 */
|
||
export interface LongWordOptions {
|
||
minLength?: number;
|
||
lang?: string;
|
||
}
|
||
/** 垂懸字避免設定(§6.5.1)。布林 true 等同 { chars: 2 }。 */
|
||
export interface OrphanOptions {
|
||
/** 段末行至少保留之 CJK 實義字數(標點/符號不計;預設 2)。 */
|
||
chars?: number;
|
||
}
|
||
/** 標點擠壓設定(架構 §6.3)。布林 true 等同 { halfWidth: "halt" }。 */
|
||
export interface JiyaOptions {
|
||
/** 半形渲染機制:
|
||
* - "halt"(預設)— 用字型 OpenType `halt`,由字型按字形正確定位,需字型支援;
|
||
* - "margin" — 負 margin 後備模式,供缺 halt 之字型,依字形偏側表收半形。 */
|
||
halfWidth?: "halt" | "margin";
|
||
/** margin 後備模式之字形偏側覆寫表(逐 lang 逐字),覆寫內建參考預設。
|
||
* halt 模式不讀此欄。 */
|
||
bias?: BiasTable;
|
||
}
|
||
/** 分塊功能選擇:選擇器 → 該塊內生效之功能集(覆寫全域)。 */
|
||
export interface BlockRule {
|
||
selector: string;
|
||
features: string[];
|
||
}
|
||
/** createJuzhen 之公開選項。所有欄位皆可選;預設見 normalizeOptions。 */
|
||
export interface JuzhenOptions {
|
||
/** 語言與全角/開明政策。 */
|
||
lang?: {
|
||
/** 無 lang 祖先時之預設語言標籤(預設 "zh-Hant")。 */
|
||
default?: string;
|
||
/** 全域標點擠壓預設:quanjiao(全角式)|kaiming(開明式)|
|
||
* banjiao(半角式)。覆寫內建逐語言預設,可被 policy 逐 lang 覆寫。 */
|
||
style?: PunctStyle;
|
||
/** 逐 lang 覆寫(最高優先)。 */
|
||
policy?: Partial<Record<LangClass, PunctStyle>>;
|
||
};
|
||
/** 自定義字符分類(擴充內置規則集)。把未分類字符或新符號歸入中文/西文/
|
||
* 全寬標點各子類,以覆寫 spacing/jiya 等規則之分類判定(追加語義)。 */
|
||
charClass?: CharClassOverride;
|
||
/** 功能分級標記(§6.5.0a)。段落級功能(禁則/行端/懸掛/垂懸字/長詞斷字)
|
||
* 僅作用於段落級元素;文本級功能(中西間隙/標點擠壓)任意文本皆適用。
|
||
* - `text`:選擇器,命中之子樹**僅文本級**(標題/按鈕/UI 文字典型);亦可
|
||
* 經元素屬性 `data-jz-level="text"`/`"paragraph"` 標記(屬性優先、最近祖先勝)。
|
||
* 預設:塊級元素(blockTags)→ 段落級(全功能)。 */
|
||
level?: {
|
||
text?: string;
|
||
};
|
||
/** 間隙標記沿用之 class(v1 相容;預設無額外 class)。 */
|
||
autospaceClass?: string;
|
||
/** justify 原子化:jz-char/jz-jinze 是否以 inline-block 作對齊單一原子
|
||
* (預設 true)。設 false 改 display:inline——用於分頁器(如 Paged.js)之
|
||
* 對齊引擎會把每個 inline-block 邊界當伸縮點、與 CJK inter-ideograph 疊加
|
||
* 致窄欄散架之情形(halt 半形與 jinze nowrap 仍保留)。 */
|
||
justifyAtoms?: boolean;
|
||
/** 作用域限定(§3.6)。 */
|
||
scope?: {
|
||
root?: Element | string;
|
||
include?: string | null;
|
||
avoid?: string;
|
||
};
|
||
spacing?: boolean;
|
||
longWord?: boolean | LongWordOptions;
|
||
jiya?: boolean | JiyaOptions;
|
||
jinze?: boolean;
|
||
orphan?: boolean | OrphanOptions;
|
||
hanging?: boolean;
|
||
biaodian?: boolean;
|
||
emphasis?: boolean;
|
||
ruby?: boolean;
|
||
/** 分塊功能選擇(§3.6)。 */
|
||
blocks?: BlockRule[];
|
||
styleInlines?: string[];
|
||
blockTags?: string[];
|
||
skipTags?: string[];
|
||
pillSelector?: string;
|
||
skipAttribute?: string;
|
||
isSkipped?: (node: Node) => boolean;
|
||
}
|
||
/** 正規化後之內部選項。 */
|
||
export interface ResolvedOptions {
|
||
locale: LocaleConfig;
|
||
/** 最終字符分類規則集(內置默認 + charClass 覆寫)。 */
|
||
ruleset: Ruleset;
|
||
autospaceClass: string;
|
||
justifyAtoms: boolean;
|
||
/** 功能分級之 text-level 選擇器(§6.5.0a);null 表示未設。 */
|
||
levelText: string | null;
|
||
scope: {
|
||
root: Element | string | null;
|
||
include: string | null;
|
||
avoid: string | null;
|
||
};
|
||
features: {
|
||
spacing: boolean;
|
||
longWord: boolean;
|
||
jiya: boolean;
|
||
jinze: boolean;
|
||
orphan: boolean;
|
||
hanging: boolean;
|
||
biaodian: boolean;
|
||
emphasis: boolean;
|
||
ruby: boolean;
|
||
};
|
||
longWord: {
|
||
minLength: number;
|
||
lang: string;
|
||
};
|
||
/** 垂懸字避免設定(解析後)。 */
|
||
orphan: {
|
||
chars: number;
|
||
};
|
||
/** 擠壓半形渲染設定(halt 預設/margin 後備)。 */
|
||
jiyaConfig: {
|
||
halfWidth: "halt" | "margin";
|
||
bias: BiasTable;
|
||
};
|
||
blocks: BlockRule[];
|
||
finder: {
|
||
styleInlines: Set<string>;
|
||
blockTags: Set<string>;
|
||
skipTags: Set<string>;
|
||
pillSelector: string;
|
||
skipAttribute: string | null;
|
||
isSkipped: ((node: Node) => boolean) | null;
|
||
};
|
||
}
|
||
/** render/revert 之統一上下文(§3.3)。 */
|
||
export interface RenderContext {
|
||
root: Element;
|
||
options: ResolvedOptions;
|
||
finder: Finder;
|
||
}
|
||
/**
|
||
* charify 階段之包裝請求:把 node.data[start, end) 包進 build() 造出之元素。
|
||
* 同階段各 pass 之請求須互不重疊(標點/字母段天然不交集)。
|
||
*/
|
||
export interface WrapRequest {
|
||
start: number;
|
||
end: number;
|
||
build: (text: string) => HTMLElement;
|
||
pass: string;
|
||
}
|
||
/** pass 種類。 */
|
||
export type PassKind = "charify" | "standalone";
|
||
/** 功能分級(§6.5.0a):text=任意文本片段皆適用;paragraph=需行/段布局。 */
|
||
export type FeatureLevel = "text" | "paragraph";
|
||
interface BasePass {
|
||
name: string;
|
||
order: number;
|
||
kind: PassKind;
|
||
/** 功能分級(§6.5.0a)。paragraph 級 pass 對 text-level 元素自動跳過。 */
|
||
level: FeatureLevel;
|
||
enabled: (options: ResolvedOptions) => boolean;
|
||
revert: (ctx: RenderContext) => void;
|
||
}
|
||
/** 共享 P1 走訪之字元包裝 pass。 */
|
||
export interface CharifyPass extends BasePass {
|
||
kind: "charify";
|
||
collect: (node: Text, ctx: RenderContext) => WrapRequest[];
|
||
}
|
||
/** 自跑走訪/版面後處理之 pass。 */
|
||
export interface StandalonePass extends BasePass {
|
||
kind: "standalone";
|
||
render: (ctx: RenderContext) => void;
|
||
}
|
||
export type Pass = CharifyPass | StandalonePass;
|