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
mapUnicode(char * name,int i)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*
mac2c(CFStringRef s)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
c2mac(char * p)61 c2mac(char *p)
62 {
63 	return CFStringCreateWithBytes(nil, (uchar*)p, strlen(p), kCFStringEncodingUTF8, false);
64 }
65 
66 Rectangle
mac2r(CGRect r,int size,int unit)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
meminvert(Memimage * m)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
loadfonts(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
fontheight(XFont * f,int size,int * height,int * ascent)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
load(XFont * f)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*
mksubfont(XFont * f,char * name,int lo,int hi,int size,int antialias)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 }
377