More work. Core Text chaos has resurged...

This commit is contained in:
Pietro Gagliardi 2017-01-04 23:50:08 -05:00
parent f147edf949
commit dfaf640101
3 changed files with 50 additions and 97 deletions

View File

@ -1,9 +1,6 @@
// 6 september 2015
#import "uipriv_darwin.h"
// TODO
#define complain(...) implbug(__VA_ARGS__)
// TODO double-check that we are properly handling allocation failures (or just toll free bridge from cocoa)
struct uiDrawFontFamilies {
CFArrayRef fonts;
@ -140,6 +137,7 @@ static void addFontSmallCapsAttr(CFMutableDictionaryRef attr)
}
#endif
#if 0
// Named constants for these were NOT added until 10.11, and even then they were added as external symbols instead of macros, so we can't use them directly :(
// kode54 got these for me before I had access to El Capitan; thanks to him.
#define ourNSFontWeightUltraLight -0.800000
@ -151,6 +149,7 @@ static void addFontSmallCapsAttr(CFMutableDictionaryRef attr)
#define ourNSFontWeightBold 0.400000
#define ourNSFontWeightHeavy 0.560000
#define ourNSFontWeightBlack 0.620000
#endif
// Now remember what I said earlier about having to add the small caps traits after calling the above? This gets a dictionary back so we can do so.
CFMutableDictionaryRef extractAttributes(CTFontDescriptorRef desc)
@ -215,83 +214,6 @@ void uiDrawTextFontGetMetrics(uiDrawTextFont *font, uiDrawTextFontMetrics *metri
metrics->UnderlineThickness = CTFontGetUnderlineThickness(font->f);
}
struct uiDrawTextLayout {
CFMutableAttributedStringRef mas;
CFRange *charsToRanges;
double width;
};
uiDrawTextLayout *uiDrawNewTextLayout(const char *str, uiDrawTextFont *defaultFont, double width)
{
uiDrawTextLayout *layout;
CFAttributedStringRef immutable;
CFMutableDictionaryRef attr;
CFStringRef backing;
CFIndex i, j, n;
layout = uiNew(uiDrawTextLayout);
// TODO docs say we need to use a different set of key callbacks
// TODO see if the font attribute key callbacks need to be the same
attr = newAttrList();
// this will retain defaultFont->f; no need to worry
CFDictionaryAddValue(attr, kCTFontAttributeName, defaultFont->f);
immutable = CFAttributedStringCreate(NULL, (CFStringRef) [NSString stringWithUTF8String:str], attr);
if (immutable == NULL)
complain("error creating immutable attributed string in uiDrawNewTextLayout()");
CFRelease(attr);
layout->mas = CFAttributedStringCreateMutableCopy(NULL, 0, immutable);
if (layout->mas == NULL)
complain("error creating attributed string in uiDrawNewTextLayout()");
CFRelease(immutable);
uiDrawTextLayoutSetWidth(layout, width);
// unfortunately the CFRanges for attributes expect UTF-16 codepoints
// we want graphemes
// fortunately CFStringGetRangeOfComposedCharactersAtIndex() is here for us
// https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Strings/Articles/stringsClusters.html says that this does work on all multi-codepoint graphemes (despite the name), and that this is the preferred function for this particular job anyway
backing = CFAttributedStringGetString(layout->mas);
n = CFStringGetLength(backing);
// allocate one extra, just to be safe
layout->charsToRanges = (CFRange *) uiAlloc((n + 1) * sizeof (CFRange), "CFRange[]");
i = 0;
j = 0;
while (i < n) {
CFRange range;
range = CFStringGetRangeOfComposedCharactersAtIndex(backing, i);
i = range.location + range.length;
layout->charsToRanges[j] = range;
j++;
}
// and set the last one
layout->charsToRanges[j].location = i;
layout->charsToRanges[j].length = 0;
return layout;
}
void uiDrawFreeTextLayout(uiDrawTextLayout *layout)
{
uiFree(layout->charsToRanges);
CFRelease(layout->mas);
uiFree(layout);
}
void uiDrawTextLayoutSetWidth(uiDrawTextLayout *layout, double width)
{
layout->width = width;
}
struct framesetter {
CTFramesetterRef fs;
CFMutableDictionaryRef frameAttrib;
CGSize extents;
};
// LONGTERM allow line separation and leading to be factored into a wrapping text layout
// TODO reconcile differences in character wrapping on platforms
@ -357,9 +279,6 @@ void doDrawText(CGContextRef c, CGFloat cheight, double x, double y, uiDrawTextL
// LONGTERM keep this for later features and documentation purposes
#if 0
w = CTLineGetTypographicBounds(line, &ascent, &descent, NULL);
// though CTLineGetTypographicBounds() returns 0 on error, it also returns 0 on an empty string, so we can't reasonably check for error
CFRelease(line);
// LONGTERM provide a way to get the image bounds as a separate function later
bounds = CTLineGetImageBounds(line, c);
@ -372,20 +291,7 @@ void doDrawText(CGContextRef c, CGFloat cheight, double x, double y, uiDrawTextL
CGContextSetTextPosition(c, x, y);
#endif
static CFRange charsToRange(uiDrawTextLayout *layout, int startChar, int endChar)
{
CFRange start, end;
CFRange out;
start = layout->charsToRanges[startChar];
end = layout->charsToRanges[endChar];
out.location = start.location;
out.length = end.location - start.location;
return out;
}
#define rangeToCFRange() charsToRange(layout, startChar, endChar)
#if 0
void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, int startChar, int endChar, double r, double g, double b, double a)
{
CGColorSpaceRef colorspace;
@ -407,3 +313,4 @@ void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, int startChar, int endCh
color);
CGColorRelease(color); // TODO safe?
}
#endif

View File

@ -7,6 +7,8 @@ struct uiDrawTextLayout {
CTFramesetterRef framesetter;
CGSize size;
CGPathRef path;
CTFrameRef frame;
CFArrayRef lines;
};
static CTFontRef fontdescToCTFont(uiDrawFontDescriptor *fd)
@ -93,11 +95,24 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto
rect.origin = CGZeroPoint;
rect.size = tl->size;
tl->path = CGPathCreateWithRect(rect, NULL);
tl->frame = CTFramesetterCreateFrame(tl->framesetter,
range,
tl->path,
// TODO kCTFramePathWidthAttributeName?
NULL);
if (tl->frame == NULL) {
// TODO
}
tl->lines = CTFrameGetLines(tl->frame);
return tl;
}
void uiDrawFreeTextLayout(uiDrawTextLayout *tl)
{
// TODO release tl->lines?
CFRelease(tl->frame);
CFRelease(tl->path);
CFRelease(tl->framesetter);
CFRelease(tl->attrstr);
@ -114,14 +129,44 @@ void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height
int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl)
{
return CFArrayGetCount(tl->lines);
}
// TODO release when done?
#define getline(tl, line) ((CTLineRef) CFArrayGetValueAtIndex(tl->lines, line))
void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end)
{
CTLineRef lr;
CFRange range;
lr = getline(tl, line);
range = CTLineGetStringRange(lr);
// TODO set start and end
}
void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m)
{
CTLineRef lr;
CFRange range;
CGPoint origin;
CGFloat ascent, descent, leading;
range.location = line;
range.length = 1;
CTFrameGetLineOrigins(tl->frame, range, &origin);
// TODO how exactly do we adjust this by CGPathGetPathBoundingBox(tl->path)?
m->X = origin.x;
m->Y = origin.y;
// TODO is m->Y the baseline position?
// TODO is m->Y flipped?
lr = getline(tl, line);
// though CTLineGetTypographicBounds() returns 0 on error, it also returns 0 on an empty string, so we can't reasonably check for error
m->Width = CTLineGetTypographicBounds(lr, ascent, descent, leading);
m->Ascent = ascent;
m->Descent = descent;
m->Leading = leading;
}
void uiDrawTextLayoutByteIndexToGraphemeRect(uiDrawTextLayout *tl, size_t pos, int *line, double *x, double *y, double *width, double *height)

View File

@ -97,6 +97,7 @@ struct uiDrawTextLayoutLineMetrics {
double Ascent;
double Descent;
double Leading;
// TODO trailing whitespace?
};
_UI_ENUM(uiDrawTextLayoutHitTestResult) {