使用DirectWrite进行部分连字选择

7

使用IDWriteTextLayout中的HitTestTextPosition样式API,我无法正确处理像Calibri这样的字体中“ti”、“ffi”或其他连字内部的文本位置。它总是返回在连字之后或之前的位置,而不是内部位置,比如t|i或f|f|i。

使用DirectWrite API在连字内进行插入符移动的推荐方法是什么?


1
DirectWrite是否有指向OpenType连字插入位置表的指针? - Jongware
2
我不确定。在DirectWrite的文档中我找不到它。你提到的文档有一个旧版Uniscribe的部分,但没有提到新版的DirectWrite。另一方面,我相信UWP TextEdit已经解决了这个问题,并且在幕后使用DirectWrite。那么他们是如何做到的呢?官方的PadWrite示例也有同样的问题:https://github.com/Microsoft/Windows-classic-samples/tree/master/Samples/Win7Samples/multimedia/DirectWrite/PadWrite - Filip Kunc
1
你能详细说明你具体想要实现什么吗?例如,描述一下作为用户你会看到什么,以及为什么你希望用户有这种体验? - Mike 'Pomax' Kamermans
@Mike'Pomax'Kamermans:我建议阅读微软Word开发者的文章:https://blogs.msdn.microsoft.com/murrays/2012/06/29/ligatures-clusters-combining-marks-and-variation-sequences/,他试图从用户角度解释如何处理连字。DirectWrite中的HitTestSomething API明确提到了GUI自动化的插入符位置,我的意思是你必须能够进入ti或ffi连字中,否则用户会感觉它被损坏了。 - Filip Kunc
2个回答

2
我所知道的文本编辑器都使用简单的方法(1)将字形群集的宽度除以群集中代码点的数量(不包括任何零宽度组合标记),而不是使用GDEF插入符号定位信息。即使是Word也是如此,如果你仔细观察下面,就能发现这一点。虽然不是非常精确,但由于在普通阅读大小时足够接近且简单易行,因此许多人都这么做:

Word displaying fi ligature in Calibri

(2) 我听说有些人可能会(但不知道是哪些人)也使用未成形字符的原始字形进度(在连字之前),并将它们与连字簇的宽度成比例地缩放。
(3) 一些文本编辑器可能使用GDEF表,但我从未确定过有哪些(可能是Adobe In-Design?)。
使用第二或第三种方法与 IDWriteTextLayout 的最具挑战性的方面在于,访问该运行中对应的 IDWriteFontFace 需要相当的间接性,因为使用的特定 IDWriteFontFace(在解析字体族名称+WWS+变量字体轴后)存储在布局中,但不可通过任何“getter”API公开访问。你唯一可以提取它们的方式是通过将字形运行绘制到用户定义的 IDWriteTextRenderer 接口中记录所有 DWRITE_GLYPH_RUN::fontFace,然后可以在代码点上调用 IDWriteFontFace::GetDesignGlyphAdvancesIDWriteFontFace::TryGetFontTable 读取 OpenType GDEF table(这很难读取)。这是很多工作,这是因为...

官方的 PadWrite 示例也有同样的问题

“IDWriteTextLayout”旨在用于显示文本而非编辑。它具有一些用于命中测试的功能,如果您想在段落中显示下划线链接并测试其是否被点击(在这种情况下,连字将在单词内完整),或者如果您想在某些文本周围绘制一些装饰,则这些功能很有用。但它实际上并不是为全面的编辑体验而设计的,这包括插入符导航。实际的文本编辑引擎(例如Word、PowerPoint、OpenOffice等)总是调用较低级别的API,这是它的预期用途。
我编写的PadWrite示例有点误导,因为虽然它支持基本编辑,但那只是为了让您可以玩弄格式并了解事物的运作方式。它还有很长的路要走,才能真正成为一个交互式编辑器。首先,每次编辑时它都会完全重新创建“IDWriteTextLayout”,这就是为什么该示例只呈现了几段文本,因为具有几页文本的完整编辑器将需要增量更新文本。我不再在那个团队工作,但我考虑过在GitHub上创建一个DWrite帮助库来填补一些后见之明的空白,如果我真的这样做了,我可能会选择使用方法1:b。

2
如果您启用了 GSUB 替换,那么就不存在“内部”位置?
Opentype GSUB 连字是代码点序列的单个字形替换,而不是“几个字形粘在一起”。它们实际上是不同的、单独的字形,具有单个边界框和单个左右侧轴距以进行光标定位/对齐。如果您有文本 A + E,并且字体具有将其转换为 Ӕ 的连字替换,则启用连字后,在该代码序列中真正存在两个光标位置:Ӕ|。您无法将光标放置在“中间”,因为没有“中间”;它是一个单一的、原子的、不可分割的元素。
对于像 这样的 f. 连字,启用 GSUB 后它们就是单个字形。这实际上是应该发生的事情:启用 GSUB 连字意味着您明确地希望文本被呈现为具有原子字形的多对一替换,例如将完整短语“صلى الله عليه وعلى آله وسلم‎”以及其变体转换为单个字形 ﷺ。
如果您想使用基本代码点序列(这样,如果您有一个文本 f + f + i,它就不会被转换为 ),则需要禁用具有 liga OpenType 特性的字体。

2
从字形的角度来看,你当然是正确的,但从文本编辑器的角度来看,这就是“插入符定位表”的作用:“连字插入符列表表(LigCaretList)定义了字体中所有连字的插入符位置。” 这个表格提供了你在第二段所需的确切信息。不过,它只是一个子集,并不适用于所有可能的连字(比如你提到的阿拉伯语例子)。另一个例子是 :) 自动显示为 `` 的情况,这种情况下它也是无用的。 - Jongware
啊,我对GDEF还没有深入地了解过(或者说根本没有)。我得仔细研究一下,虽然我们还留有Filip的措辞,但感觉他试图把光标放在不应该放置的位置(即,演示文稿上下文与编辑上下文如此不同,我很难理解为什么要尝试适应这种情况。当然,这并不否定问题,但仍然增加了反问号的数量)。 - Mike 'Pomax' Kamermans
2
连字内的定位必须是可能的,参见TrueType中类似的内容:https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6lcar.html - Filip Kunc
你可能需要阅读这里的harfbuzz源代码,以了解它如何让DirectWrite实现此功能。 - Mike 'Pomax' Kamermans

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接