aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/fontsrv/mac.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/fontsrv/mac.c')
-rw-r--r--src/cmd/fontsrv/mac.c362
1 files changed, 362 insertions, 0 deletions
diff --git a/src/cmd/fontsrv/mac.c b/src/cmd/fontsrv/mac.c
new file mode 100644
index 00000000..b4dadd90
--- /dev/null
+++ b/src/cmd/fontsrv/mac.c
@@ -0,0 +1,362 @@
+#include <u.h>
+
+#define Point OSXPoint
+#define Rect OSXRect
+#define Cursor OSXCursor
+#include <Carbon/Carbon.h>
+#undef Rect
+#undef Point
+#undef Cursor
+#undef offsetof
+#undef nil
+
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#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; j<nelem(skipquotemap); j++) {
+ if(strstr(name, skipquotemap[j]))
+ return i;
+ }
+ switch(i) {
+ case '\'':
+ return 0x2019;
+ case '`':
+ return 0x2018;
+ }
+ return i;
+}
+
+char*
+mac2c(CFStringRef s)
+{
+ char *p;
+ int n;
+
+ n = CFStringGetLength(s)*8;
+ p = malloc(n);
+ CFStringGetCString(s, p, n, kCFStringEncodingUTF8);
+ return p;
+}
+
+CFStringRef
+c2mac(char *p)
+{
+ return CFStringCreateWithBytes(nil, (uchar*)p, strlen(p), kCFStringEncodingUTF8, false);
+}
+
+Rectangle
+mac2r(CGRect r, int size, int unit)
+{
+ Rectangle rr;
+
+ rr.min.x = r.origin.x*size/unit;
+ rr.min.y = r.origin.y*size/unit;
+ rr.max.x = (r.origin.x+r.size.width)*size/unit + 0.99999999;
+ rr.max.y = (r.origin.x+r.size.width)*size/unit + 0.99999999;
+ return rr;
+}
+
+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; i<n; i++) {
+ f = (void*)CFArrayGetValueAtIndex(array, i);
+ if(f == nil)
+ continue;
+ s = CTFontDescriptorCopyAttribute(f, kCTFontNameAttribute);
+ xfont[nxfont].name = mac2c(s);
+ CFRelease(s);
+ nxfont++;
+ }
+}
+
+// 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;
+ 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;
+
+ color = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray);
+ ctxt = CGBitmapContextCreate(nil, 1, 1, 8, 1, color, kCGImageAlphaNone);
+ CGColorSpaceRelease(color);
+ CGContextSetTextPosition(ctxt, 0, 0);
+
+ for(i=0; i<nelem(lines); i++) {
+ CFStringRef keys[] = { kCTFontAttributeName };
+ CFTypeRef values[] = { font };
+ CFStringRef str;
+ CFDictionaryRef attrs;
+ CFAttributedStringRef attrString;
+ CGRect r;
+ CTLineRef line;
+
+ str = c2mac(lines[i]);
+
+ // 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);
+ r = CTLineGetImageBounds(line, ctxt);
+ r.size.width += r.origin.x;
+ r.size.height += r.origin.y;
+ CFRelease(line);
+
+// fprint(2, "%s: %g %g %g %g\n", lines[i], r.origin.x, r.origin.y, r.size.width, r.size.height);
+
+ if(i == 0)
+ bbox = r;
+ if(bbox.origin.x > 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
+ for(i=0; i<nelem(f->range); i++) {
+ f->range[i] = 1;
+ f->nrange++;
+ }
+}
+
+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 whitef[] = { 1.0, 1.0 };
+ CGColorRef white;
+
+ 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);
+ white = CGColorCreate(color, whitef);
+ 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, white };
+
+ 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, DBlack);
+ 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;
+ }
+
+ 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;
+}