#include #define Point OSXPoint #define Rect OSXRect #define Cursor OSXCursor #include #undef Rect #undef Point #undef Cursor #undef offsetof #undef nil #include #include #include #include "a.h" extern void CGFontGetGlyphsForUnichars(CGFontRef, const UniChar[], const CGGlyph[], size_t); // In these fonts, it's too hard to distinguish U+2018 and U+2019, // so don't map the ASCII quotes there. // See https://github.com/9fans/plan9port/issues/86 static char *skipquotemap[] = { "Courier", "Osaka", }; int mapUnicode(char *name, int i) { int j; if(0xd800 <= i && i < 0xe000) // surrogate pairs, will crash OS X libraries! return 0xfffd; for(j=0; jr.min); ep = p + 4*m->width*Dy(m->r); for(; p < ep; p++) *p ^= 0xff; } void loadfonts(void) { int i, n; CTFontCollectionRef allc; CFArrayRef array; CFStringRef s; CTFontDescriptorRef f; allc = CTFontCollectionCreateFromAvailableFonts(0); array = CTFontCollectionCreateMatchingFontDescriptors(allc); n = CFArrayGetCount(array); xfont = emalloc9p(n*sizeof xfont[0]); for(i=0; iname); desc = CTFontDescriptorCreateWithNameAndSize(s, size); CFRelease(s); if(desc == nil) return; font = CTFontCreateWithFontDescriptor(desc, 0, nil); CFRelease(desc); if(font == nil) return; color = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray); ctxt = CGBitmapContextCreate(nil, 1, 1, 8, 1, color, kCGImageAlphaNone); CGColorSpaceRelease(color); CGContextSetTextPosition(ctxt, 0, 0); for(i=0; i r.origin.x) bbox.origin.x = r.origin.x; if(bbox.origin.y > r.origin.y) bbox.origin.y = r.origin.y; if(bbox.size.width < r.size.width) bbox.size.width = r.size.width; 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; *height = bbox.size.height + 0.999999; *ascent = *height - (-bbox.origin.y + 0.999999); CGContextRelease(ctxt); CFRelease(font); } void load(XFont *f) { int i; if(f->loaded) return; f->loaded = 1; // compute height and ascent for each size on demand f->loadheight = fontheight; // enable all Unicode ranges if(nelem(f->file) > 0xffff) sysfatal("too many subfiles"); // f->file holds ushorts for(i=0; irange); i++) { f->range[i] = 1; f->file[i] = i; f->nfile++; } } Memsubfont* mksubfont(XFont *f, char *name, int lo, int hi, int size, int antialias) { CFStringRef s; CGColorSpaceRef color; CGContextRef ctxt; CTFontRef font; CTFontDescriptorRef desc; CGRect bbox; Memimage *m, *mc, *m1; int x, y, y0; int i, height, ascent; Fontchar *fc, *fc0; Memsubfont *sf; CGFloat blackf[] = { 0.0, 1.0 }; CGColorRef black; s = c2mac(name); desc = CTFontDescriptorCreateWithNameAndSize(s, size); CFRelease(s); if(desc == nil) return nil; font = CTFontCreateWithFontDescriptor(desc, 0, nil); CFRelease(desc); if(font == nil) return nil; bbox = CTFontGetBoundingBox(font); x = (int)(bbox.size.width*2 + 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; mc = allocmemimage(Rect(0, 0, x+1, y+1), GREY8); if(mc == nil){ freememimage(m); return nil; } memfillcolor(m, DBlack); memfillcolor(mc, DBlack); fc = malloc((hi+2 - lo) * sizeof fc[0]); sf = malloc(sizeof *sf); if(fc == nil || sf == nil) { freememimage(m); freememimage(mc); free(fc); free(sf); return nil; } fc0 = fc; color = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray); ctxt = CGBitmapContextCreate(byteaddr(mc, mc->r.min), Dx(mc->r), Dy(mc->r), 8, mc->width*sizeof(u32int), color, kCGImageAlphaNone); black = CGColorCreate(color, blackf); CGColorSpaceRelease(color); if(ctxt == nil) { freememimage(m); freememimage(mc); free(fc); free(sf); return nil; } CGContextSetAllowsAntialiasing(ctxt, antialias); CGContextSetTextPosition(ctxt, 0, 0); // XXX #if OSX_VERSION >= 101400 CGContextSetAllowsFontSmoothing(ctxt, false); #endif x = 0; for(i=lo; i<=hi; i++, fc++) { char buf[20]; CFStringRef str; CFDictionaryRef attrs; CFAttributedStringRef attrString; CTLineRef line; CGRect r; CGPoint p1; CFStringRef keys[] = { kCTFontAttributeName, kCTForegroundColorAttributeName }; CFTypeRef values[] = { font, black }; sprint(buf, "%C", (Rune)mapUnicode(name, 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, DWhite); CTLineDraw(line, ctxt); CFRelease(line); fc->x = x; fc->top = 0; fc->bottom = Dy(m->r); // fprint(2, "printed %#x: %g %g\n", mapUnicode(i), p1.x, p1.y); p1 = CGContextGetTextPosition(ctxt); if(p1.x <= 0 || mapUnicode(name, i) == 0xfffd) { fc->width = 0; fc->left = 0; if(i == 0) { drawpjw(m, fc, x, (int)(bbox.size.width + 0.99999999), y, y - y0); x += fc->width; } continue; } meminvert(mc); memimagedraw(m, Rect(x, 0, x + p1.x, y), mc, ZP, memopaque, ZP, S); fc->width = p1.x; fc->left = 0; x += p1.x; } fc->x = x; // round up to 32-bit boundary // so that in-memory data is same // layout as in-file data. if(x == 0) x = 1; if(y == 0) y = 1; if(antialias) x += -x & 3; else x += -x & 31; m1 = allocmemimage(Rect(0, 0, x, y), antialias ? GREY8 : GREY1); memimagedraw(m1, m1->r, m, m->r.min, memopaque, ZP, S); freememimage(m); freememimage(mc); sf->name = nil; sf->n = hi+1 - lo; sf->height = Dy(m1->r); sf->ascent = Dy(m1->r) - y0; sf->info = fc0; sf->bits = m1; return sf; }