128 lines
3.9 KiB
Mathematica
128 lines
3.9 KiB
Mathematica
|
// 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);
|