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 }