问题描述
我们正在尝试使用 Core Text API CTFramesetterSuggestFrameSizeWithConstraints 获取建议的帧大小。
让,E = \uFFFC ; W = \u200B ; S = \u00A0
WSESWWSESW
字符 S 和 E 被赋予 CTRunDelegates,它们具有 CTRunDelegateGetWidthCallback,分别返回 0 和 100 .
执行以下代码时:
auto frameOptions = @{ (id)kCTFrameProgressionAttributeName: @(kCTFrameProgressionTopToBottom)) };
auto constraintSize = CGSizeMake(200,CGFLOAT_MAX);
CFRange fitRange = CFRangeMake(0,0);
auto pathSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter_.get(),CFRangeMake(0,0),(CFDictionaryRef)frameOptions,constraintSize,&fitRange);
API 返回的 pathSize 为 100,我们得到两个单独的行
然后我们使用以下代码创建 CTFrameRef:
auto rect = CGRectMake(0,200,pathSize.height);
auto path = CGPathCreateWithRect(rect,nil));
auto frame = CTFramesetterCreateFrame(framesetter_.get(),path,(CFDictionaryRef)frameOptions));
问题:
-
为什么 CTFramesetterSuggestFrameSizeWithConstraints 返回一个宽度为 100 的 pathSize,而显然约束宽度 (200) 应该足以容纳一行中的整个文本。
-
CoreText 如何决定在一次运行中应将属性字符串中的哪些字形/字符范围放在一起? 在上面的例子中,我们得到以下两行:
第 1 行:W S E SWW (4 CTTextRun)
第 2 行:S E SW (3 CTTextRun)
有人可以帮我回答与上述场景相关的这两个问题吗?提前致谢!
编辑 1:
为问题和我们当前的方法添加更多上下文: Image reference
“Parent”、“Child1”、“Child2”请参考上图。
“Parent”代表我们想要布局和计算尺寸的框/矩形。我们想把文本放在“父”框中。 “Child1”、“Child2”在我们的文本中由“E”字符表示。
- 首先,我们尝试计算“父”框的大小。
- 我们使用“父”框的计算大小作为
的约束 CTFramesetterSuggestFrameSizeWithConstraints,然后使用其返回值创建CTFrame
大小计算 :“父”框的大小计算取决于多种因素 - 可能有一个指定的大小作为外部参数,或者它可能来自它的容器框,或者可能相等到足以适合其文本内容的大小(由一些最小,最大限制)。 因此,它并不总是不受约束(单行),有时甚至不依赖于文本内容。
让我们看一下计算依赖于文本内容的情况:
因此我们使用以下方法计算没有任何约束的文本的宽度:
CTFramesetterSuggestFrameSizeWithConstraints(framesetter_.get(),CGSizeMake(CGFLOAT_MAX,CGFLOAT_MAX),&fitRange);
这将返回 100 (child1) + 100 (child2) = 200 pts 的值。假设我们有足够的空间,200 pts 是“父”框的大小。
文本布局:此时,我们只有“父”框的计算大小,没有关于它是如何计算的信息。
如果现在我们使用 200 pts 的约束和 CTFramesetterSuggestFrameSizeWithConstraints,它会返回 100pts 并将文本分成两行。
这就是我们遇到的问题 - 用于创建 CTFrame 时不受约束的文本大小会导致意外的换行。
解决方法
对于您的第一个问题,正如评论中所讨论的那样,因为 CTFramesetter 具有各种可能添加填充或进行字形调整的规则。这取决于很多事情(并且不承诺随着时间的推移保持一致),因此您无法猜测。只需询问 CTFramesetter(或 CTLine)尺寸是多少,并相应地调整您的布局。或者,如果您有一个硬约束(这必须适合 100 分),然后设置它,它会换行。但是,如果您说“嗯,101 分也可以”,那么 100 分就不是限制条件。让你的约束成为实际的约束,或者让它不受约束(在这种情况下你可能想要 CTLine,而不是 CTFramesetter)。
对于第二个问题,CTRun 是一个附加了所有相同属性值的范围。 NSAttributedString 对此很聪明,即使您以重叠方式设置属性,也可以将等效的相邻区域合并在一起。然而,它并不承诺总是这样做。连续的 CTRun 对象具有相同的属性是合法的。当以复杂的方式应用属性或将属性字符串连接在一起时,就会发生这种情况。
如果您检查 NSAttributedString 的属性,您会发现第一个和第二个字符、第二个和第三个字符之间可能存在一些差异,然后字符 4-6 具有完全相同的属性。