fontsrv: use CoreText API on OS X

This gets us font fallback for free and avoids use of a
deprecated API that might go away some day.

Change-Id: I4b9b1a1ce3e6d98bfb407e3baea13f4adfe2c26a
Reviewed-on: https://plan9port-review.googlesource.com/1160
Reviewed-by: Russ Cox <rsc@swtch.com>
This commit is contained in:
Russ Cox 2015-02-16 23:58:22 -05:00
parent 5d86ecd4b7
commit 32dc15fa62
5 changed files with 142 additions and 105 deletions

View file

@ -11,6 +11,7 @@ struct XFont
int unit; int unit;
double height; double height;
double originy; double originy;
void (*loadheight)(XFont*, int, int*, int*);
// fontconfig workarround, as FC_FULLNAME does not work for matching fonts. // fontconfig workarround, as FC_FULLNAME does not work for matching fonts.
char *fontfile; char *fontfile;
@ -19,7 +20,7 @@ struct XFont
void loadfonts(void); void loadfonts(void);
void load(XFont*); void load(XFont*);
Memsubfont* mksubfont(char*, int, int, int, int); Memsubfont* mksubfont(XFont*, char*, int, int, int, int);
extern XFont *xfont; extern XFont *xfont;
extern int nxfont; extern int nxfont;

View file

@ -144,7 +144,7 @@ xwalk1(Fid *fid, char *name, Qid *qid)
switch(QTYPE(path)) { switch(QTYPE(path)) {
default: default:
NotFound: NotFound:
return "file not found"; return "file not found";
case Qroot: case Qroot:
if(dotdot) if(dotdot)
@ -313,10 +313,18 @@ xread(Req *r)
fmtstrinit(&fmt); fmtstrinit(&fmt);
f = &xfont[QFONT(path)]; f = &xfont[QFONT(path)];
load(f); load(f);
if(f->unit == 0) if(f->unit == 0 && f->loadheight == nil) {
readstr(r, "font missing\n");
break; break;
height = f->height * (int)QSIZE(path)/f->unit + 0.99999999; }
ascent = height - (int)(-f->originy * (int)QSIZE(path)/f->unit + 0.99999999); height = 0;
ascent = 0;
if(f->unit > 0) {
height = f->height * (int)QSIZE(path)/f->unit + 0.99999999;
ascent = height - (int)(-f->originy * (int)QSIZE(path)/f->unit + 0.99999999);
}
if(f->loadheight != nil)
f->loadheight(f, QSIZE(path), &height, &ascent);
fmtprint(&fmt, "%11d %11d\n", height, ascent); fmtprint(&fmt, "%11d %11d\n", height, ascent);
for(i=0; i<nelem(f->range); i++) { for(i=0; i<nelem(f->range); i++) {
if(f->range[i] == 0) if(f->range[i] == 0)
@ -331,7 +339,7 @@ xread(Req *r)
f = &xfont[QFONT(path)]; f = &xfont[QFONT(path)];
load(f); load(f);
if(r->fid->aux == nil) { if(r->fid->aux == nil) {
r->fid->aux = mksubfont(f->name, QRANGE(path)<<8, (QRANGE(path)<<8)+0xFF, QSIZE(path), QANTIALIAS(path)); r->fid->aux = mksubfont(f, f->name, QRANGE(path)<<8, (QRANGE(path)<<8)+0xFF, QSIZE(path), QANTIALIAS(path));
if(r->fid->aux == nil) { if(r->fid->aux == nil) {
responderrstr(r); responderrstr(r);
return; return;

View file

@ -15,7 +15,7 @@ load(XFont *f)
} }
Memsubfont* Memsubfont*
mksubfont(char *name, int lo, int hi, int size, int antialias) mksubfont(XFont *f, char *name, int lo, int hi, int size, int antialias)
{ {
return nil; return nil;
} }

View file

@ -15,7 +15,6 @@
#include <memdraw.h> #include <memdraw.h>
#include "a.h" #include "a.h"
extern void CGFontGetGlyphsForUnichars(CGFontRef, const UniChar[], const CGGlyph[], size_t); extern void CGFontGetGlyphsForUnichars(CGFontRef, const UniChar[], const CGGlyph[], size_t);
int int
@ -84,35 +83,73 @@ loadfonts(void)
} }
} }
CGRect // Some representative text to try to discern line heights.
subfontbbox(CGFontRef font, int lo, int hi) static char *lines[] = {
"ABCDEFGHIJKLMNOPQRSTUVWXYZ",
"abcdefghijklmnopqrstuvwxyz",
"g",
"ὕαλον ϕαγεῖν δύναμαι· τοῦτο οὔ με βλάπτει.",
"私はガラスを食べられます。それは私を傷つけません。",
"Aš galiu valgyti stiklą ir jis manęs nežeidžia",
"Môžem jesť sklo. Nezraní ma.",
};
static void
fontheight(XFont *f, int size, int *height, int *ascent)
{ {
int i, first; int i;
CFStringRef s;
CGRect bbox; CGRect bbox;
CTFontRef font;
CTFontDescriptorRef desc;
CGContextRef ctxt;
CGColorSpaceRef color;
bbox.origin.x = 0; s = c2mac(f->name);
bbox.origin.y = 0; desc = CTFontDescriptorCreateWithNameAndSize(s, size);
bbox.size.height = 0; CFRelease(s);
bbox.size.width = 0; if(desc == nil)
return;
font = CTFontCreateWithFontDescriptor(desc, 0, nil);
CFRelease(desc);
if(font == nil)
return;
first = 1; color = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray);
for(i=lo; i<=hi; i++) { ctxt = CGBitmapContextCreate(nil, 1, 1, 8, 1, color, kCGImageAlphaNone);
UniChar u; CGColorSpaceRelease(color);
CGGlyph g; CGContextSetTextPosition(ctxt, 0, 0);
for(i=0; i<nelem(lines); i++) {
CFStringRef keys[] = { kCTFontAttributeName };
CFTypeRef values[] = { font };
CFStringRef str;
CFDictionaryRef attrs;
CFAttributedStringRef attrString;
CGRect r; CGRect r;
CTLineRef line;
u = mapUnicode(i); str = c2mac(lines[i]);
CGFontGetGlyphsForUnichars(font, &u, &g, 1);
if(g == 0 || !CGFontGetGlyphBBoxes(font, &g, 1, &r)) // See https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/CoreText_Programming/LayoutOperations/LayoutOperations.html#//apple_ref/doc/uid/TP40005533-CH12-SW2
continue; attrs = CFDictionaryCreate(kCFAllocatorDefault, (const void**)&keys,
(const void**)&values, sizeof(keys) / sizeof(keys[0]),
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
attrString = CFAttributedStringCreate(kCFAllocatorDefault, str, attrs);
CFRelease(str);
CFRelease(attrs);
line = CTLineCreateWithAttributedString(attrString);
r = CTLineGetImageBounds(line, ctxt);
r.size.width += r.origin.x; r.size.width += r.origin.x;
r.size.height += r.origin.y; r.size.height += r.origin.y;
if(first) { CFRelease(line);
// fprint(2, "%s: %g %g %g %g\n", lines[i], r.origin.x, r.origin.y, r.size.width, r.size.height);
if(i == 0)
bbox = r; bbox = r;
first = 0;
continue;
}
if(bbox.origin.x > r.origin.x) if(bbox.origin.x > r.origin.x)
bbox.origin.x = r.origin.x; bbox.origin.x = r.origin.x;
if(bbox.origin.y > r.origin.y) if(bbox.origin.y > r.origin.y)
@ -122,79 +159,71 @@ subfontbbox(CGFontRef font, int lo, int hi)
if(bbox.size.height < r.size.height) if(bbox.size.height < r.size.height)
bbox.size.height = r.size.height; bbox.size.height = r.size.height;
} }
bbox.size.width -= bbox.origin.x; bbox.size.width -= bbox.origin.x;
bbox.size.height -= bbox.origin.y; bbox.size.height -= bbox.origin.y;
return bbox;
*height = bbox.size.height + 0.999999;
*ascent = *height - (-bbox.origin.y + 0.999999);
CGContextRelease(ctxt);
CFRelease(font);
} }
void void
load(XFont *f) load(XFont *f)
{ {
int i, j; int i;
CGFontRef font;
CFStringRef s;
UniChar u[256];
CGGlyph g[256];
CGRect bbox;
if(f->loaded) if(f->loaded)
return; return;
f->loaded = 1; f->loaded = 1;
s = c2mac(f->name);
font = CGFontCreateWithFontName(s);
CFRelease(s);
if(font == nil)
return;
// assume bbox gives latin1 is height/ascent for all
bbox = subfontbbox(font, 0x00, 0xff);
f->unit = CGFontGetUnitsPerEm(font);
f->height = bbox.size.height;
f->originy = bbox.origin.y;
// figure out where the letters are // compute height and ascent for each size on demand
for(i=0; i<0xffff; i+=0x100) { f->loadheight = fontheight;
for(j=0; j<0x100; j++) {
u[j] = mapUnicode(i+j); // enable all Unicode ranges
g[j] = 0; for(i=0; i<nelem(f->range); i++) {
} f->range[i] = 1;
CGFontGetGlyphsForUnichars(font, u, g, 256); f->nrange++;
for(j=0; j<0x100; j++) {
if(g[j] != 0) {
f->range[i>>8] = 1;
f->nrange++;
break;
}
}
} }
CFRelease(font);
} }
Memsubfont* Memsubfont*
mksubfont(char *name, int lo, int hi, int size, int antialias) mksubfont(XFont *f, char *name, int lo, int hi, int size, int antialias)
{ {
CFStringRef s; CFStringRef s;
CGColorSpaceRef color; CGColorSpaceRef color;
CGContextRef ctxt; CGContextRef ctxt;
CGFontRef font; CTFontRef font;
CTFontDescriptorRef desc;
CGRect bbox; CGRect bbox;
Memimage *m, *mc, *m1; Memimage *m, *mc, *m1;
int x, y, y0; int x, y, y0;
int i, unit; int i, height, ascent;
Fontchar *fc, *fc0; Fontchar *fc, *fc0;
Memsubfont *sf; Memsubfont *sf;
CGFloat whitef[] = { 1.0, 1.0 };
CGColorRef white;
s = c2mac(name); s = c2mac(name);
font = CGFontCreateWithFontName(s); desc = CTFontDescriptorCreateWithNameAndSize(s, size);
CFRelease(s); CFRelease(s);
if(desc == nil)
return nil;
font = CTFontCreateWithFontDescriptor(desc, 0, nil);
CFRelease(desc);
if(font == nil) if(font == nil)
return nil; return nil;
bbox = subfontbbox(font, lo, hi);
unit = CGFontGetUnitsPerEm(font);
x = (int)(bbox.size.width * size / unit + 0.99999999); bbox = CTFontGetBoundingBox(font);
y = bbox.size.height * size/unit + 0.99999999; x = (int)(bbox.size.width + 0.99999999);
y0 = (int)(-bbox.origin.y * size/unit + 0.99999999);
fontheight(f, size, &height, &ascent);
y = height;
y0 = height - ascent;
m = allocmemimage(Rect(0, 0, x*(hi+1-lo)+1, y+1), GREY8); m = allocmemimage(Rect(0, 0, x*(hi+1-lo)+1, y+1), GREY8);
if(m == nil) if(m == nil)
return nil; return nil;
@ -217,6 +246,7 @@ mksubfont(char *name, int lo, int hi, int size, int antialias)
color = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray); color = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray);
ctxt = CGBitmapContextCreate(byteaddr(mc, mc->r.min), Dx(mc->r), Dy(mc->r), 8, ctxt = CGBitmapContextCreate(byteaddr(mc, mc->r.min), Dx(mc->r), Dy(mc->r), 8,
mc->width*sizeof(u32int), color, kCGImageAlphaNone); mc->width*sizeof(u32int), color, kCGImageAlphaNone);
white = CGColorCreate(color, whitef);
CGColorSpaceRelease(color); CGColorSpaceRelease(color);
if(ctxt == nil) { if(ctxt == nil) {
freememimage(m); freememimage(m);
@ -226,46 +256,56 @@ mksubfont(char *name, int lo, int hi, int size, int antialias)
return nil; return nil;
} }
CGContextSetFont(ctxt, font);
CGContextSetFontSize(ctxt, size);
CGContextSetAllowsAntialiasing(ctxt, antialias); CGContextSetAllowsAntialiasing(ctxt, antialias);
CGContextSetRGBFillColor(ctxt, 1, 1, 1, 1);
CGContextSetTextPosition(ctxt, 0, 0); // XXX CGContextSetTextPosition(ctxt, 0, 0); // XXX
x = 0; x = 0;
for(i=lo; i<=hi; i++, fc++) { for(i=lo; i<=hi; i++, fc++) {
UniChar u[2]; char buf[20];
CGGlyph g[2]; CFStringRef str;
CGRect r[2]; CFDictionaryRef attrs;
CFAttributedStringRef attrString;
CTLineRef line;
CGRect r;
CGPoint p1; CGPoint p1;
int n; CFStringRef keys[] = { kCTFontAttributeName, kCTForegroundColorAttributeName };
CFTypeRef values[] = { font, white };
sprint(buf, "%C", (Rune)mapUnicode(i));
str = c2mac(buf);
// See https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/CoreText_Programming/LayoutOperations/LayoutOperations.html#//apple_ref/doc/uid/TP40005533-CH12-SW2
attrs = CFDictionaryCreate(kCFAllocatorDefault, (const void**)&keys,
(const void**)&values, sizeof(keys) / sizeof(keys[0]),
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
attrString = CFAttributedStringCreate(kCFAllocatorDefault, str, attrs);
CFRelease(str);
CFRelease(attrs);
line = CTLineCreateWithAttributedString(attrString);
CGContextSetTextPosition(ctxt, 0, y0);
r = CTLineGetImageBounds(line, ctxt);
memfillcolor(mc, DBlack);
CTLineDraw(line, ctxt);
CFRelease(line);
fc->x = x; fc->x = x;
fc->top = 0; fc->top = 0;
fc->bottom = Dy(m->r); fc->bottom = Dy(m->r);
n = 0; // fprint(2, "printed %#x: %g %g\n", mapUnicode(i), p1.x, p1.y);
u[n++] = mapUnicode(i); p1 = CGContextGetTextPosition(ctxt);
if(0) // debugging if(p1.x <= 0 || mapUnicode(i) == 0xfffd) {
u[n++] = '|';
g[0] = 0;
CGFontGetGlyphsForUnichars(font, u, g, n);
if(g[0] == 0 || !CGFontGetGlyphBBoxes(font, g, n, r)) {
None:
fc->width = 0; fc->width = 0;
fc->left = 0; fc->left = 0;
if(i == 0) { if(i == 0) {
drawpjw(m, fc, x, (int)(bbox.size.width * size / unit + 0.99999999), y, y - y0); drawpjw(m, fc, x, (int)(bbox.size.width + 0.99999999), y, y - y0);
x += fc->width; x += fc->width;
} }
continue; continue;
} }
memfillcolor(mc, DBlack);
CGContextSetTextPosition(ctxt, 0, y0);
CGContextShowGlyphs(ctxt, g, n);
p1 = CGContextGetTextPosition(ctxt);
if(p1.x <= 0)
goto None;
memimagedraw(m, Rect(x, 0, x + p1.x, y), mc, ZP, memopaque, ZP, S); memimagedraw(m, Rect(x, 0, x + p1.x, y), mc, ZP, memopaque, ZP, S);
fc->width = p1.x; fc->width = p1.x;
fc->left = 0; fc->left = 0;
@ -297,4 +337,3 @@ mksubfont(char *name, int lo, int hi, int size, int antialias)
return sf; return sf;
} }

View file

@ -102,7 +102,7 @@ load(XFont *f)
} }
Memsubfont* Memsubfont*
mksubfont(char *name, int lo, int hi, int size, int antialias) mksubfont(XFont *xf, char *name, int lo, int hi, int size, int antialias)
{ {
XFont *xf, *xfp, *xfe; XFont *xf, *xfp, *xfe;
FT_Face face; FT_Face face;
@ -115,17 +115,6 @@ mksubfont(char *name, int lo, int hi, int size, int antialias)
Memsubfont *sf; Memsubfont *sf;
//Point rect_points[4]; //Point rect_points[4];
xf = nil;
for(xfp=xfont, xfe=xfont+nxfont; xfp != xfe; xfp++) {
if(strcmp(xfp->name, name) == 0) {
xf = xfp;
break;
}
}
if(!xf)
return nil;
e = FT_New_Face(lib, xf->fontfile, xf->index, &face); e = FT_New_Face(lib, xf->fontfile, xf->index, &face);
if(e){ if(e){