tmac.c - 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
       ---
       tmac.c (8926B)
       ---
            1 #include <u.h>
            2 
            3 #define Point OSXPoint
            4 #define Rect OSXRect
            5 #define Cursor OSXCursor
            6 #include <Carbon/Carbon.h>
            7 #undef Rect
            8 #undef Point
            9 #undef Cursor
           10 #undef offsetof
           11 #undef nil
           12 
           13 #include <libc.h>
           14 #include <draw.h>
           15 #include <memdraw.h>
           16 #include "a.h"
           17 
           18 extern void CGFontGetGlyphsForUnichars(CGFontRef, const UniChar[], const CGGlyph[], size_t);
           19 
           20 // In these fonts, it's too hard to distinguish U+2018 and U+2019,
           21 // so don't map the ASCII quotes there.
           22 // See https://github.com/9fans/plan9port/issues/86
           23 static char *skipquotemap[] = {
           24         "Courier",
           25         "Osaka",
           26 };
           27 
           28 int
           29 mapUnicode(char *name, int i)
           30 {
           31         int j;
           32 
           33         if(0xd800 <= i && i < 0xe000) // surrogate pairs, will crash OS X libraries!
           34                 return 0xfffd;
           35         for(j=0; j<nelem(skipquotemap); j++) {
           36                 if(strstr(name, skipquotemap[j]))
           37                         return i;
           38         }
           39         switch(i) {
           40         case '\'':
           41                 return 0x2019;
           42         case '`':
           43                 return 0x2018;
           44         }
           45         return i;
           46 }
           47 
           48 char*
           49 mac2c(CFStringRef s)
           50 {
           51         char *p;
           52         int n;
           53 
           54         n = CFStringGetLength(s)*8;
           55         p = malloc(n);
           56         CFStringGetCString(s, p, n, kCFStringEncodingUTF8);
           57         return p;
           58 }
           59 
           60 CFStringRef
           61 c2mac(char *p)
           62 {
           63         return CFStringCreateWithBytes(nil, (uchar*)p, strlen(p), kCFStringEncodingUTF8, false);
           64 }
           65 
           66 Rectangle
           67 mac2r(CGRect r, int size, int unit)
           68 {
           69         Rectangle rr;
           70 
           71         rr.min.x = r.origin.x*size/unit;
           72         rr.min.y = r.origin.y*size/unit;
           73         rr.max.x = (r.origin.x+r.size.width)*size/unit + 0.99999999;
           74         rr.max.y = (r.origin.x+r.size.width)*size/unit + 0.99999999;
           75         return rr;
           76 }
           77 
           78 void
           79 meminvert(Memimage *m)
           80 {
           81         uchar *p, *ep;
           82 
           83         p = byteaddr(m, m->r.min);
           84         ep = p + 4*m->width*Dy(m->r);
           85         for(; p < ep; p++)
           86                 *p ^= 0xff;
           87 }
           88 
           89 void
           90 loadfonts(void)
           91 {
           92         int i, n;
           93         CTFontCollectionRef allc;
           94         CFArrayRef array;
           95         CFStringRef s;
           96         CTFontDescriptorRef f;
           97 
           98         allc = CTFontCollectionCreateFromAvailableFonts(0);
           99         array = CTFontCollectionCreateMatchingFontDescriptors(allc);
          100         n = CFArrayGetCount(array);
          101         xfont = emalloc9p(n*sizeof xfont[0]);
          102         for(i=0; i<n; i++) {
          103                 f = (void*)CFArrayGetValueAtIndex(array, i);
          104                 if(f == nil)
          105                         continue;
          106                 s = CTFontDescriptorCopyAttribute(f, kCTFontNameAttribute);
          107                 xfont[nxfont].name = mac2c(s);
          108                 CFRelease(s);
          109                 nxfont++;
          110         }
          111 }
          112 
          113 // Some representative text to try to discern line heights.
          114 static char *lines[] = {
          115         "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
          116         "abcdefghijklmnopqrstuvwxyz",
          117         "g",
          118         "ÁĂÇÂÄĊÀČĀĄÅÃĥľƒ",
          119         "ὕαλον ϕαγεῖν δύναμαι· τοῦτο οὔ με βλάπτει.",
          120         "私はガラスを食べられます。それは私を傷つけません。",
          121         "Aš galiu valgyti stiklą ir jis manęs nežeidžia",
          122         "Môžem jesť sklo. Nezraní ma.",
          123 };
          124 
          125 static void
          126 fontheight(XFont *f, int size, int *height, int *ascent)
          127 {
          128         int i;
          129         CFStringRef s;
          130         CGRect bbox;
          131         CTFontRef font;
          132         CTFontDescriptorRef desc;
          133         CGContextRef ctxt;
          134         CGColorSpaceRef color;
          135 
          136         s = c2mac(f->name);
          137         desc = CTFontDescriptorCreateWithNameAndSize(s, size);
          138         CFRelease(s);
          139         if(desc == nil)
          140                 return;
          141         font = CTFontCreateWithFontDescriptor(desc, 0, nil);
          142         CFRelease(desc);
          143         if(font == nil)
          144                 return;
          145 
          146         color = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray);
          147         ctxt = CGBitmapContextCreate(nil, 1, 1, 8, 1, color, kCGImageAlphaNone);
          148         CGColorSpaceRelease(color);
          149         CGContextSetTextPosition(ctxt, 0, 0);
          150 
          151         for(i=0; i<nelem(lines); i++) {
          152                 CFStringRef keys[] = { kCTFontAttributeName };
          153                 CFTypeRef values[] = { font };
          154                 CFStringRef str;
          155                 CFDictionaryRef attrs;
          156                 CFAttributedStringRef attrString;
          157                 CGRect r;
          158                 CTLineRef line;
          159 
          160                  str = c2mac(lines[i]);
          161 
          162                  // See https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/CoreText_Programming/LayoutOperations/LayoutOperations.html#//apple_ref/doc/uid/TP40005533-CH12-SW2
          163                  attrs = CFDictionaryCreate(kCFAllocatorDefault, (const void**)&keys,
          164                         (const void**)&values, sizeof(keys) / sizeof(keys[0]),
          165                         &kCFTypeDictionaryKeyCallBacks,
          166                         &kCFTypeDictionaryValueCallBacks);
          167                 attrString = CFAttributedStringCreate(kCFAllocatorDefault, str, attrs);
          168                 CFRelease(str);
          169                 CFRelease(attrs);
          170 
          171                 line = CTLineCreateWithAttributedString(attrString);
          172                 r = CTLineGetImageBounds(line, ctxt);
          173                 r.size.width += r.origin.x;
          174                 r.size.height += r.origin.y;
          175                 CFRelease(line);
          176 
          177 //        fprint(2, "%s: %g %g %g %g\n", lines[i], r.origin.x, r.origin.y, r.size.width, r.size.height);
          178 
          179                 if(i == 0)
          180                         bbox = r;
          181                 if(bbox.origin.x > r.origin.x)
          182                         bbox.origin.x = r.origin.x;
          183                 if(bbox.origin.y > r.origin.y)
          184                         bbox.origin.y = r.origin.y;
          185                 if(bbox.size.width < r.size.width)
          186                         bbox.size.width = r.size.width;
          187                 if(bbox.size.height < r.size.height)
          188                         bbox.size.height = r.size.height;
          189         }
          190 
          191         bbox.size.width -= bbox.origin.x;
          192         bbox.size.height -= bbox.origin.y;
          193 
          194         *height = bbox.size.height + 0.999999;
          195         *ascent = *height - (-bbox.origin.y + 0.999999);
          196 
          197         CGContextRelease(ctxt);
          198         CFRelease(font);
          199 }
          200 
          201 void
          202 load(XFont *f)
          203 {
          204         int i;
          205 
          206         if(f->loaded)
          207                 return;
          208         f->loaded = 1;
          209 
          210         // compute height and ascent for each size on demand
          211         f->loadheight = fontheight;
          212 
          213         // enable all Unicode ranges
          214         if(nelem(f->file) > 0xffff)
          215                 sysfatal("too many subfiles"); // f->file holds ushorts
          216         for(i=0; i<nelem(f->range); i++) {
          217                 f->range[i] = 1;
          218                 f->file[i] = i;
          219                 f->nfile++;
          220         }
          221 }
          222 
          223 Memsubfont*
          224 mksubfont(XFont *f, char *name, int lo, int hi, int size, int antialias)
          225 {
          226         CFStringRef s;
          227         CGColorSpaceRef color;
          228         CGContextRef ctxt;
          229         CTFontRef font;
          230         CTFontDescriptorRef desc;
          231         CGRect bbox;
          232         Memimage *m, *mc, *m1;
          233         int x, y, y0;
          234         int i, height, ascent;
          235         Fontchar *fc, *fc0;
          236         Memsubfont *sf;
          237         CGFloat blackf[] = { 0.0, 1.0 };
          238         CGColorRef black;
          239 
          240         s = c2mac(name);
          241         desc = CTFontDescriptorCreateWithNameAndSize(s, size);
          242         CFRelease(s);
          243         if(desc == nil)
          244                 return nil;
          245         font = CTFontCreateWithFontDescriptor(desc, 0, nil);
          246         CFRelease(desc);
          247         if(font == nil)
          248                 return nil;
          249 
          250         bbox = CTFontGetBoundingBox(font);
          251         x = (int)(bbox.size.width*2 + 0.99999999);
          252 
          253         fontheight(f, size, &height, &ascent);
          254         y = height;
          255         y0 = height - ascent;
          256 
          257         m = allocmemimage(Rect(0, 0, x*(hi+1-lo)+1, y+1), GREY8);
          258         if(m == nil)
          259                 return nil;
          260         mc = allocmemimage(Rect(0, 0, x+1, y+1), GREY8);
          261         if(mc == nil){
          262                 freememimage(m);
          263                 return nil;
          264         }
          265         memfillcolor(m, DBlack);
          266         memfillcolor(mc, DBlack);
          267         fc = malloc((hi+2 - lo) * sizeof fc[0]);
          268         sf = malloc(sizeof *sf);
          269         if(fc == nil || sf == nil) {
          270                 freememimage(m);
          271                 freememimage(mc);
          272                 free(fc);
          273                 free(sf);
          274                 return nil;
          275         }
          276         fc0 = fc;
          277 
          278         color = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray);
          279         ctxt = CGBitmapContextCreate(byteaddr(mc, mc->r.min), Dx(mc->r), Dy(mc->r), 8,
          280                 mc->width*sizeof(u32int), color, kCGImageAlphaNone);
          281         black = CGColorCreate(color, blackf);
          282         CGColorSpaceRelease(color);
          283         if(ctxt == nil) {
          284                 freememimage(m);
          285                 freememimage(mc);
          286                 free(fc);
          287                 free(sf);
          288                 return nil;
          289         }
          290 
          291         CGContextSetAllowsAntialiasing(ctxt, antialias);
          292         CGContextSetTextPosition(ctxt, 0, 0);        // XXX
          293 #if OSX_VERSION >= 101400
          294         CGContextSetAllowsFontSmoothing(ctxt, false);
          295 #endif
          296 
          297         x = 0;
          298         for(i=lo; i<=hi; i++, fc++) {
          299                 char buf[20];
          300                 CFStringRef str;
          301                 CFDictionaryRef attrs;
          302                 CFAttributedStringRef attrString;
          303                 CTLineRef line;
          304                 CGRect r;
          305                 CGPoint p1;
          306                 CFStringRef keys[] = { kCTFontAttributeName, kCTForegroundColorAttributeName };
          307                 CFTypeRef values[] = { font, black };
          308 
          309                 sprint(buf, "%C", (Rune)mapUnicode(name, i));
          310                  str = c2mac(buf);
          311 
          312                  // See https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/CoreText_Programming/LayoutOperations/LayoutOperations.html#//apple_ref/doc/uid/TP40005533-CH12-SW2
          313                  attrs = CFDictionaryCreate(kCFAllocatorDefault, (const void**)&keys,
          314                         (const void**)&values, sizeof(keys) / sizeof(keys[0]),
          315                         &kCFTypeDictionaryKeyCallBacks,
          316                         &kCFTypeDictionaryValueCallBacks);
          317                 attrString = CFAttributedStringCreate(kCFAllocatorDefault, str, attrs);
          318                 CFRelease(str);
          319                 CFRelease(attrs);
          320 
          321                 line = CTLineCreateWithAttributedString(attrString);
          322                 CGContextSetTextPosition(ctxt, 0, y0);
          323                 r = CTLineGetImageBounds(line, ctxt);
          324                 memfillcolor(mc, DWhite);
          325                 CTLineDraw(line, ctxt);
          326                 CFRelease(line);
          327 
          328                 fc->x = x;
          329                 fc->top = 0;
          330                 fc->bottom = Dy(m->r);
          331 
          332 //                fprint(2, "printed %#x: %g %g\n", mapUnicode(i), p1.x, p1.y);
          333                 p1 = CGContextGetTextPosition(ctxt);
          334                 if(p1.x <= 0 || mapUnicode(name, i) == 0xfffd) {
          335                         fc->width = 0;
          336                         fc->left = 0;
          337                         if(i == 0) {
          338                                 drawpjw(m, fc, x, (int)(bbox.size.width + 0.99999999), y, y - y0);
          339                                 x += fc->width;
          340                         }
          341                         continue;
          342                 }
          343 
          344                 meminvert(mc);
          345                 memimagedraw(m, Rect(x, 0, x + p1.x, y), mc, ZP, memopaque, ZP, S);
          346                 fc->width = p1.x;
          347                 fc->left = 0;
          348                 x += p1.x;
          349         }
          350         fc->x = x;
          351 
          352         // round up to 32-bit boundary
          353         // so that in-memory data is same
          354         // layout as in-file data.
          355         if(x == 0)
          356                 x = 1;
          357         if(y == 0)
          358                 y = 1;
          359         if(antialias)
          360                 x += -x & 3;
          361         else
          362                 x += -x & 31;
          363         m1 = allocmemimage(Rect(0, 0, x, y), antialias ? GREY8 : GREY1);
          364         memimagedraw(m1, m1->r, m, m->r.min, memopaque, ZP, S);
          365         freememimage(m);
          366         freememimage(mc);
          367 
          368         sf->name = nil;
          369         sf->n = hi+1 - lo;
          370         sf->height = Dy(m1->r);
          371         sf->ascent = Dy(m1->r) - y0;
          372         sf->info = fc0;
          373         sf->bits = m1;
          374 
          375         return sf;
          376 }