Started a new drawtext.m with a different way to handle the empty-string crash problem.
This commit is contained in:
parent
bf58601ff8
commit
7451d455e5
|
@ -449,6 +449,7 @@ static CTParagraphStyleRef mkParagraphStyle(uiDrawTextLayoutParams *p)
|
|||
return ps;
|
||||
}
|
||||
|
||||
// TODO either rename this to uiprivDrawTextLayoutParams... or rename this file or both or split the struct or something else...
|
||||
CFAttributedStringRef uiprivAttributedStringToCFAttributedString(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks)
|
||||
{
|
||||
CFStringRef cfstr;
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
// 7 march 2018
|
||||
#import "uipriv_darwin.h"
|
||||
#import "draw.h"
|
||||
#import "attrstr.h"
|
||||
|
||||
// problem: for a CTFrame made from an empty string, the CTLine array will be empty, and we will crash when gathering metrics or hit-testing
|
||||
// solution: for those cases, maintain a separate framesetter just for computing those things
|
||||
// in the usual case, the separate copy will just be identical to the regular one, with extra references to everything within
|
||||
struct frame {
|
||||
CFAttributedStringRef attrstr;
|
||||
NSArray *backgroundBlocks;
|
||||
CTFramesetterRef framesetter;
|
||||
CGSize size;
|
||||
CGPathRef path;
|
||||
CTFrameRef frame;
|
||||
};
|
||||
|
||||
struct uiDrawTextLayout {
|
||||
struct frame forDrawing;
|
||||
struct frame forMetrics;
|
||||
};
|
||||
|
||||
static void paramsToFrame(uiDrawTextLayoutParams *params, struct frame *frame)
|
||||
{
|
||||
CFRange range;
|
||||
CGFloat width;
|
||||
CFRange unused;
|
||||
CGRect rect;
|
||||
|
||||
frame->attrstr = uiprivAttributedStringToCFAttributedString(p, &(frame->backgroundBlocks));
|
||||
// TODO kCTParagraphStyleSpecifierMaximumLineSpacing, kCTParagraphStyleSpecifierMinimumLineSpacing, kCTParagraphStyleSpecifierLineSpacingAdjustment for line spacing
|
||||
frame->framesetter = CTFramesetterCreateWithAttributedString(tl->attrstr);
|
||||
if (frame->framesetter == NULL) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
range.location = 0;
|
||||
range.length = CFAttributedStringGetLength(tl->attrstr);
|
||||
|
||||
cgwidth = (CGFloat) (frame->width);
|
||||
if (cgwidth < 0)
|
||||
cgwidth = CGFLOAT_MAX;
|
||||
frame->size = CTFramesetterSuggestFrameSizeWithConstraints(frame->framesetter,
|
||||
range,
|
||||
// TODO kCTFramePathWidthAttributeName?
|
||||
NULL,
|
||||
CGSizeMake(cgwidth, CGFLOAT_MAX),
|
||||
&unused); // not documented as accepting NULL (TODO really?)
|
||||
|
||||
rect.origin = CGPointZero;
|
||||
rect.size = frame->size;
|
||||
frame->path = CGPathCreateWithRect(rect, NULL);
|
||||
frame->frame = CTFramesetterCreateFrame(tl->framesetter,
|
||||
range,
|
||||
tl->path,
|
||||
// TODO kCTFramePathWidthAttributeName?
|
||||
NULL);
|
||||
if (frame->frame == NULL) {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
static void freeFrame(struct frame *frame)
|
||||
{
|
||||
CFRelease(frame->frame);
|
||||
CFRelease(frame->path);
|
||||
CFRelease(frame->framesetter);
|
||||
[frame->backgroundBlocks release];
|
||||
CFRelease(frame->attrstr);
|
||||
}
|
||||
|
||||
static void retainFrameCopy(struct frame *out, const struct frame *frame)
|
||||
{
|
||||
memcpy(out, frame, sizeof (struct frame));
|
||||
CFRetain(out->attrstr);
|
||||
[out->backgroundBlocks retain];
|
||||
CFRetain(out->framesetter);
|
||||
CFRetain(out->path);
|
||||
CFRetain(out->frame);
|
||||
}
|
||||
|
||||
uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p)
|
||||
{
|
||||
uiDrawTextLayout *tl;
|
||||
|
||||
tl = uiprivNew(uiDrawTextLayout);
|
||||
paramsToFrame(p, &(tl->forDrawing));
|
||||
if (uiAttributedStringLength(p->String) != 0)
|
||||
retainFrameCopy(&(tl->forMetrics), &(tl->forDrawing));
|
||||
else {
|
||||
uiAttributedString *space;
|
||||
uiDrawTextLayoutParams p2;
|
||||
|
||||
space = uiNewAttributedString(" ");
|
||||
p2 = *p;
|
||||
p2.String = space;
|
||||
paramsToFrame(&p2, &(tl->forMetrics));
|
||||
uiFreeAttributedString(space);
|
||||
}
|
||||
return tl;
|
||||
}
|
||||
|
||||
void uiDrawFreeTextLayout(uiDrawTextLayout *tl)
|
||||
{
|
||||
freeFrame(&(tl->forMetrics));
|
||||
freeFrame(&(tl->forDrawing));
|
||||
uiprivFree(tl);
|
||||
}
|
||||
|
||||
// uiDrawText() draws tl in c with the top-left point of tl at (x, y).
|
||||
_UI_EXTERN void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y);
|
||||
|
||||
// uiDrawTextLayoutExtents() returns the width and height of tl
|
||||
// in width and height. The returned width may be smaller than
|
||||
// the width passed into uiDrawNewTextLayout() depending on
|
||||
// how the text in tl is wrapped. Therefore, you can use this
|
||||
// function to get the actual size of the text layout.
|
||||
_UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height);
|
||||
|
||||
// uiDrawTextLayoutNumLines() returns the number of lines in tl.
|
||||
// This number will always be greater than or equal to 1; a text
|
||||
// layout with no text only has one line.
|
||||
_UI_EXTERN int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl);
|
||||
|
||||
// uiDrawTextLayoutLineByteRange() returns the byte indices of the
|
||||
// text that falls into the given line of tl as [start, end).
|
||||
_UI_EXTERN void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end);
|
Loading…
Reference in New Issue