Skip to content

feat: 新增注音排布规范化,合并边界重叠的相邻注音单元 - #10

Closed
Xpercent wants to merge 1 commit into
SPlayer-Dev:mainfrom
Xpercent:feat/ruby-layout-normalize
Closed

Xpercent wants to merge 1 commit into
SPlayer-Dev:mainfrom
Xpercent:feat/ruby-layout-normalize

Conversation

@Xpercent

@Xpercent Xpercent commented Oct 5, 2026

Copy link
Copy Markdown

before
a

after
image

改动说明

问题:注音(furigana / ruby)文本宽于其正文时,注音盒以正文盒中线为轴不改变整词前进宽度,
注音会溢出到正文框外,压到相邻注音上。混合存在单字注音与多字注音时,重叠可能发生在任意相邻单元
之间(单+单、多+单、多+多、以及同一个词内的逐字注音之间),表现为假名互相叠字。

这个 PR 做什么:新增一个可选、不依赖 DOM 的注音排布规范化能力,供宿主在送渲染前使用。

新增 normalizeRubyLayout(lines, measure):

  • 行内宽度测量由调用方注入(measure(text, fontRatio, language)),库本身保持零依赖,可在
    Node / 单测桩 / 浏览器 Canvas 下运行;language 用于取到与渲染层一致的字体。
  • 逐词按渲染器的真实配对规则展开注音单元(有效注音条目数与词内图素数一致时逐字配对,否则整词
    共用一条注音,空白注音条目按渲染器的过滤规则忽略)。
  • 相撞判定按图素粒度进行,并覆盖两套渲染器各自的排布模型:逐字注音盒(lyric-dom 每个图素
    一个 ruby 元素)与整行注音盒(applemusic-like-lyrics 每个词一行居中注音、不参与整词计宽),
    任一模型判定相撞即合并。
  • 相撞的相邻单元合并为一个多字整体注音单元,合并后仍与左右相邻相撞的继续沿正文顺序链式合并
    (前向与后向都覆盖);未相撞的注音保持原样,不做冗余合并。
  • 合并只发生在完整歌词词之间:输出词是若干完整原词的拼接,正文文本与词时间窗都取自原数据,
    不会产生半个词或自造的时间边界。romanWord / obscene / emptyBeat 在合并时聚合保留。
  • 纯函数:改写行数据时返回新数组与新行对象,未参与合并的词沿用原对象引用;整首无需合并时返回
    入参数组本身,便于上层做响应式引用比较。

为什么放在 utils/ 而不是挂进 normalizeLyricLines:注音是否显示、用什么字体量宽都属于宿主
的显示层决策(渲染器的 --lp-ruby-font-size / .rubyWord 字号、按行主语言选择的字族都在宿主侧)。
作为解析管线的固定步骤会静默改变所有下游消费者的输出语义,因此这里只提供显式调用的能力。

是否包含破坏性变更

  • 否

纯新增导出(normalizeRubyLayout 及 type RubyWidthMeasurer),未改动任何既有函数签名与行为,
normalizeLyricLines 输出不变。

关联 Issue

无(由 SPlayer 侧的注音压字问题触发,尚未在上游开 Issue)。

测试情况

新增 tests/ruby-layout.spec.ts 共 18 例,覆盖:

  • 单+单、多+单、多+多相撞合并;链式合并直到不再相撞;恰好相切 / 占满正文宽度时不合并。
  • 词内逐字注音相撞时压缩为该词的整词注音(不依赖跨词才触发)。
  • 整行注音超出正文时合并(首末字注音盒恰好相切、逐字模型检测不出的场景)。
  • 正文间空白按实际宽度吸收溢出;未注音的词隔开相邻注音,不跨词合并。
  • 只有空白注音的词不被丢弃,整行正文逐字与原文一致(不丢字、不重复);规范化幂等(重跑无改动)。
  • 合并保留罗马音、脏词标记、空拍与时间窗;未参与合并的词与整行保持原对象引用;背景行同样处理。
  • 量宽时把行主语言透传给测量函数。

本地执行:pnpm test(27 files / 247 passed)、pnpm typecheck、pnpm lint、pnpm build 全部通过。
真实渲染效果在浏览器端验证,见 SPlayer 侧 PR 的「测试情况」。

自查清单

  • 本 PR 只包含一个主要功能(注音排布规范化),无夹带无关改动
  • 已在本地完整测试并通过,代码已逐行审阅(含 AI 辅助生成的部分),未做未经验证的提交
  • 已运行 pnpm format,pnpm typecheck、pnpm lint 通过
  • 不涉及原生模块
  • 该仓库无 dev 分支,默认分支为 main,故向 main 提交

@Xpercent Xpercent closed this Oct 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant