tfontsrv: use CoreText API on OS X - plan9port - [fork] Plan 9 from user space
 (HTM) git clone git://src.adamsgaard.dk/plan9port
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 32dc15fa62d94c88f0b62bfe4d64ba60fe1733a6
 (DIR) parent 5d86ecd4b7fd8bccc88a06803c7f8ace26a88788
 (HTM) Author: Russ Cox <rsc@swtch.com>
       Date:   Mon, 16 Feb 2015 23:58:22 -0500
       
       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>
       
       Diffstat:
         M src/cmd/fontsrv/a.h                 |       3 ++-
         M src/cmd/fontsrv/main.c              |      18 +++++++++++++-----
         M src/cmd/fontsrv/nowsys.c            |       2 +-
         M src/cmd/fontsrv/osx.c               |     213 ++++++++++++++++++-------------
         M src/cmd/fontsrv/x11.c               |      13 +------------
       
       5 files changed, 143 insertions(+), 106 deletions(-)
       ---
 (DIR) diff --git a/src/cmd/fontsrv/a.h b/src/cmd/fontsrv/a.h
       t@@ -11,6 +11,7 @@ struct XFont
                int unit;
                double height;
                double originy;
       +        void (*loadheight)(XFont*, int, int*, int*);
        
                // fontconfig workarround, as FC_FULLNAME does not work for matching fonts.
                char *fontfile;
       t@@ -19,7 +20,7 @@ struct XFont
        
        void        loadfonts(void);
        void        load(XFont*);
       -Memsubfont*        mksubfont(char*, int, int, int, int);
       +Memsubfont*        mksubfont(XFont*, char*, int, int, int, int);
        
        extern XFont *xfont;
        extern int nxfont;
 (DIR) diff --git a/src/cmd/fontsrv/main.c b/src/cmd/fontsrv/main.c
       t@@ -144,7 +144,7 @@ xwalk1(Fid *fid, char *name, Qid *qid)
                switch(QTYPE(path)) {
                default:
                NotFound:
       -                return "file not  found";
       +                return "file not found";
        
                case Qroot:
                        if(dotdot)
       t@@ -313,10 +313,18 @@ xread(Req *r)
                        fmtstrinit(&fmt);
                        f = &xfont[QFONT(path)];
                        load(f);
       -                if(f->unit == 0)
       +                if(f->unit == 0 && f->loadheight == nil) {
       +                        readstr(r, "font missing\n");
                                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);
                        for(i=0; i<nelem(f->range); i++) {
                                if(f->range[i] == 0)
       t@@ -331,7 +339,7 @@ xread(Req *r)
                        f = &xfont[QFONT(path)];
                        load(f);
                        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) {
                                        responderrstr(r);
                                        return;
 (DIR) diff --git a/src/cmd/fontsrv/nowsys.c b/src/cmd/fontsrv/nowsys.c
       t@@ -15,7 +15,7 @@ load(XFont *f)
        }
        
        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;
        }
 (DIR) diff --git a/src/cmd/fontsrv/osx.c b/src/cmd/fontsrv/osx.c
       t@@ -15,7 +15,6 @@
        #include <memdraw.h>
        #include "a.h"
        
       -
        extern void CGFontGetGlyphsForUnichars(CGFontRef, const UniChar[], const CGGlyph[], size_t);
        
        int
       t@@ -84,35 +83,73 @@ loadfonts(void)
                }
        }
        
       -CGRect
       -subfontbbox(CGFontRef font, int lo, int hi)
       +// Some representative text to try to discern line heights.
       +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;
       +        CTFontRef font;
       +        CTFontDescriptorRef desc;
       +        CGContextRef ctxt;
       +        CGColorSpaceRef color;
       +
       +        s = c2mac(f->name);
       +        desc = CTFontDescriptorCreateWithNameAndSize(s, size);
       +        CFRelease(s);
       +        if(desc == nil)
       +                return;
       +        font = CTFontCreateWithFontDescriptor(desc, 0, nil);
       +        CFRelease(desc);
       +        if(font == nil)
       +                return;
        
       -        bbox.origin.x = 0;
       -        bbox.origin.y = 0;
       -        bbox.size.height = 0;
       -        bbox.size.width = 0;
       +        color = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray);
       +        ctxt = CGBitmapContextCreate(nil, 1, 1, 8, 1, color, kCGImageAlphaNone);
       +        CGColorSpaceRelease(color);
       +        CGContextSetTextPosition(ctxt, 0, 0);
        
       -        first = 1;
       -        for(i=lo; i<=hi; i++) {
       -                UniChar u;
       -                CGGlyph g;
       +        for(i=0; i<nelem(lines); i++) {
       +                CFStringRef keys[] = { kCTFontAttributeName };
       +                CFTypeRef values[] = { font };
       +                CFStringRef str;
       +                CFDictionaryRef attrs;
       +                CFAttributedStringRef attrString;
                        CGRect r;
       +                CTLineRef line;
        
       -                u = mapUnicode(i);
       -                CGFontGetGlyphsForUnichars(font, &u, &g, 1);
       -                if(g == 0 || !CGFontGetGlyphBBoxes(font, &g, 1, &r))
       -                        continue;
       +                 str = c2mac(lines[i]);
       +                 
       +                 // 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);
       +                r = CTLineGetImageBounds(line, ctxt);
                        r.size.width += r.origin.x;
                        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;
       -                        first = 0;
       -                        continue;
       -                }
                        if(bbox.origin.x > r.origin.x)
                                bbox.origin.x = r.origin.x;        
                        if(bbox.origin.y > r.origin.y)
       t@@ -122,79 +159,71 @@ subfontbbox(CGFontRef font, int lo, int hi)
                        if(bbox.size.height < r.size.height)
                                bbox.size.height = r.size.height;
                }
       -        
       +
                bbox.size.width -= bbox.origin.x;
                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
        load(XFont *f)
        {
       -        int i, j;
       -        CGFontRef font;
       -        CFStringRef s;
       -        UniChar u[256];
       -        CGGlyph g[256];
       -        CGRect bbox;
       +        int i;
        
                if(f->loaded)
                        return;
                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
       -        for(i=0; i<0xffff; i+=0x100) {
       -                for(j=0; j<0x100; j++) {
       -                        u[j] = mapUnicode(i+j);
       -                        g[j] = 0;
       -                }
       -                CGFontGetGlyphsForUnichars(font, u, g, 256);
       -                for(j=0; j<0x100; j++) {
       -                        if(g[j] != 0) {
       -                                f->range[i>>8] = 1;
       -                                f->nrange++;
       -                                break;
       -                        }
       -                }
       +
       +        // compute height and ascent for each size on demand
       +        f->loadheight = fontheight;
       +
       +        // enable all Unicode ranges
       +        for(i=0; i<nelem(f->range); i++) {
       +                f->range[i] = 1;
       +                f->nrange++;
                }
       -        CFRelease(font);
        }
        
        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;
                CGColorSpaceRef color;
                CGContextRef ctxt;
       -        CGFontRef font;
       +        CTFontRef font;
       +        CTFontDescriptorRef desc;
                CGRect bbox;
                Memimage *m, *mc, *m1;
                int x, y, y0;
       -        int i, unit;
       +        int i, height, ascent;
                Fontchar *fc, *fc0;
                Memsubfont *sf;
       +        CGFloat whitef[] = { 1.0, 1.0 };
       +        CGColorRef white;
        
                s = c2mac(name);
       -        font = CGFontCreateWithFontName(s);
       +        desc = CTFontDescriptorCreateWithNameAndSize(s, size);
                CFRelease(s);
       +        if(desc == nil)
       +                return nil;
       +        font = CTFontCreateWithFontDescriptor(desc, 0, nil);
       +        CFRelease(desc);
                if(font == nil)
                        return nil;
       -        bbox = subfontbbox(font, lo, hi);
       -        unit = CGFontGetUnitsPerEm(font);
       -        x = (int)(bbox.size.width * size / unit + 0.99999999);
       -        y = bbox.size.height * size/unit + 0.99999999;
       -        y0 = (int)(-bbox.origin.y * size/unit + 0.99999999);
       +        
       +        
       +        bbox = CTFontGetBoundingBox(font);
       +        x = (int)(bbox.size.width + 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);
                if(m == nil)
                        return nil;
       t@@ -217,6 +246,7 @@ mksubfont(char *name, int lo, int hi, int size, int antialias)
                color = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray);
                ctxt = CGBitmapContextCreate(byteaddr(mc, mc->r.min), Dx(mc->r), Dy(mc->r), 8,
                        mc->width*sizeof(u32int), color, kCGImageAlphaNone);
       +        white = CGColorCreate(color, whitef);
                CGColorSpaceRelease(color);
                if(ctxt == nil) {
                        freememimage(m);
       t@@ -226,46 +256,56 @@ mksubfont(char *name, int lo, int hi, int size, int antialias)
                        return nil;
                }
        
       -        CGContextSetFont(ctxt, font);
       -        CGContextSetFontSize(ctxt, size);
                CGContextSetAllowsAntialiasing(ctxt, antialias);
       -        CGContextSetRGBFillColor(ctxt, 1, 1, 1, 1);
                CGContextSetTextPosition(ctxt, 0, 0);        // XXX
        
                x = 0;
                for(i=lo; i<=hi; i++, fc++) {
       -                UniChar u[2];
       -                CGGlyph g[2];
       -                CGRect r[2];
       +                char buf[20];
       +                CFStringRef str;
       +                CFDictionaryRef attrs;
       +                CFAttributedStringRef attrString;
       +                CTLineRef line;
       +                CGRect r;
                        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->top = 0;
                        fc->bottom = Dy(m->r);
        
       -                n = 0;
       -                u[n++] = mapUnicode(i);
       -                if(0)        // debugging
       -                        u[n++] = '|';
       -                g[0] = 0;
       -                CGFontGetGlyphsForUnichars(font, u, g, n);
       -                if(g[0] == 0 || !CGFontGetGlyphBBoxes(font, g, n, r)) {
       -                None:
       +//                fprint(2, "printed %#x: %g %g\n", mapUnicode(i), p1.x, p1.y);
       +                p1 = CGContextGetTextPosition(ctxt);
       +                if(p1.x <= 0 || mapUnicode(i) == 0xfffd) {
                                fc->width = 0;
                                fc->left = 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;
                                }        
                                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);
                        fc->width = p1.x;
                        fc->left = 0;
       t@@ -297,4 +337,3 @@ mksubfont(char *name, int lo, int hi, int size, int antialias)
                
                return sf;
        }
       -
 (DIR) diff --git a/src/cmd/fontsrv/x11.c b/src/cmd/fontsrv/x11.c
       t@@ -102,7 +102,7 @@ load(XFont *f)
        }
        
        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;
                FT_Face face;
       t@@ -115,17 +115,6 @@ mksubfont(char *name, int lo, int hi, int size, int antialias)
                Memsubfont *sf;
                //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);
        
                if(e){