plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

mac.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 }