【问题标题】:FMX: Why does TTextLayout.RegionForRange fail in this particular case?FMX:为什么 TTextLayout.RegionForRange 在这种特殊情况下会失败?
【发布时间】:2021-10-07 18:00:21
【问题描述】:

我创建了一个TTextLayout 对象,其文本包含连续的“t”字符和“Calibri”字体。然后我有以下代码使用RegionForRange 函数返回每个字符的矩形区域。结果是第一个't'的宽度为0,第二个't'的位置与第一个相同。文本中的任何其他字符都是正确的——即使是错误之后的字符,尽管字母“f”也有同样的问题,以及“t”和“f”的任何连续组合。大多数其他字体似乎不会导致问题,尽管 'Gabriola' 会。

procedure TForm1.FormCreate(Sender: TObject);
var
  Layout : TTextLayout;
  LRange : TTextRange;
  LRegion : TRegion;
  LRects : array of TRectF;
  i : Integer;
begin
  Layout := TTextLayoutManager.DefaultTextLayout.Create;
  Layout.Font.Size := 20;
  // Calibra and Gabriola fail but Arial and most other fonts don't
  Layout.Font.Family := 'Calibri';// 'Gabriola';
  Layout.Text := 'tt'; // ff, ft, tf also fail

  LRange.Length := 1;
  SetLength(LRects, Length(Layout.Text));
  for i := 0 to Length(Layout.Text) - 1 do begin
    LRange.Pos := i;
    LRegion := Layout.RegionForRange(LRange);
    LRects[i] := LRegion[0]; // Bounding rect of this character
  end;

end;

在函数末尾打断点,可以查看LRects中存储的left和right的值。

进入RegionForRange 函数会导致TTextLayoutD2D.DoRegionForRange 但从那里我无法进一步查看可能出现的问题。为什么这些特定字符会发生这种情况并且只发生在这些字体上?为什么范围内的字符后面的字符会影响结果?它是一个错误吗?我或许可以编写一些代码来检测这些序列并纠正位置,但我觉得我不需要这样做。

请注意,我使用的是 Delphi 10.4。我没有尝试更新最近的更新,所以如果有人能确认这个问题发生在哪个版本中,我将不胜感激。

【问题讨论】:

  • 您的代码有一个问题。您正在遍历字符串字符,就好像您将拥有基于 0 的字符串,而 Delphi 10.4 默认使用基于 1 的字符串,这意味着第一个字符串字符的索引为 1 而不是 0。
  • @SilverWarior 似乎 TTextRange 对象使用 0 索引,即使 Delphi 中的字符串从索引 1 开始。文档不清楚这一点,但如果我传入 1 作为位置,那么它给出的位置第二个字符。
  • 好吧,至少我帮助你纠正了你的循环,使它只产生 2 个循环,而不是像以前那样做 3 个循环。这就是为什么我认为它是通过索引位置读取每个字符串字符的原因。
  • 是的,我在回复你之后才意识到。谢谢。

标签: delphi firemonkey delphi-10.4-sydney


【解决方案1】:

原因被称为ligature。 应用连字时,两个或多个字形的组合将被另一个单个字形替换。

连字由 OpenType 字体定义。每种字体都定义了自己的一组连字。 Calibri 定义了很多(您只找到了几个)。

【讨论】:

  • 谢谢。好的,很高兴知道这不是错误。我上面应该问的是如何防止它发生?我想我已经找到了答案。我只将文本传递给 RegionFromRange,直到我想要该区域的字母为止。这可以防止第一个字符的宽度返回为 0。我根据前一个字符的位置和宽度计算出位置。
  • 我已经意识到,当对齐为中心或尾随时,仅将文本传递到我想要区域的字符不起作用,所以我仍在寻找解决方案.我需要字符串中的每个字符都有自己独特的位置,就好像没有连字一样。
  • @XylemFlow 你到底想达到什么目的?
  • 我正在使用 ConvertToPath 将文本转换为路径,但我正在通过为每个唯一字符存储一个路径来压缩结果。然后可以通过索引字符路径列表和存储文本中每个字符位置的列表来呈现最终文本。 RegionForRange 用于获取每个字符的位置。然而,整个系统由于连字而崩溃。如果我可以关闭合字,那将是理想的,但我认为没有办法做到这一点。我可以尝试检测连字并将它们存储为另一个独特的字符。它变得复杂。
  • 您可能会问我为什么首先要将文本布局转换为路径以及为什么要压缩路径数据。这是因为文本对象被保存到一个文件中,该文件可以加载到没有最初使用的字体的不同设备上。我正在按照描述的方式进行压缩,因为它大大减少了文件大小,尤其是对于长文本序列。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-17
  • 1970-01-01
  • 2014-05-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多