]> code.delx.au - gnu-emacs/blob - src/nsfont.m
(main): Sort scores before trimming them,
[gnu-emacs] / src / nsfont.m
1 /* Font back-end driver for the NeXT/Open/GNUstep and MacOSX window system.
2 See font.h
3 Copyright (C) 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
4
5 This file is part of GNU Emacs.
6
7 GNU Emacs is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 GNU Emacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
19
20 Author: Adrian Robert (arobert@cogsci.ucsd.edu)
21 */
22
23 /* This should be the first include, as it may set up #defines affecting
24 interpretation of even the system includes. */
25 #include "config.h"
26
27 #include "lisp.h"
28 #include "dispextern.h"
29 #include "composite.h"
30 #include "blockinput.h"
31 #include "charset.h"
32 #include "frame.h"
33 #include "window.h"
34 #include "fontset.h"
35 #include "nsterm.h"
36 #include "frame.h"
37 #include "character.h"
38 #include "font.h"
39
40 /* This header is not included from GNUstep's (0.16.0) AppKit.h. */
41 #ifdef NS_IMPL_GNUSTEP
42 #import <AppKit/NSFontDescriptor.h>
43 #endif
44
45 #define NSFONT_TRACE 0
46
47 extern Lisp_Object Qns;
48 extern Lisp_Object Qnormal, Qbold, Qitalic, Qcondensed, Qexpanded;
49 static Lisp_Object Qapple, Qroman, Qmedium;
50 extern Lisp_Object Qappend;
51 extern int ns_antialias_text, ns_use_qd_smoothing;
52 extern float ns_antialias_threshold;
53 extern int ns_tmp_flags;
54 extern struct nsfont_info *ns_tmp_font;
55
56 /* font glyph and metrics caching functions, implemented at end */
57 static void ns_uni_to_glyphs (struct nsfont_info *font_info,
58 unsigned char block);
59 static void ns_glyph_metrics (struct nsfont_info *font_info,
60 unsigned char block);
61
62
63 /* ==========================================================================
64
65 Utilities
66
67 ========================================================================== */
68
69
70 /* Replace spaces w/another character so emacs core font parsing routines
71 aren't thrown off. */
72 static void
73 ns_escape_name (char *name)
74 {
75 int i =0, len =strlen (name);
76 for ( ; i<len; i++)
77 if (name[i] == ' ')
78 name[i] = '_';
79 }
80
81
82 /* Reconstruct spaces in a font family name passed through emacs. */
83 static void
84 ns_unescape_name (char *name)
85 {
86 int i =0, len =strlen (name);
87 for ( ; i<len; i++)
88 if (name[i] == '_')
89 name[i] = ' ';
90 }
91
92
93 /* Extract family name from a font spec. */
94 static NSString *
95 ns_get_family (Lisp_Object font_spec)
96 {
97 Lisp_Object tem = AREF (font_spec, FONT_FAMILY_INDEX);
98 if (NILP (tem))
99 return nil;
100 else
101 {
102 char *tmp = strdup (SDATA (SYMBOL_NAME (tem)));
103 NSString *family;
104 ns_unescape_name (tmp);
105 family = [NSString stringWithUTF8String: tmp];
106 free (tmp);
107 return family;
108 }
109 }
110
111
112 /* Return 0 if attr not set, else value (which might also be 0).
113 On Leopard 0 gets returned even on descriptors where the attribute
114 was never set, so there's no way to distinguish between unspecified
115 and set to not have. Callers should assume 0 means unspecified. */
116 static float
117 ns_attribute_fvalue (NSFontDescriptor *fdesc, NSString *trait)
118 {
119 NSDictionary *tdict = [fdesc objectForKey: NSFontTraitsAttribute];
120 NSNumber *val = [tdict objectForKey: trait];
121 return val == nil ? 0.0 : [val floatValue];
122 }
123
124
125 /* Converts FONT_WEIGHT, FONT_SLANT, FONT_WIDTH, plus family and script/lang
126 to NSFont descriptor. Information under extra only needed for matching. */
127 #define STYLE_REF 100
128 static NSFontDescriptor
129 *ns_spec_to_descriptor(Lisp_Object font_spec)
130 {
131 NSFontDescriptor *fdesc;
132 NSMutableDictionary *fdAttrs = [NSMutableDictionary new];
133 NSMutableDictionary *tdict = [NSMutableDictionary new];
134 NSString *family = ns_get_family (font_spec);
135 float n;
136
137 /* add each attr in font_spec to fdAttrs.. */
138 n = min (FONT_WEIGHT_NUMERIC (font_spec), 200);
139 if (n != -1 && n != STYLE_REF)
140 [tdict setObject: [NSNumber numberWithFloat: (n - 100.0) / 100.0]
141 forKey: NSFontWeightTrait];
142 n = min (FONT_SLANT_NUMERIC (font_spec), 200);
143 if (n != -1 && n != STYLE_REF)
144 [tdict setObject: [NSNumber numberWithFloat: (n - 100.0) / 100.0]
145 forKey: NSFontSlantTrait];
146 n = min (FONT_WIDTH_NUMERIC (font_spec), 200);
147 if (n > -1 && (n > STYLE_REF + 10 || n < STYLE_REF - 10))
148 [tdict setObject: [NSNumber numberWithFloat: (n - 100.0) / 100.0]
149 forKey: NSFontWidthTrait];
150 if ([tdict count] > 0)
151 [fdAttrs setObject: tdict forKey: NSFontTraitsAttribute];
152
153 fdesc = [NSFontDescriptor fontDescriptorWithFontAttributes: fdAttrs];
154 if (family != nil)
155 fdesc = [fdesc fontDescriptorWithFamily: family];
156 return fdesc;
157 }
158
159
160 /* Converts NSFont descriptor to FONT_WEIGHT, FONT_SLANT, FONT_WIDTH, etc.. */
161 static Lisp_Object
162 ns_descriptor_to_entity (NSFontDescriptor *desc, Lisp_Object extra, char *style)
163 {
164 Lisp_Object font_entity = font_make_entity ();
165 /* NSString *psName = [desc postscriptName]; */
166 NSString *family = [desc objectForKey: NSFontFamilyAttribute];
167 unsigned int traits = [desc symbolicTraits];
168 char *escapedFamily;
169
170 /* Shouldn't happen, but on Tiger fallback desc gets name but no family. */
171 if (family == nil)
172 family = [desc objectForKey: NSFontNameAttribute];
173 if (family == nil)
174 family = [[NSFont userFixedPitchFontOfSize: 0] familyName];
175
176 escapedFamily = strdup ([family UTF8String]);
177 ns_escape_name (escapedFamily);
178
179 ASET (font_entity, FONT_TYPE_INDEX, Qns);
180 ASET (font_entity, FONT_FOUNDRY_INDEX, Qapple);
181 ASET (font_entity, FONT_FAMILY_INDEX, intern (escapedFamily));
182 ASET (font_entity, FONT_ADSTYLE_INDEX, style ? intern (style) : Qnil);
183 ASET (font_entity, FONT_REGISTRY_INDEX, Qiso10646_1);
184
185 FONT_SET_STYLE (font_entity, FONT_WEIGHT_INDEX,
186 traits & NSFontBoldTrait ? Qbold : Qmedium);
187 /* FONT_SET_STYLE (font_entity, FONT_WEIGHT_INDEX,
188 make_number (100 + 100
189 * ns_attribute_fvalue (desc, NSFontWeightTrait)));*/
190 FONT_SET_STYLE (font_entity, FONT_SLANT_INDEX,
191 traits & NSFontItalicTrait ? Qitalic : Qnormal);
192 /* FONT_SET_STYLE (font_entity, FONT_SLANT_INDEX,
193 make_number (100 + 100
194 * ns_attribute_fvalue (desc, NSFontSlantTrait)));*/
195 FONT_SET_STYLE (font_entity, FONT_WIDTH_INDEX,
196 traits & NSFontCondensedTrait ? Qcondensed :
197 traits & NSFontExpandedTrait ? Qexpanded : Qnormal);
198 /* FONT_SET_STYLE (font_entity, FONT_WIDTH_INDEX,
199 make_number (100 + 100
200 * ns_attribute_fvalue (desc, NSFontWidthTrait)));*/
201
202 ASET (font_entity, FONT_SIZE_INDEX, make_number (0));
203 ASET (font_entity, FONT_AVGWIDTH_INDEX, make_number (0));
204 ASET (font_entity, FONT_SPACING_INDEX,
205 make_number([desc symbolicTraits] & NSFontMonoSpaceTrait
206 ? FONT_SPACING_MONO : FONT_SPACING_PROPORTIONAL));
207
208 ASET (font_entity, FONT_EXTRA_INDEX, extra);
209 ASET (font_entity, FONT_OBJLIST_INDEX, Qnil);
210
211 if (NSFONT_TRACE)
212 {
213 fprintf (stderr, "created font_entity:\n ");
214 debug_print (font_entity);
215 }
216
217 free (escapedFamily);
218 return font_entity;
219 }
220
221
222 /* Default font entity. */
223 static Lisp_Object
224 ns_fallback_entity ()
225 {
226 return ns_descriptor_to_entity ([[NSFont userFixedPitchFontOfSize: 0]
227 fontDescriptor], Qnil, NULL);
228 }
229
230
231 /* Utility: get width of a char c in screen font sfont */
232 static float
233 ns_char_width (NSFont *sfont, int c)
234 {
235 float w;
236 NSString *cstr = [NSString stringWithFormat: @"%c", c];
237 #ifdef NS_IMPL_COCOA
238 NSGlyph glyph = [sfont glyphWithName: cstr];
239 if (glyph)
240 {
241 float w = [sfont advancementForGlyph: glyph].width;
242 if (w >= 1.5)
243 return w;
244 }
245 #endif
246 w = [sfont widthOfString: cstr];
247 return max (w, 2.0);
248 }
249
250
251 /* Return whether set1 covers set2 to a reasonable extent given by pct.
252 We check, out of each 16 unicode char range containing chars in set2,
253 whether at least one character is present in set1.
254 This must be true for pct of the pairs to consider it covering. */
255 static BOOL
256 ns_charset_covers(NSCharacterSet *set1, NSCharacterSet *set2, float pct)
257 {
258 const unsigned short *bytes1 = [[set1 bitmapRepresentation] bytes];
259 const unsigned short *bytes2 = [[set2 bitmapRepresentation] bytes];
260 int i, off = 0, tot = 0;
261
262 for (i=0; i<4096; i++, bytes1++, bytes2++)
263 if (*bytes2)
264 {
265 tot++;
266 if (*bytes1 == 0) // *bytes1 & *bytes2 != *bytes2
267 off++;
268 }
269 //fprintf(stderr, "off = %d\ttot = %d\n", off,tot);
270 return (float)off / tot < 1.0 - pct;
271 }
272
273
274 /* Convert :lang property to a script. Use of :lang property by font backend
275 seems to be limited for now (2009/05) to ja, zh, and ko. */
276 static NSString
277 *ns_lang_to_script (Lisp_Object lang)
278 {
279 if (!strcmp (SDATA (SYMBOL_NAME (lang)), "ja"))
280 return @"han";
281 /* NOTE: ja given for any hanzi that's also a kanji, but Chinese fonts
282 have more characters. */
283 else if (!strcmp (SDATA (SYMBOL_NAME (lang)), "zh"))
284 return @"han";
285 else if (!strcmp (SDATA (SYMBOL_NAME (lang)), "ko"))
286 return @"hangul";
287 else
288 return @"";
289 }
290
291
292 /* Convert OTF 4-letter script code to emacs script name. (Why can't
293 everyone just use some standard unicode names for these?) */
294 static NSString
295 *ns_otf_to_script (Lisp_Object otf)
296 {
297 Lisp_Object script = assq_no_quit (XCAR (otf), Votf_script_alist);
298 return CONSP (script)
299 ? [NSString stringWithUTF8String: SDATA (SYMBOL_NAME XCDR ((script)))]
300 : @"";
301 }
302
303
304 /* Searches the :script, :lang, and :otf extra-bundle properties of the spec
305 for something that can be mapped to a unicode script. Empty string returned
306 if no script spec found.
307 TODO: Eventually registry / encoding should be checked and mapped, but for
308 now the font backend will try script/lang/otf if registry fails, so it is
309 not needed. */
310 static NSString
311 *ns_get_req_script (Lisp_Object font_spec)
312 {
313 Lisp_Object extra = AREF (font_spec, FONT_EXTRA_INDEX);
314
315 for ( ; CONSP (extra); extra = XCDR (extra))
316 {
317 Lisp_Object tmp = XCAR (extra);
318 if (CONSP (tmp))
319 {
320 Lisp_Object key = XCAR (tmp), val = XCDR (tmp);
321 if (EQ (key, QCscript) && SYMBOLP (val))
322 return [NSString stringWithUTF8String:
323 SDATA (SYMBOL_NAME (val))];
324 if (EQ (key, QClang) && SYMBOLP (val))
325 return ns_lang_to_script (val);
326 if (EQ (key, QCotf) && CONSP (val) && SYMBOLP (XCAR (val)))
327 return ns_otf_to_script (val);
328 }
329 }
330 return @"";
331 }
332
333
334 /* This small function is static in fontset.c. If it can be made public for
335 all ports, remove this, but otherwise it doesn't seem worth the ifdefs. */
336 static void
337 accumulate_script_ranges (Lisp_Object arg, Lisp_Object range, Lisp_Object val)
338 {
339 if (EQ (XCAR (arg), val))
340 {
341 if (CONSP (range))
342 XSETCDR (arg, Fcons (Fcons (XCAR (range), XCDR (range)), XCDR (arg)));
343 else
344 XSETCDR (arg, Fcons (Fcons (range, range), XCDR (arg)));
345 }
346 }
347
348
349 /* Use the unicode range information in Vchar_script_table to convert a script
350 name into an NSCharacterSet. */
351 static NSCharacterSet
352 *ns_script_to_charset (NSString *scriptName)
353 {
354 NSMutableCharacterSet *charset = [NSMutableCharacterSet new];
355 Lisp_Object script = intern ([scriptName UTF8String]);
356 Lisp_Object script_list = XCHAR_TABLE (Vchar_script_table)->extras[0];
357
358 if (! NILP (Fmemq (script, script_list)))
359 {
360 Lisp_Object ranges, range_list;
361
362 ranges = Fcons (script, Qnil);
363 map_char_table (accumulate_script_ranges, Qnil, Vchar_script_table,
364 ranges);
365 range_list = Fnreverse (XCDR (ranges));
366 if (! NILP (range_list))
367 {
368 for (; CONSP (range_list); range_list = XCDR (range_list))
369 {
370 int start = XINT (XCAR (XCAR (range_list)));
371 int end = XINT (XCDR (XCAR (range_list)));
372 if (NSFONT_TRACE)
373 debug_print (XCAR (range_list));
374 if (end < 0x10000)
375 [charset addCharactersInRange:
376 NSMakeRange (start, end-start)];
377 }
378 }
379 }
380 return charset;
381 }
382
383
384 /* Return an array of font families containing characters for the given
385 script, for the given coverage criterion, including at least LastResort.
386 Results are cached by script for faster access.
387 If none are found, we reduce the percentage and try again, until 5%.
388 This provides a font with at least some characters if such can be found.
389 We don't use isSupersetOfSet: because (a) it doesn't work on Tiger, and
390 (b) need approximate match as fonts covering full unicode ranges are rare. */
391 static NSSet
392 *ns_get_covering_families (NSString *script, float pct)
393 {
394 static NSMutableDictionary *scriptToFamilies = nil;
395 NSMutableSet *families;
396
397 if (NSFONT_TRACE)
398 NSLog(@"Request covering families for script: '%@'", script);
399
400 if (scriptToFamilies == nil)
401 scriptToFamilies = [[NSMutableDictionary alloc] init];
402
403 if ((families = [scriptToFamilies objectForKey: script]) == nil)
404 {
405 NSFontManager *fontMgr = [NSFontManager sharedFontManager];
406 NSArray *allFamilies = [fontMgr availableFontFamilies];
407
408 if ([script length] == 0)
409 families = [NSMutableSet setWithArray: allFamilies];
410 else
411 {
412 NSCharacterSet *charset = ns_script_to_charset (script);
413 NSString *family;
414 families = [NSMutableSet setWithCapacity: 10];
415 while (1)
416 {
417 NSEnumerator *allFamiliesEnum = [allFamilies objectEnumerator];
418 while (family = [allFamiliesEnum nextObject])
419 {
420 NSCharacterSet *fset = [[fontMgr fontWithFamily: family
421 traits: 0 weight: 5 size: 12.0] coveredCharacterSet];
422 /* Some fonts on OS X, maybe many on GNUstep, return nil. */
423 if (fset == nil)
424 fset = [NSCharacterSet characterSetWithRange:
425 NSMakeRange (0, 127)];
426 if (ns_charset_covers(fset, charset, pct))
427 [families addObject: family];
428 }
429 pct -= 0.2;
430 if ([families count] > 0 || pct < 0.05)
431 break;
432 }
433 }
434 #ifdef NS_IMPL_COCOA
435 if ([families count] == 0)
436 [families addObject: @"LastResort"];
437 #endif
438 [scriptToFamilies setObject: families forKey: script];
439 }
440
441 if (NSFONT_TRACE)
442 NSLog(@" returning %d families", [families count]);
443 return families;
444 }
445
446
447 /* Implementation for list() and match(). List() can return nil, match()
448 must return something. Strategy is to drop family name from attribute
449 matching set for match. */
450 static Lisp_Object
451 ns_findfonts (Lisp_Object font_spec, BOOL isMatch)
452 {
453 Lisp_Object tem, list = Qnil;
454 NSFontDescriptor *fdesc, *desc;
455 NSMutableSet *fkeys;
456 NSArray *matchingDescs;
457 NSEnumerator *dEnum;
458 NSString *family;
459 NSSet *cFamilies;
460 BOOL foundItal = NO;
461
462 if (NSFONT_TRACE)
463 {
464 fprintf (stderr, "nsfont: %s for fontspec:\n ",
465 (isMatch ? "match" : "list"));
466 debug_print (font_spec);
467 }
468
469 /* If has non-unicode registry, give up. */
470 tem = AREF (font_spec, FONT_REGISTRY_INDEX);
471 if (! NILP (tem) && !EQ (tem, Qiso10646_1) && !EQ (tem, Qunicode_bmp))
472 return isMatch ? ns_fallback_entity () : Qnil;
473
474 cFamilies = ns_get_covering_families (ns_get_req_script (font_spec), 0.90);
475
476 fdesc = ns_spec_to_descriptor (font_spec);
477 fkeys = [NSMutableSet setWithArray: [[fdesc fontAttributes] allKeys]];
478 if (isMatch)
479 [fkeys removeObject: NSFontFamilyAttribute];
480
481 matchingDescs = [fdesc matchingFontDescriptorsWithMandatoryKeys: fkeys];
482 if (NSFONT_TRACE)
483 NSLog(@"Got desc %@ and found %d matching fonts from it: ", fdesc,
484 [matchingDescs count]);
485
486 for (dEnum = [matchingDescs objectEnumerator]; desc = [dEnum nextObject]; )
487 {
488 if (![cFamilies containsObject:
489 [desc objectForKey: NSFontFamilyAttribute]])
490 continue;
491 tem = ns_descriptor_to_entity (desc,
492 AREF (font_spec, FONT_EXTRA_INDEX),
493 NULL);
494 if (isMatch)
495 return tem;
496 list = Fcons (tem, list);
497 if (fabs (ns_attribute_fvalue (desc, NSFontSlantTrait)) > 0.05)
498 foundItal = YES;
499 }
500
501 /* Add synthItal member if needed. */
502 family = [fdesc objectForKey: NSFontFamilyAttribute];
503 if (family != nil && !foundItal && XINT (Flength (list)) > 0)
504 {
505 NSFontDescriptor *sDesc = [[[NSFontDescriptor new]
506 fontDescriptorWithSymbolicTraits: NSFontItalicTrait]
507 fontDescriptorWithFamily: family];
508 list = Fcons (ns_descriptor_to_entity (sDesc,
509 AREF (font_spec, FONT_EXTRA_INDEX),
510 "synthItal"), list);
511 }
512
513 /* Return something if was a match and nothing found. */
514 if (isMatch)
515 return ns_fallback_entity ();
516
517 if (NSFONT_TRACE)
518 fprintf (stderr, " Returning %d entities.\n", XINT (Flength (list)));
519
520 return list;
521 }
522
523
524
525 /* ==========================================================================
526
527 Font driver implementation
528
529 ========================================================================== */
530
531
532 static Lisp_Object nsfont_get_cache (FRAME_PTR frame);
533 static Lisp_Object nsfont_list (Lisp_Object frame, Lisp_Object font_spec);
534 static Lisp_Object nsfont_match (Lisp_Object frame, Lisp_Object font_spec);
535 static Lisp_Object nsfont_list_family (Lisp_Object frame);
536 static Lisp_Object nsfont_open (FRAME_PTR f, Lisp_Object font_entity,
537 int pixel_size);
538 static void nsfont_close (FRAME_PTR f, struct font *font);
539 static int nsfont_has_char (Lisp_Object entity, int c);
540 static unsigned int nsfont_encode_char (struct font *font, int c);
541 static int nsfont_text_extents (struct font *font, unsigned int *code,
542 int nglyphs, struct font_metrics *metrics);
543 static int nsfont_draw (struct glyph_string *s, int from, int to, int x, int y,
544 int with_background);
545
546 struct font_driver nsfont_driver =
547 {
548 0, /* Qns */
549 1, /* case sensitive */
550 nsfont_get_cache,
551 nsfont_list,
552 nsfont_match,
553 nsfont_list_family,
554 NULL, /*free_entity */
555 nsfont_open,
556 nsfont_close,
557 NULL, /* prepare_face */
558 NULL, /* done_face */
559 nsfont_has_char,
560 nsfont_encode_char,
561 nsfont_text_extents,
562 nsfont_draw,
563 /* excluded: get_bitmap, free_bitmap, get_outline, free_outline,
564 anchor_point, otf_capability, otf_driver,
565 start_for_frame, end_for_frame, shape */
566 };
567
568
569 /* Return a cache of font-entities on FRAME. The cache must be a
570 cons whose cdr part is the actual cache area. */
571 static Lisp_Object
572 nsfont_get_cache (FRAME_PTR frame)
573 {
574 Display_Info *dpyinfo = FRAME_NS_DISPLAY_INFO (frame);
575 return (dpyinfo->name_list_element);
576 }
577
578
579 /* List fonts exactly matching with FONT_SPEC on FRAME. The value is a
580 **list** of font-entities. This and match () are sole APIs that allocate
581 font-entities. Properties to be considered (2009/05/19) are:
582 regular: foundry, family, adstyle, registry
583 extended: script, lang, otf
584 "Extended" properties are not part of the vector but get stored as
585 lisp properties under FONT_EXTRA_INDEX.
586
587 The returned entities should have type set (to 'ns), plus the following:
588 foundry, family, adstyle, registry,
589 weight, slant, width, size (0 if scalable),
590 dpi, spacing, avgwidth (0 if scalable) */
591 static Lisp_Object
592 nsfont_list (Lisp_Object frame, Lisp_Object font_spec)
593 {
594 return ns_findfonts (font_spec, NO);
595 }
596
597
598 /* Return a font entity most closely maching with FONT_SPEC on
599 FRAME. The closeness is determined by the font backend, thus
600 `face-font-selection-order' is ignored here.
601 Properties to be considered are same as for list(). */
602 static Lisp_Object
603 nsfont_match (Lisp_Object frame, Lisp_Object font_spec)
604 {
605 return ns_findfonts(font_spec, YES);
606 }
607
608
609 /* List available families. The value is a list of family names
610 (symbols). */
611 static Lisp_Object
612 nsfont_list_family (Lisp_Object frame)
613 {
614 Lisp_Object list = Qnil;
615 NSEnumerator *families =
616 [[[NSFontManager sharedFontManager] availableFontFamilies]
617 objectEnumerator];
618 NSString *family;
619 while (family = [families nextObject])
620 list = Fcons (intern ([family UTF8String]), list);
621 /* FIXME: escape the name? */
622
623 if (NSFONT_TRACE)
624 fprintf (stderr, "nsfont: list families returning %d entries\n",
625 XINT (Flength (list)));
626
627 return list;
628 }
629
630
631 /* Open a font specified by FONT_ENTITY on frame F. If the font is
632 scalable, open it with PIXEL_SIZE. */
633 static Lisp_Object
634 nsfont_open (FRAME_PTR f, Lisp_Object font_entity, int pixel_size)
635 {
636 BOOL synthItal;
637 unsigned int traits = 0;
638 struct nsfont_info *font_info;
639 struct font *font;
640 NSFontDescriptor *fontDesc = ns_spec_to_descriptor (font_entity);
641 NSFontManager *fontMgr = [NSFontManager sharedFontManager];
642 NSString *family;
643 NSFont *nsfont, *sfont;
644 Lisp_Object tem;
645 NSRect brect;
646 Lisp_Object font_object;
647 int i;
648 int fixLeopardBug;
649 static NSMutableDictionary *fontCache = nil;
650 NSNumber *cached;
651
652 /* 2008/03/08: The same font may end up being requested for different
653 entities, due to small differences in numeric values or other issues,
654 or for different copies of the same entity. Therefore we cache to
655 avoid creating multiple struct font objects (with metrics cache, etc.)
656 for the same NSFont object. */
657 if (fontCache == nil)
658 fontCache = [[NSMutableDictionary alloc] init];
659
660 if (NSFONT_TRACE)
661 {
662 fprintf (stderr, "nsfont: open size %d of fontentity:\n ", pixel_size);
663 debug_print (font_entity);
664 }
665
666 if (pixel_size <= 0)
667 {
668 /* try to get it out of frame params */
669 Lisp_Object tem = get_frame_param (f, Qfontsize);
670 pixel_size = NILP (tem) ? 0 : XFASTINT (tem);
671 }
672
673 tem = AREF (font_entity, FONT_ADSTYLE_INDEX);
674 synthItal = !NILP (tem) && !strncmp ("synthItal", SDATA (SYMBOL_NAME (tem)),
675 9);
676 family = ns_get_family (font_entity);
677 if (family == nil)
678 family = [[NSFont userFixedPitchFontOfSize: 0] familyName];
679 /* Should be > 0.23 as some font descriptors (e.g. Terminus) set to that
680 when setting family in ns_spec_to_descriptor(). */
681 if (ns_attribute_fvalue (fontDesc, NSFontWeightTrait) > 0.50)
682 traits |= NSBoldFontMask;
683 if (fabs (ns_attribute_fvalue (fontDesc, NSFontSlantTrait) > 0.05))
684 traits |= NSItalicFontMask;
685
686 /* see http://cocoadev.com/forums/comments.php?DiscussionID=74 */
687 fixLeopardBug = traits & NSBoldFontMask ? 10 : 5;
688 nsfont = [fontMgr fontWithFamily: family
689 traits: traits weight: fixLeopardBug
690 size: pixel_size];
691 /* if didn't find, try synthetic italic */
692 if (nsfont == nil && synthItal)
693 {
694 nsfont = [fontMgr fontWithFamily: family
695 traits: traits & ~NSItalicFontMask
696 weight: fixLeopardBug size: pixel_size];
697 }
698 #ifdef NS_IMPL_COCOA
699 /* LastResort not really a family */
700 if (nsfont == nil && [@"LastResort" isEqualToString: family])
701 nsfont = [NSFont fontWithName: @"LastResort" size: pixel_size];
702 #endif
703
704 if (nsfont == nil)
705 {
706 message_with_string ("*** Warning: font in family '%s' not found",
707 build_string ([family UTF8String]), 1);
708 nsfont = [NSFont userFixedPitchFontOfSize: pixel_size];
709 }
710
711 if (NSFONT_TRACE)
712 NSLog (@"%@\n", nsfont);
713
714 /* Check the cache */
715 cached = [fontCache objectForKey: nsfont];
716 if (cached != nil && !synthItal)
717 {
718 if (NSFONT_TRACE)
719 fprintf(stderr, "*** nsfont_open CACHE HIT!\n");
720 /* FIXME: Cast from (unsigned long) to Lisp_Object. */
721 XHASH (font_object) = [cached unsignedLongValue];
722 return font_object;
723 }
724 else
725 {
726 font_object = font_make_object (VECSIZE (struct nsfont_info),
727 font_entity, pixel_size);
728 if (!synthItal)
729 [fontCache setObject: [NSNumber numberWithUnsignedLong:
730 (unsigned long) XHASH (font_object)]
731 forKey: nsfont];
732 }
733
734 font_info = (struct nsfont_info *) XFONT_OBJECT (font_object);
735 font = (struct font *) font_info;
736 if (!font)
737 return Qnil; /* FIXME: other terms do, but return Qnil causes segfault */
738
739 font_info->glyphs = (unsigned short **)
740 xmalloc (0x100 * sizeof (unsigned short *));
741 font_info->metrics = (struct font_metrics **)
742 xmalloc (0x100 * sizeof (struct font_metrics *));
743 if (!font_info->glyphs || !font_info->metrics)
744 return Qnil;
745 bzero (font_info->glyphs, 0x100 * sizeof (unsigned short *));
746 bzero (font_info->metrics, 0x100 * sizeof (struct font_metrics *));
747
748 BLOCK_INPUT;
749
750 /* for metrics */
751 sfont = [nsfont screenFont];
752 if (sfont == nil)
753 sfont = nsfont;
754
755 /* non-metric backend font struct fields */
756 font = (struct font *) font_info;
757 font->pixel_size = [sfont pointSize];
758 font->driver = &nsfont_driver;
759 font->encoding_type = FONT_ENCODING_NOT_DECIDED;
760 font->encoding_charset = -1;
761 font->repertory_charset = -1;
762 font->default_ascent = 0;
763 font->vertical_centering = 0;
764 font->baseline_offset = 0;
765 font->relative_compose = 0;
766 font->font_encoder = NULL;
767
768 font->props[FONT_FORMAT_INDEX] = Qns;
769 font->props[FONT_FILE_INDEX] = Qnil;
770
771 {
772 double expand, hshrink;
773 float full_height, min_height, hd;
774 const char *fontName = [[nsfont fontName] UTF8String];
775 int len = strlen (fontName);
776
777 #ifdef NS_IMPL_GNUSTEP
778 font_info->nsfont = sfont;
779 #else
780 font_info->nsfont = nsfont;
781 #endif
782 [font_info->nsfont retain];
783
784 /* set up ns_font (defined in nsgui.h) */
785 font_info->name = (char *)xmalloc (strlen (fontName) + 1);
786 bcopy (fontName, font_info->name, strlen (fontName) + 1);
787 font_info->bold = [fontMgr traitsOfFont: nsfont] & NSBoldFontMask;
788 font_info->ital =
789 synthItal || ([fontMgr traitsOfFont: nsfont] & NSItalicFontMask);
790
791 /* Metrics etc.; some fonts return an unusually large max advance, so we
792 only use it for fonts that have wide characters. */
793 font_info->width = ([sfont numberOfGlyphs] > 2000) ?
794 [sfont maximumAdvancement].width : ns_char_width (sfont, '0');
795
796 brect = [sfont boundingRectForFont];
797 full_height = brect.size.height;
798 min_height = [sfont ascender] - [sfont descender];
799 hd = full_height - min_height;
800
801 /* standard height, similar to Carbon. Emacs.app: was 0.5 by default. */
802 expand = 0.0;
803 hshrink = 1.0;
804
805 font_info->underpos = 2; /*[sfont underlinePosition] is often clipped out */
806 font_info->underwidth = [sfont underlineThickness];
807 font_info->size = font->pixel_size;
808 font_info->voffset = lrint (hshrink * [sfont ascender] + expand * hd / 2);
809
810 /* max bounds */
811 font_info->max_bounds.ascent =
812 lrint (hshrink * [sfont ascender] + expand * hd/2);
813 font_info->max_bounds.descent =
814 -lrint (hshrink* [sfont descender] - expand*hd/2);
815 font_info->height =
816 font_info->max_bounds.ascent + font_info->max_bounds.descent;
817 font_info->max_bounds.width = lrint (font_info->width);
818 font_info->max_bounds.lbearing = lrint (brect.origin.x);
819 font_info->max_bounds.rbearing =
820 lrint (brect.size.width - font_info->width);
821
822 #ifdef NS_IMPL_COCOA
823 /* set up synthItal and the CG font */
824 font_info->synthItal = synthItal;
825 {
826 ATSFontRef atsFont = ATSFontFindFromPostScriptName
827 ((CFStringRef)[nsfont fontName], kATSOptionFlagsDefault);
828
829 if (atsFont == kATSFontRefUnspecified)
830 {
831 /* see if we can get it by dropping italic (then synthesizing) */
832 atsFont = ATSFontFindFromPostScriptName ((CFStringRef)
833 [[fontMgr convertFont: nsfont toNotHaveTrait: NSItalicFontMask]
834 fontName], kATSOptionFlagsDefault);
835 if (atsFont != kATSFontRefUnspecified)
836 font_info->synthItal = YES;
837 else
838 {
839 /* last resort fallback */
840 atsFont = ATSFontFindFromPostScriptName
841 ((CFStringRef)@"Monaco", kATSOptionFlagsDefault);
842 }
843 }
844 font_info->cgfont = CGFontCreateWithPlatformFont ((void*)&atsFont);
845 }
846 #endif
847
848 /* set up metrics portion of font struct */
849 font->ascent = [sfont ascender];
850 font->descent = -[sfont descender];
851 font->min_width = [sfont widthOfString: @"|"]; /* FIXME */
852 font->space_width = lrint (ns_char_width (sfont, ' '));
853 font->average_width = lrint (font_info->width);
854 font->max_width = lrint (font_info->max_bounds.width);
855 font->height = lrint (font_info->height);
856 font->underline_position = lrint (font_info->underpos);
857 font->underline_thickness = lrint (font_info->underwidth);
858
859 font->props[FONT_NAME_INDEX] = Ffont_xlfd_name (font_object, Qnil);
860 font->props[FONT_FULLNAME_INDEX] =
861 make_unibyte_string (font_info->name, strlen (font_info->name));
862 }
863 UNBLOCK_INPUT;
864
865 return font_object;
866 }
867
868
869 /* Close FONT on frame F. */
870 static void
871 nsfont_close (FRAME_PTR f, struct font *font)
872 {
873 struct nsfont_info *font_info = (struct nsfont_info *)font;
874 int i;
875
876 /* FIXME: this occurs apparently due to same failure to detect same font
877 that causes need for cache in nsfont_open () */
878 if (!font_info)
879 return;
880
881 for (i =0; i<0x100; i++)
882 {
883 xfree (font_info->glyphs[i]);
884 xfree (font_info->metrics[i]);
885 }
886 [font_info->nsfont release];
887 #ifdef NS_IMPL_COCOA
888 CGFontRelease (font_info->cgfont);
889 #endif
890 xfree (font_info->name);
891 xfree (font_info);
892 }
893
894
895 /* If FONT_ENTITY has a glyph for character C (Unicode code point),
896 return 1. If not, return 0. If a font must be opened to check
897 it, return -1. */
898 static int
899 nsfont_has_char (Lisp_Object entity, int c)
900 {
901 return -1;
902 }
903
904
905 /* Return a glyph code of FONT for character C (Unicode code point).
906 If FONT doesn't have such a glyph, return FONT_INVALID_CODE. */
907 static unsigned int
908 nsfont_encode_char (struct font *font, int c)
909 {
910 struct nsfont_info *font_info = (struct nsfont_info *)font;
911 unsigned char high = (c & 0xff00) >> 8, low = c & 0x00ff;
912 unsigned short g;
913
914 if (c > 0xFFFF)
915 return FONT_INVALID_CODE;
916
917 /* did we already cache this block? */
918 if (!font_info->glyphs[high])
919 ns_uni_to_glyphs (font_info, high);
920
921 g = font_info->glyphs[high][low];
922 return g == 0xFFFF ? FONT_INVALID_CODE : g;
923 }
924
925
926 /* Perform the size computation of glyphs of FONT and fill in members
927 of METRICS. The glyphs are specified by their glyph codes in
928 CODE (length NGLYPHS). */
929 static int
930 nsfont_text_extents (struct font *font, unsigned int *code, int nglyphs,
931 struct font_metrics *metrics)
932 {
933 struct nsfont_info *font_info = (struct nsfont_info *)font;
934 struct font_metrics *pcm;
935 unsigned char high, low;
936 int totalWidth = 0;
937 int i;
938
939 bzero (metrics, sizeof (struct font_metrics));
940
941 for (i =0; i<nglyphs; i++)
942 {
943 /* get metrics for this glyph, filling cache if need be */
944 /* TODO: get metrics for whole string from an NSLayoutManager
945 (if not too slow) */
946 high = (code[i] & 0xFF00) >> 8;
947 low = code[i] & 0x00FF;
948 if (!font_info->metrics[high])
949 ns_glyph_metrics (font_info, high);
950 pcm = &(font_info->metrics[high][low]);
951
952 if (metrics->lbearing > totalWidth + pcm->lbearing)
953 metrics->lbearing = totalWidth + pcm->lbearing;
954 if (metrics->rbearing < totalWidth + pcm->rbearing)
955 metrics->rbearing = totalWidth + pcm->rbearing;
956 if (metrics->ascent < pcm->ascent)
957 metrics->ascent = pcm->ascent;
958 if (metrics->descent < pcm->descent)
959 metrics->descent = pcm->descent;
960
961 totalWidth += pcm->width;
962 }
963
964 metrics->width = totalWidth;
965
966 return totalWidth; /* not specified in doc, but xfont.c does it */
967 }
968
969
970 /* Draw glyphs between FROM and TO of S->char2b at (X Y) pixel
971 position of frame F with S->FACE and S->GC. If WITH_BACKGROUND
972 is nonzero, fill the background in advance. It is assured that
973 WITH_BACKGROUND is zero when (FROM > 0 || TO < S->nchars). */
974 static int
975 nsfont_draw (struct glyph_string *s, int from, int to, int x, int y,
976 int with_background)
977 /* NOTE: focus and clip must be set
978 also, currently assumed (true in nsterm.m call) from ==0, to ==nchars */
979 {
980 static char cbuf[1024];
981 char *c = cbuf;
982 #ifdef NS_IMPL_GNUSTEP
983 static float advances[1024];
984 float *adv = advances;
985 #else
986 static CGSize advances[1024];
987 CGSize *adv = advances;
988 #endif
989 struct face *face;
990 NSRect r;
991 struct nsfont_info *font = ns_tmp_font;
992 NSColor *col, *bgCol;
993 unsigned short *t = s->char2b;
994 int i, len;
995 char isComposite = s->first_glyph->type == COMPOSITE_GLYPH;
996 int end = isComposite ? s->cmp_to : s->nchars;
997
998 /* Select face based on input flags */
999 switch (ns_tmp_flags)
1000 {
1001 case NS_DUMPGLYPH_CURSOR:
1002 face = s->face;
1003 break;
1004 case NS_DUMPGLYPH_MOUSEFACE:
1005 face = FACE_FROM_ID (s->f,
1006 FRAME_NS_DISPLAY_INFO (s->f)->mouse_face_face_id);
1007 if (!face)
1008 face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
1009 break;
1010 default:
1011 face = s->face;
1012 }
1013
1014 r.origin.x = s->x;
1015 if (s->face->box != FACE_NO_BOX && s->first_glyph->left_box_line_p)
1016 r.origin.x += abs (s->face->box_line_width);
1017
1018 r.origin.y = s->y;
1019 r.size.height = FONT_HEIGHT (font);
1020
1021 /* Convert UTF-16 (?) to UTF-8 and determine advances. Note if we just ask
1022 NS to render the string, it will come out differently from the individual
1023 character widths added up because of layout processing. */
1024 {
1025 XCharStruct *cs;
1026 int cwidth, twidth = 0;
1027 int hi, lo;
1028 /* FIXME: composition: no vertical displacement is considered. */
1029 t += s->cmp_from; /* advance into composition */
1030 for (i = s->cmp_from; i < end; i++, t++)
1031 {
1032 hi = (*t & 0xFF00) >> 8;
1033 lo = *t & 0x00FF;
1034 if (isComposite)
1035 {
1036 if (!s->first_glyph->u.cmp.automatic)
1037 cwidth = s->cmp->offsets[i * 2] /* (H offset) */ - twidth;
1038 else
1039 {
1040 Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
1041 Lisp_Object glyph = LGSTRING_GLYPH (gstring, i);
1042 if (NILP (LGLYPH_ADJUSTMENT (glyph)))
1043 cwidth = LGLYPH_WIDTH (glyph);
1044 else
1045 {
1046 cwidth = LGLYPH_WADJUST (glyph);
1047 #ifdef NS_IMPL_GNUSTEP
1048 *(adv-1) += LGLYPH_XOFF (glyph);
1049 #else
1050 (*(adv-1)).width += LGLYPH_XOFF (glyph);
1051 #endif
1052 }
1053 }
1054 }
1055 else
1056 {
1057 if (!font->metrics[hi]) /* FIXME: why/how can we need this now? */
1058 ns_glyph_metrics (font, hi);
1059 cwidth = font->metrics[hi][lo].width;
1060 }
1061 twidth += cwidth;
1062 #ifdef NS_IMPL_GNUSTEP
1063 *adv++ = cwidth;
1064 CHAR_STRING_ADVANCE (*t, c); /* this converts the char to UTF-8 */
1065 #else
1066 (*adv++).width = cwidth;
1067 #endif
1068 }
1069 len = adv - advances;
1070 r.size.width = twidth;
1071 *c = 0;
1072 }
1073
1074 /* fill background if requested */
1075 if (with_background && !isComposite)
1076 {
1077 NSRect br = r;
1078 int fibw = FRAME_INTERNAL_BORDER_WIDTH (s->f);
1079 int mbox_line_width = max (s->face->box_line_width, 0);
1080
1081 if (s->row->full_width_p)
1082 {
1083 if (br.origin.x <= fibw + 1 + mbox_line_width)
1084 {
1085 br.size.width += br.origin.x - mbox_line_width;
1086 br.origin.x = mbox_line_width;
1087 }
1088 if (FRAME_PIXEL_WIDTH (s->f) - (br.origin.x + br.size.width)
1089 <= fibw+1)
1090 br.size.width += fibw;
1091 }
1092 if (s->face->box == FACE_NO_BOX)
1093 {
1094 /* expand unboxed top row over internal border */
1095 if (br.origin.y <= fibw + 1 + mbox_line_width)
1096 {
1097 br.size.height += br.origin.y;
1098 br.origin.y = 0;
1099 }
1100 }
1101 else
1102 {
1103 int correction = abs (s->face->box_line_width)+1;
1104 br.origin.y += correction;
1105 br.size.height -= 2*correction;
1106 br.origin.x += correction;
1107 br.size.width -= 2*correction;
1108 }
1109
1110 if (!s->face->stipple)
1111 [(NS_FACE_BACKGROUND (face) != 0
1112 ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
1113 : FRAME_BACKGROUND_COLOR (s->f)) set];
1114 else
1115 {
1116 struct ns_display_info *dpyinfo = FRAME_NS_DISPLAY_INFO (s->f);
1117 [[dpyinfo->bitmaps[face->stipple-1].img stippleMask] set];
1118 }
1119 NSRectFill (br);
1120 }
1121
1122
1123 /* set up for character rendering */
1124 r.origin.y += font->voffset + (s->height - font->height)/2;
1125
1126 col = (NS_FACE_FOREGROUND (face) != 0
1127 ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f)
1128 : FRAME_FOREGROUND_COLOR (s->f));
1129 /* FIXME: find another way to pass this */
1130 bgCol = (ns_tmp_flags != NS_DUMPGLYPH_FOREGROUND ? nil
1131 : (NS_FACE_BACKGROUND (face) != 0
1132 ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
1133 : FRAME_BACKGROUND_COLOR (s->f)));
1134
1135 /* render under GNUstep using DPS */
1136 #ifdef NS_IMPL_GNUSTEP
1137 {
1138 NSGraphicsContext *context = GSCurrentContext ();
1139
1140 DPSgsave (context);
1141 [font->nsfont set];
1142
1143 /* do erase if "foreground" mode */
1144 if (bgCol != nil)
1145 {
1146 [bgCol set];
1147 DPSmoveto (context, r.origin.x, r.origin.y);
1148 /*[context GSSetTextDrawingMode: GSTextFillStroke]; /// not implemented yet */
1149 DPSxshow (context, cbuf, advances, len);
1150 DPSstroke (context);
1151 [col set];
1152 /*[context GSSetTextDrawingMode: GSTextFill]; /// not implemented yet */
1153 }
1154
1155 /* do underline */
1156 if (face->underline_p)
1157 {
1158 if (face->underline_color != 0)
1159 [ns_lookup_indexed_color (face->underline_color, s->f) set];
1160 else
1161 [col set];
1162 DPSmoveto (context, r.origin.x, r.origin.y + font->underpos);
1163 DPSlineto (context, r.origin.x+r.size.width, r.origin.y+font->underpos);
1164 if (face->underline_color != 0)
1165 [col set];
1166 }
1167 else
1168 [col set];
1169
1170 /* draw with DPSxshow () */
1171 DPSmoveto (context, r.origin.x, r.origin.y);
1172 DPSxshow (context, cbuf, advances, len);
1173 DPSstroke (context);
1174
1175 DPSgrestore (context);
1176 return to-from;
1177 }
1178
1179 #else /* NS_IMPL_COCOA */
1180 {
1181 CGContextRef gcontext =
1182 [[NSGraphicsContext currentContext] graphicsPort];
1183 static CGAffineTransform fliptf;
1184 static BOOL firstTime = YES;
1185
1186 if (firstTime)
1187 {
1188 firstTime = NO;
1189 fliptf = CGAffineTransformMakeScale (1.0, -1.0);
1190 }
1191
1192 CGContextSaveGState (gcontext);
1193
1194 fliptf.c = font->synthItal ? Fix2X (kATSItalicQDSkew) : 0.0;
1195
1196 CGContextSetFont (gcontext, font->cgfont);
1197 CGContextSetFontSize (gcontext, font->size);
1198 if (ns_antialias_text == Qnil || font->size <= ns_antialias_threshold)
1199 CGContextSetShouldAntialias (gcontext, 0);
1200 else
1201 CGContextSetShouldAntialias (gcontext, 1);
1202 if (EQ (ns_use_qd_smoothing, Qt))
1203 CGContextSetFontRenderingMode (gcontext, 2); /* 0 is Cocoa, 2 is QD */
1204
1205 CGContextSetTextMatrix (gcontext, fliptf);
1206
1207 if (bgCol != nil)
1208 {
1209 /* foreground drawing; erase first to avoid overstrike */
1210 [bgCol set];
1211 CGContextSetTextDrawingMode (gcontext, kCGTextFillStroke);
1212 CGContextSetTextPosition (gcontext, r.origin.x, r.origin.y);
1213 CGContextShowGlyphsWithAdvances (gcontext, s->char2b, advances, len);
1214 CGContextSetTextDrawingMode (gcontext, kCGTextFill);
1215 }
1216
1217 if (face->underline_p)
1218 {
1219 if (face->underline_color != 0)
1220 [ns_lookup_indexed_color (face->underline_color, s->f) set];
1221 else
1222 [col set];
1223 CGContextBeginPath (gcontext);
1224 CGContextMoveToPoint (gcontext,
1225 r.origin.x, r.origin.y + font->underpos);
1226 CGContextAddLineToPoint (gcontext, r.origin.x + r.size.width,
1227 r.origin.y + font->underpos);
1228 CGContextStrokePath (gcontext);
1229 if (face->underline_color != 0)
1230 [col set];
1231 }
1232 else
1233 [col set];
1234
1235 CGContextSetTextPosition (gcontext, r.origin.x, r.origin.y);
1236 CGContextShowGlyphsWithAdvances (gcontext, s->char2b + s->cmp_from,
1237 advances, len);
1238
1239 if (face->overstrike)
1240 {
1241 CGContextSetTextPosition (gcontext, r.origin.x+0.5, r.origin.y);
1242 CGContextShowGlyphsWithAdvances (gcontext, s->char2b + s->cmp_from,
1243 advances, len);
1244 }
1245
1246 CGContextRestoreGState (gcontext);
1247 return;
1248 }
1249 #endif /* NS_IMPL_COCOA */
1250
1251 }
1252
1253
1254
1255 /* ==========================================================================
1256
1257 Font glyph and metrics caching functions
1258
1259 ========================================================================== */
1260
1261 /* Find and cache corresponding glyph codes for unicode values in given
1262 hi-byte block of 256. */
1263 static void
1264 ns_uni_to_glyphs (struct nsfont_info *font_info, unsigned char block)
1265 {
1266 #ifdef NS_IMPL_COCOA
1267 static EmacsGlyphStorage *glyphStorage;
1268 static char firstTime = 1;
1269 #endif
1270 unichar *unichars = xmalloc (0x101 * sizeof (unichar));
1271 unsigned int i, g, idx;
1272 unsigned short *glyphs;
1273
1274 if (NSFONT_TRACE)
1275 fprintf (stderr, "%p\tFinding glyphs for glyphs in block %d\n",
1276 font_info, block);
1277
1278 BLOCK_INPUT;
1279
1280 #ifdef NS_IMPL_COCOA
1281 if (firstTime)
1282 {
1283 firstTime = 0;
1284 glyphStorage = [[EmacsGlyphStorage alloc] initWithCapacity: 0x100];
1285 }
1286 #endif
1287
1288 font_info->glyphs[block] = xmalloc (0x100 * sizeof (unsigned short));
1289 if (!unichars || !(font_info->glyphs[block]))
1290 abort ();
1291
1292 /* create a string containing all unicode characters in this block */
1293 for (idx = block<<8, i =0; i<0x100; idx++, i++)
1294 if (idx < 0xD800 || idx > 0xDFFF)
1295 unichars[i] = idx;
1296 else
1297 unichars[i] = 0xFEFF;
1298 unichars[0x100] = 0;
1299
1300 {
1301 #ifdef NS_IMPL_COCOA
1302 NSString *allChars = [[NSString alloc]
1303 initWithCharactersNoCopy: unichars
1304 length: 0x100
1305 freeWhenDone: NO];
1306 NSGlyphGenerator *glyphGenerator = [NSGlyphGenerator sharedGlyphGenerator];
1307 /*NSCharacterSet *coveredChars = [nsfont coveredCharacterSet]; */
1308 unsigned int numGlyphs = [font_info->nsfont numberOfGlyphs];
1309 unsigned int gInd =0, cInd =0;
1310
1311 [glyphStorage setString: allChars font: font_info->nsfont];
1312 [glyphGenerator generateGlyphsForGlyphStorage: glyphStorage
1313 desiredNumberOfCharacters: glyphStorage->maxChar
1314 glyphIndex: &gInd characterIndex: &cInd];
1315 #endif
1316 glyphs = font_info->glyphs[block];
1317 for (i =0; i<0x100; i++, glyphs++)
1318 {
1319 #ifdef NS_IMPL_GNUSTEP
1320 g = unichars[i];
1321 #else
1322 g = glyphStorage->cglyphs[i];
1323 /* TODO: is this a good check? maybe need to use coveredChars.. */
1324 if (g > numGlyphs)
1325 g = 0xFFFF; /* hopefully unused... */
1326 #endif
1327 *glyphs = g;
1328 }
1329
1330 #ifdef NS_IMPL_COCOA
1331 [allChars release];
1332 #endif
1333 }
1334
1335 UNBLOCK_INPUT;
1336 xfree (unichars);
1337 }
1338
1339
1340 /* Determine and cache metrics for corresponding glyph codes in given
1341 hi-byte block of 256. */
1342 static void
1343 ns_glyph_metrics (struct nsfont_info *font_info, unsigned char block)
1344 {
1345 unsigned int i, g;
1346 unsigned int numGlyphs = [font_info->nsfont numberOfGlyphs];
1347 NSFont *sfont;
1348 struct font_metrics *metrics;
1349
1350 if (NSFONT_TRACE)
1351 fprintf (stderr, "%p\tComputing metrics for glyphs in block %d\n",
1352 font_info, block);
1353
1354 #ifdef NS_IMPL_GNUSTEP
1355 /* not implemented yet (as of startup 0.18), so punt */
1356 if (numGlyphs == 0)
1357 numGlyphs = 0x10000;
1358 #endif
1359
1360 BLOCK_INPUT;
1361 sfont = [font_info->nsfont screenFont];
1362
1363 font_info->metrics[block] = xmalloc (0x100 * sizeof (struct font_metrics));
1364 bzero (font_info->metrics[block], 0x100 * sizeof (struct font_metrics));
1365 if (!(font_info->metrics[block]))
1366 abort ();
1367
1368 metrics = font_info->metrics[block];
1369 for (g = block<<8, i =0; i<0x100 && g < numGlyphs; g++, i++, metrics++)
1370 {
1371 float w, lb, rb;
1372 NSRect r = [sfont boundingRectForGlyph: g];
1373
1374 #ifdef NS_IMPL_GNUSTEP
1375 {
1376 /* lord help us */
1377 NSString *s = [NSString stringWithFormat: @"%c", g];
1378 w = [sfont widthOfString: s];
1379 }
1380 #else
1381 w = [sfont advancementForGlyph: g].width;
1382 #endif
1383 w = max (w, 2.0);
1384 metrics->width = lrint (w);
1385
1386 lb = r.origin.x;
1387 rb = r.size.width - w;
1388 if (lb < 0)
1389 metrics->lbearing = round (lb);
1390 if (font_info->ital)
1391 rb += 0.22 * font_info->height;
1392 metrics->rbearing = lrint (w + rb);
1393
1394 metrics->descent = r.origin.y < 0 ? -r.origin.y : 0;
1395 /*lrint (hshrink * [sfont ascender] + expand * hd/2); */
1396 metrics->ascent = r.size.height - metrics->descent;
1397 /*-lrint (hshrink* [sfont descender] - expand * hd/2); */
1398 }
1399 UNBLOCK_INPUT;
1400 }
1401
1402
1403 #ifdef NS_IMPL_COCOA
1404 /* helper for font glyph setup */
1405 @implementation EmacsGlyphStorage
1406
1407 - init
1408 {
1409 return [self initWithCapacity: 1024];
1410 }
1411
1412 - initWithCapacity: (unsigned long) c
1413 {
1414 self = [super init];
1415 maxChar = 0;
1416 maxGlyph = 0;
1417 dict = [NSMutableDictionary new];
1418 cglyphs = (CGGlyph *)xmalloc (c * sizeof (CGGlyph));
1419 return self;
1420 }
1421
1422 - (void) dealloc
1423 {
1424 if (attrStr != nil)
1425 [attrStr release];
1426 [dict release];
1427 xfree (cglyphs);
1428 [super dealloc];
1429 }
1430
1431 - (void) setString: (NSString *)str font: (NSFont *)font
1432 {
1433 [dict setObject: font forKey: NSFontAttributeName];
1434 attrStr = [[NSAttributedString alloc] initWithString: str attributes: dict];
1435 maxChar = [str length];
1436 maxGlyph = 0;
1437 }
1438
1439 /* NSGlyphStorage protocol */
1440 - (unsigned int)layoutOptions
1441 {
1442 return 0;
1443 }
1444
1445 - (NSAttributedString *)attributedString
1446 {
1447 return attrStr;
1448 }
1449
1450 - (void)insertGlyphs: (const NSGlyph *)glyphs length: (unsigned int)length
1451 forStartingGlyphAtIndex: (unsigned int)glyphIndex
1452 characterIndex: (unsigned int)charIndex
1453 {
1454 len = glyphIndex+length;
1455 for (i =glyphIndex; i<len; i++)
1456 cglyphs[i] = glyphs[i-glyphIndex];
1457 if (len > maxGlyph)
1458 maxGlyph = len;
1459 }
1460
1461 - (void)setIntAttribute: (int)attributeTag value: (int)val
1462 forGlyphAtIndex: (unsigned)glyphIndex
1463 {
1464 return;
1465 }
1466
1467 @end
1468 #endif /* NS_IMPL_COCOA */
1469
1470
1471 /* Debugging */
1472 void
1473 ns_dump_glyphstring (struct glyph_string *s)
1474 {
1475 int i;
1476
1477 fprintf (stderr, "Glyph string len = %d at (%d, %d) overhang (%d, %d),"
1478 "overlap = %d, bg_filled = %d:",
1479 s->nchars, s->x, s->y, s->left_overhang, s->right_overhang,
1480 s->row->overlapping_p, s->background_filled_p);
1481 for (i =0; i<s->nchars; i++)
1482 fprintf (stderr, "%c", s->first_glyph[i].u.ch);
1483 fprintf (stderr, "\n");
1484 }
1485
1486
1487 void
1488 syms_of_nsfont ()
1489 {
1490 nsfont_driver.type = Qns;
1491 register_font_driver (&nsfont_driver, NULL);
1492 DEFSYM (Qapple, "apple");
1493 DEFSYM (Qroman, "roman");
1494 DEFSYM (Qmedium, "medium");
1495 }
1496
1497 // arch-tag: d6c3c6f0-62de-4978-8b1e-b7966fe02cae