Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
215 views
in Technique[技术] by (71.8m points)

macos - CTFramesetterSuggestFrameSizeWithConstraints line breaks even when there is space available on the same line

We are trying to get the suggested frame size using the Core Text API CTFramesetterSuggestFrameSizeWithConstraints.

Let, E = uFFFC ; W = u200B ; S = u00A0

The CTFrameSetter is created with the following attributed string:

WSESWWSESW

The character S and E are given CTRunDelegates which have the CTRunDelegateGetWidthCallback that return 0 and say 100 respectively.

When the following code is executed :

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);

The pathSize returned by the API comes out to 100 and we get two separate lines

We then create the CTFrameRef using the following piece of code:

auto rect = CGRectMake(0, 0, 200, pathSize.height);
auto path = CGPathCreateWithRect(rect, nil));
auto frame = CTFramesetterCreateFrame(framesetter_.get(),
    CFRangeMake(0, 0), path, (CFDictionaryRef)frameOptions));

Question:

  1. Why does the CTFramesetterSuggestFrameSizeWithConstraints return a pathSize which has a width of 100 when clearly the constraint width (200) should be sufficient to accommodate the entire text in a single line.

  2. How does CoreText decide which glyps/character range from attributed string should be put together in a single run? In the above example we get the following two lines :

Line 1: W S E SWW (4 CTTextRun)

Line 2: S E SW (3 CTTextRun)

Can somebody please help me with answers to these two questions related to the abovementioned scenario. Thanks in advance!

Edit 1:

Adding some more context to the problem and our current approach: Image reference

Please refer to the image above for reference to "Parent", "Child1", "Child2".

"Parent" represents the box/rect that we want to layout and compute sizes for. We want to put the text in the "Parent" box. "Child1", "Child2" are represented by 'E' characters in our text.

  1. First, we try to compute the size for the "Parent" box.
  2. We use the computed size of the "Parent" box as constraints for the
    CTFramesetterSuggestFrameSizeWithConstraints and then use its return value to create CTFrame

Size Computation : The size computation of "Parent" box depends of multiple factors - there could be a specified size as a external parameter, or it could come from its container box, or could be equal to the size (clamped by some min, max) enough to fit its text content. So, it is not always unconstrained (single line) or, sometimes even not dependent on the text content.

Let's look at the case when the computation is dependent on the text content:

So we calculate the width of the text without any constraints using:

CTFramesetterSuggestFrameSizeWithConstraints(framesetter_.get(), CFRangeMake(0, 0),
            (CFDictionaryRef)frameOptions, CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX), &fitRange); 

This returns the value of 100 (child1) + 100 (child2) = 200 pts exactly. Let say we have enough space and 200 pts is what is set as the size of the "Parent" box.

Text Layout : At this point, we only have the computed size of the "Parent" Box and no information on how it was calculated.

If now we now use the constraints of 200 pts with CTFramesetterSuggestFrameSizeWithConstraints, it returns 100pts and breaks the text in two lines.

This is where we have a problem - the unconstrained text size when used to create CTFrame gives an unexpected line break.

question from:https://stackoverflow.com/questions/65945071/ctframesettersuggestframesizewithconstraints-line-breaks-even-when-there-is-spac

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

To your first question, as discussed in the comments, because CTFramesetter has various rules that may add padding or make glyph adjustments. It depends on lots of things (and is not promised to be consistent over time), so you can't guess. Just ask CTFramesetter (or CTLine) what the size is, and adjust your layout accordingly. Or if you have a hard constraint (this must fit in 100 points), then set that and it'll wrap. But if you say "well, 101 points would have been ok, too," then 100 points wasn't the constraint. Make your constraints the actual constraint, or leave it unconstrained (in which case yo probably want CTLine, not CTFramesetter).

To the second question, a CTRun is a range that has all the same attribute values attached. NSAttributedString is smart about this, and can merge together equivalent neighboring regions even if you set the attributes in overlapping ways. It does not promise that this is always done, however. It is legal for consecutive CTRun objects to have the same attributes. This can happen when the attributes are applied in complicated ways, or when attributed strings are joined together.

If you check the attributes for your NSAttributedString, you'll find that there is likely some difference between the first and second characters, the second and third characters, and then characters 4-6 have exactly the same attributes.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...