tfontsrv: use CoreText API on OS X - plan9port - [fork] Plan 9 from user space
git clone git://src.adamsgaard.dk/plan9port
Log
Files
Refs
README
LICENSE
---
commit 32dc15fa62d94c88f0b62bfe4d64ba60fe1733a6
parent 5d86ecd4b7fd8bccc88a06803c7f8ace26a88788
Author: Russ Cox 
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 

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(-)
---
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;
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; irange); 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;
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;
 }
diff --git a/src/cmd/fontsrv/osx.c b/src/cmd/fontsrv/osx.c
t@@ -15,7 +15,6 @@
 #include 
 #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 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; irange); 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;
 }
-
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){