Blob


1 #include <u.h>
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
13 #include <libc.h>
14 #include <draw.h>
15 #include <memdraw.h>
16 #include "a.h"
18 extern void CGFontGetGlyphsForUnichars(CGFontRef, const UniChar[], const CGGlyph[], size_t);
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 };
28 int
29 mapUnicode(char *name, int i)
30 {
31 int j;
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 }
48 char*
49 mac2c(CFStringRef s)
50 {
51 char *p;
52 int n;
54 n = CFStringGetLength(s)*8;
55 p = malloc(n);
56 CFStringGetCString(s, p, n, kCFStringEncodingUTF8);
57 return p;
58 }
60 CFStringRef
61 c2mac(char *p)
62 {
63 return CFStringCreateWithBytes(nil, (uchar*)p, strlen(p), kCFStringEncodingUTF8, false);
64 }
66 Rectangle
67 mac2r(CGRect r, int size, int unit)
68 {
69 Rectangle rr;
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 }
78 void
79 meminvert(Memimage *m)
80 {
81 uchar *p, *ep;
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 }
89 void
90 loadfonts(void)
91 {
92 int i, n;
93 CTFontCollectionRef allc;
94 CFArrayRef array;
95 CFStringRef s;
96 CTFontDescriptorRef f;
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++;
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 };
125 static void
126 fontheight(XFont *f, int size, int *height, int *ascent)
128 int i;
129 CFStringRef s;
130 CGRect bbox;
131 CTFontRef font;
132 CTFontDescriptorRef desc;
133 CGContextRef ctxt;
134 CGColorSpaceRef color;
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;
146 color = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray);
147 ctxt = CGBitmapContextCreate(nil, 1, 1, 8, 1, color, kCGImageAlphaNone);
148 CGColorSpaceRelease(color);
149 CGContextSetTextPosition(ctxt, 0, 0);
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;
160 str = c2mac(lines[i]);
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);
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);
177 // fprint(2, "%s: %g %g %g %g\n", lines[i], r.origin.x, r.origin.y, r.size.width, r.size.height);
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;
191 bbox.size.width -= bbox.origin.x;
192 bbox.size.height -= bbox.origin.y;
194 *height = bbox.size.height + 0.999999;
195 *ascent = *height - (-bbox.origin.y + 0.999999);
197 CGContextRelease(ctxt);
198 CFRelease(font);
201 void
202 load(XFont *f)
204 int i;
206 if(f->loaded)
207 return;
208 f->loaded = 1;
210 // compute height and ascent for each size on demand
211 f->loadheight = fontheight;
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++;
223 Memsubfont*
224 mksubfont(XFont *f, char *name, int lo, int hi, int size, int antialias)
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;
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;
250 bbox = CTFontGetBoundingBox(font);
251 x = (int)(bbox.size.width*2 + 0.99999999);
253 fontheight(f, size, &height, &ascent);
254 y = height;
255 y0 = height - ascent;
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;
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;
276 fc0 = fc;
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;
291 CGContextSetAllowsAntialiasing(ctxt, antialias);
292 CGContextSetTextPosition(ctxt, 0, 0); // XXX
293 #if OSX_VERSION >= 101400
294 CGContextSetAllowsFontSmoothing(ctxt, false);
295 #endif
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 };
309 sprint(buf, "%C", (Rune)mapUnicode(name, i));
310 str = c2mac(buf);
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);
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);
328 fc->x = x;
329 fc->top = 0;
330 fc->bottom = Dy(m->r);
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;
341 continue;
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;
350 fc->x = x;
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);
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;
375 return sf;