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:
parent
5d86ecd4b7
commit
32dc15fa62
5 changed files with 142 additions and 105 deletions
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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){
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue