Fix last changelog entry.
[emacs.git] / src / nsfont.m
blobdde21ccd019e61ddf0b36ffba6bc31f673388712
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.
5 This file is part of GNU Emacs.
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.
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.
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/>.
20 Author: Adrian Robert (arobert@cogsci.ucsd.edu)
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"
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"
40 #define NSFONT_TRACE 0
42 extern Lisp_Object Qns;
43 extern Lisp_Object Qnormal, Qbold, Qitalic, Qcondensed, Qexpanded;
44 static Lisp_Object Qapple, Qroman, Qmedium;
45 extern Lisp_Object Qappend;
46 extern int ns_antialias_text, ns_use_qd_smoothing;
47 extern float ns_antialias_threshold;
48 extern int ns_tmp_flags;
49 extern struct nsfont_info *ns_tmp_font;
51 /* font glyph and metrics caching functions, implemented at end */
52 static void ns_uni_to_glyphs (struct nsfont_info *font_info,
53                               unsigned char block);
54 static void ns_glyph_metrics (struct nsfont_info *font_info,
55                               unsigned char block);
58 /* ==========================================================================
60     Utilities
62    ========================================================================== */
65 /* Replace spaces w/another character so emacs core font parsing routines
66    aren't thrown off. */
67 static void
68 ns_escape_name (char *name)
70   int i =0, len =strlen (name);
71   for ( ; i<len; i++)
72     if (name[i] == ' ')
73       name[i] = '_';
77 /* Reconstruct spaces in a font family name passed through emacs. */
78 static void
79 ns_unescape_name (char *name)
81   int i =0, len =strlen (name);
82   for ( ; i<len; i++)
83     if (name[i] == '_')
84       name[i] = ' ';
88 /* Extract family name from a font spec. */
89 static NSString *
90 ns_get_family (Lisp_Object font_spec)
92   Lisp_Object tem = AREF (font_spec, FONT_FAMILY_INDEX);
93   if (NILP (tem))
94       return nil;
95   else
96     {
97       char *tmp = strdup (SDATA (SYMBOL_NAME (tem)));
98       NSString *family;
99       ns_unescape_name (tmp);
100       /* For names hard-coded into emacs, like 'helvetica' for splash. */
101       tmp[0] = toupper (tmp[0]);
102       family = [NSString stringWithUTF8String: tmp];
103       free (tmp);
104       return family;
105     }
109 /* Return NSNumber or nil if attr is not set. */
110 static NSNumber
111 *ns_attribute_value (NSFontDescriptor *fdesc, NSString *trait)
113     NSDictionary *tdict = [fdesc objectForKey: NSFontTraitsAttribute];
114     NSNumber *val = [tdict objectForKey: trait];
115     return val;
119 /* Return 0 if attr not set, else value (which might also be 0). */
120 static float
121 ns_attribute_fvalue (NSFontDescriptor *fdesc, NSString *trait)
123     NSNumber *val = ns_attribute_value (fdesc, trait);
124     return val == nil ? 0.0 : [val floatValue];
128 /* Return whether font has attribute set to non-standard value. */
129 static BOOL
130 ns_has_attribute (NSFontDescriptor *fdesc, NSString *trait)
132     float v = ns_attribute_fvalue (fdesc, trait);
133     return v < -0.25 || v > 0.25;
137 /* Converts FONT_WEIGHT, FONT_SLANT, FONT_WIDTH, plus family and script/lang
138    to NSFont descriptor.  Information under extra only needed for matching. */
139 #define STYLE_REF 100
140 static NSFontDescriptor
141 *ns_spec_to_descriptor(Lisp_Object font_spec)
143     NSFontDescriptor *fdesc;
144     NSMutableDictionary *fdAttrs = [NSMutableDictionary new];
145     NSMutableDictionary *tdict = [NSMutableDictionary new];
146     NSString *family = ns_get_family (font_spec);
147     float n;
149     /* add each attr in font_spec to fdAttrs.. */
150     n = min (FONT_WEIGHT_NUMERIC (font_spec), 200);
151     if (n != -1 && n != STYLE_REF)
152         [tdict setObject: [NSNumber numberWithFloat: (n - 100.0) / 100.0]
153                   forKey: NSFontWeightTrait];
154     n = min (FONT_SLANT_NUMERIC (font_spec), 200);
155     if (n != -1 && n != STYLE_REF)
156         [tdict setObject: [NSNumber numberWithFloat: (n - 100.0) / 100.0]
157                   forKey: NSFontSlantTrait];
158     n = min (FONT_WIDTH_NUMERIC (font_spec), 200);
159     if (n > -1 && (n > STYLE_REF + 10 || n < STYLE_REF - 10))
160         [tdict setObject: [NSNumber numberWithFloat: (n - 100.0) / 100.0]
161                   forKey: NSFontWidthTrait];
162     if ([tdict count] > 0)
163         [fdAttrs setObject: tdict forKey: NSFontTraitsAttribute];
165     fdesc = [NSFontDescriptor fontDescriptorWithFontAttributes: fdAttrs];
166     if (family != nil)
167         fdesc = [fdesc fontDescriptorWithFamily: family];
168     return fdesc;
172 /* Converts NSFont descriptor to FONT_WEIGHT, FONT_SLANT, FONT_WIDTH, etc.. */
173 static Lisp_Object
174 ns_descriptor_to_entity (NSFontDescriptor *desc, Lisp_Object extra, char *style)
176     Lisp_Object font_entity = font_make_entity ();
177     /*   NSString *psName = [desc postscriptName]; */
178     NSString *family = [desc objectForKey: NSFontFamilyAttribute];
179     char *escapedFamily = strdup ([family UTF8String]);
180     unsigned int traits = [desc symbolicTraits];
182     ns_escape_name (escapedFamily);
184     ASET (font_entity, FONT_TYPE_INDEX, Qns);
185     ASET (font_entity, FONT_FOUNDRY_INDEX, Qapple);
186     ASET (font_entity, FONT_FAMILY_INDEX, intern (escapedFamily));
187     ASET (font_entity, FONT_ADSTYLE_INDEX, style ? intern (style) : Qnil);
188     ASET (font_entity, FONT_REGISTRY_INDEX, Qiso10646_1);
190     FONT_SET_STYLE (font_entity, FONT_WEIGHT_INDEX,
191                     traits & NSFontBoldTrait ? Qbold : Qmedium);
192 /*    FONT_SET_STYLE (font_entity, FONT_WEIGHT_INDEX,
193                     make_number (100 + 100
194                         * ns_attribute_fvalue (desc, NSFontWeightTrait)));*/
195     FONT_SET_STYLE (font_entity, FONT_SLANT_INDEX,
196                     traits & NSFontItalicTrait ? Qitalic : Qnormal);
197 /*    FONT_SET_STYLE (font_entity, FONT_SLANT_INDEX,
198                     make_number (100 + 100
199                          * ns_attribute_fvalue (desc, NSFontSlantTrait)));*/
200     FONT_SET_STYLE (font_entity, FONT_WIDTH_INDEX,
201                     traits & NSFontCondensedTrait ? Qcondensed :
202                     traits & NSFontExpandedTrait ? Qexpanded : Qnormal);
203 /*    FONT_SET_STYLE (font_entity, FONT_WIDTH_INDEX,
204                     make_number (100 + 100
205                          * ns_attribute_fvalue (desc, NSFontWidthTrait)));*/
207     ASET (font_entity, FONT_SIZE_INDEX, make_number (0));
208     ASET (font_entity, FONT_AVGWIDTH_INDEX, make_number (0));
209     ASET (font_entity, FONT_SPACING_INDEX,
210           make_number([desc symbolicTraits] & NSFontMonoSpaceTrait
211               ? FONT_SPACING_MONO : FONT_SPACING_PROPORTIONAL));
213     ASET (font_entity, FONT_EXTRA_INDEX, extra);
214     ASET (font_entity, FONT_OBJLIST_INDEX, Qnil);
216     if (NSFONT_TRACE)
217       {
218         fprintf (stderr, "created font_entity:\n    ");
219         debug_print (font_entity);
220       }
222     free (escapedFamily);
223     return font_entity;
227 /* Default font entity. */
228 static Lisp_Object
229 ns_fallback_entity ()
231   return ns_descriptor_to_entity ([[NSFont userFixedPitchFontOfSize: 0]
232       fontDescriptor], Qnil, NULL);
236 /* Utility: get width of a char c in screen font sfont */
237 static float
238 ns_char_width (NSFont *sfont, int c)
240     float w;
241     NSString *cstr = [NSString stringWithFormat: @"%c", c];
242 #ifdef NS_IMPL_COCOA
243     NSGlyph glyph = [sfont glyphWithName: cstr];
244     if (glyph)
245       {
246         float w = [sfont advancementForGlyph: glyph].width;
247         if (w >= 1.5)
248             return w;
249       }
250 #endif
251     w = [sfont widthOfString: cstr];
252     return max (w, 2.0);
256 /* Return whether set1 covers set2 to a reasonable extent given by pct.
257    We check, out of each 16 unicode char range containing chars in set2,
258    whether at least one character is present in set1.
259    This must be true for pct of the pairs to consider it covering. */
260 static BOOL
261 ns_charset_covers(NSCharacterSet *set1, NSCharacterSet *set2, float pct)
263     const unsigned short *bytes1 = [[set1 bitmapRepresentation] bytes];
264     const unsigned short *bytes2 = [[set2 bitmapRepresentation] bytes];
265     int i, off = 0, tot = 0;
267     for (i=0; i<4096; i++, bytes1++, bytes2++)
268         if (*bytes2)
269           {
270             tot++;
271             if (*bytes1 == 0)  // *bytes1 & *bytes2 != *bytes2
272                 off++;
273           }
274 //fprintf(stderr, "off = %d\ttot = %d\n", off,tot);
275     return (float)off / tot < 1.0 - pct;
279 /* Convert :lang property to a script.  Use of :lang property by font backend
280    seems to be limited for now (2009/05) to ja, zh, and ko. */
281 static NSString
282 *ns_lang_to_script (Lisp_Object lang)
284     if (!strcmp (SDATA (SYMBOL_NAME (lang)), "ja"))
285         return @"han";
286     /* NOTE: ja given for any hanzi that's also a kanji, but Chinese fonts
287              have more characters. */
288     else if (!strcmp (SDATA (SYMBOL_NAME (lang)), "zh"))
289         return @"han";
290     else if (!strcmp (SDATA (SYMBOL_NAME (lang)), "ko"))
291         return @"hangul";
292     else
293         return @"";
297 /* Convert OTF 4-letter script code to emacs script name.  (Why can't
298    everyone just use some standard unicode names for these?) */
299 static NSString
300 *ns_otf_to_script (Lisp_Object otf)
302     Lisp_Object script = assq_no_quit (XCAR (otf), Votf_script_alist);
303     return CONSP (script)
304         ? [NSString stringWithUTF8String: SDATA (SYMBOL_NAME XCDR ((script)))]
305         : @"";
309 /* Searches the :script, :lang, and :otf extra-bundle properties of the spec
310    for something that can be mapped to a unicode script.  Empty string returned
311    if no script spec found.
312    TODO: Eventually registry / encoding should be checked and mapped, but for
313    now the font backend will try script/lang/otf if registry fails, so it is
314    not needed. */
315 static NSString
316 *ns_get_req_script (Lisp_Object font_spec)
318     Lisp_Object extra = AREF (font_spec, FONT_EXTRA_INDEX);
320     for ( ; CONSP (extra); extra = XCDR (extra))
321       {
322         Lisp_Object tmp = XCAR (extra);
323         if (CONSP (tmp))
324           {
325             Lisp_Object key = XCAR (tmp), val = XCDR (tmp);
326             if (EQ (key, QCscript) && SYMBOLP (val))
327                 return [NSString stringWithUTF8String:
328                             SDATA (SYMBOL_NAME (val))];
329             if (EQ (key, QClang) && SYMBOLP (val))
330                 return ns_lang_to_script (val);
331             if (EQ (key, QCotf) && CONSP (val) && SYMBOLP (XCAR (val)))
332                 return ns_otf_to_script (val);
333           }
334       }
335     return @"";
339 /* This small function is static in fontset.c.  If it can be made public for
340    all ports, remove this, but otherwise it doesn't seem worth the ifdefs. */
341 static void
342 accumulate_script_ranges (Lisp_Object arg, Lisp_Object range, Lisp_Object val)
344     if (EQ (XCAR (arg), val))
345       {
346         if (CONSP (range))
347           XSETCDR (arg, Fcons (Fcons (XCAR (range), XCDR (range)), XCDR (arg)));
348         else
349           XSETCDR (arg, Fcons (Fcons (range, range), XCDR (arg)));
350       }
354 /* Use the unicode range information in Vchar_script_table to convert a script
355    name into an NSCharacterSet. */
356 static NSCharacterSet
357 *ns_script_to_charset (NSString *scriptName)
359     NSMutableCharacterSet *charset = [NSMutableCharacterSet new];
360     Lisp_Object script = intern ([scriptName UTF8String]);
361     Lisp_Object script_list = XCHAR_TABLE (Vchar_script_table)->extras[0];
363     if (! NILP (Fmemq (script, script_list)))
364       {
365         Lisp_Object ranges, range_list;
367         ranges = Fcons (script, Qnil);
368         map_char_table (accumulate_script_ranges, Qnil, Vchar_script_table,
369                         ranges);
370         range_list = Fnreverse (XCDR (ranges));
371         if (! NILP (range_list))
372           {
373             for (; CONSP (range_list); range_list = XCDR (range_list))
374               {
375                 int start = XINT (XCAR (XCAR (range_list)));
376                 int end = XINT (XCDR (XCAR (range_list)));
377                 if (NSFONT_TRACE)
378                     debug_print (XCAR (range_list));
379                 if (end < 0x10000)
380                     [charset addCharactersInRange:
381                         NSMakeRange (start, end-start)];
382               }
383           }
384       }
385     return charset;
389 /* Return an array of font families containing characters for the given
390    script, for the given coverage criterion, including at least LastResort.
391    Results are cached by script for faster access.
392    If none are found, we reduce the percentage and try again, until 5%.
393    This provides a font with at least some characters if such can be found.
394    We don't use isSupersetOfSet: because (a) it doesn't work on Tiger, and
395    (b) need approximate match as fonts covering full unicode ranges are rare. */
396 static NSSet
397 *ns_get_covering_families (NSString *script, float pct)
399     static NSMutableDictionary *scriptToFamilies = nil;
400     NSMutableSet *families;
402     if (NSFONT_TRACE)
403         NSLog(@"Request covering families for script: '%@'", script);
405     if (scriptToFamilies == nil)
406         scriptToFamilies = [NSMutableDictionary dictionaryWithCapacity: 30];
408     if ((families = [scriptToFamilies objectForKey: script]) == nil)
409       {
410         NSFontManager *fontMgr = [NSFontManager sharedFontManager];
411         NSArray *allFamilies = [fontMgr availableFontFamilies];
413         if ([script length] == 0)
414             families = [NSMutableSet setWithArray: allFamilies];
415         else
416           {
417             NSCharacterSet *charset = ns_script_to_charset (script);
418             NSString *family;
419             families = [NSMutableSet setWithCapacity: 10];
420             while (1)
421               {
422                 NSEnumerator *allFamiliesEnum = [allFamilies objectEnumerator];
423                 while (family = [allFamiliesEnum nextObject])
424                   {
425                     NSCharacterSet *fset = [[fontMgr fontWithFamily: family
426                         traits: 0 weight: 5 size: 12.0] coveredCharacterSet];
427                     /* Some fonts on OS X, maybe many on GNUstep, return nil. */
428                     if (fset == nil)
429                       fset = [NSCharacterSet characterSetWithRange:
430                                                NSMakeRange (0, 127)];
431                     if (ns_charset_covers(fset, charset, pct))
432                         [families addObject: family];
433                   }
434                 pct -= 0.2;
435                 if ([families count] > 0 || pct < 0.05)
436                     break;
437               }
438           }
439 #ifdef NS_IMPL_COCOA
440         if ([families count] == 0)
441             [families addObject: @"LastResort"];
442 #endif
443         [scriptToFamilies setObject: families forKey: script];
444       }
446     if (NSFONT_TRACE)
447         NSLog(@"    returning %d families", [families count]);
448     return families;
452 /* Implementation for list() and match().  List() can return nil, match()
453 must return something.  Strategy is to drop family name from attribute
454 matching set for match. */
455 static Lisp_Object
456 ns_findfonts (Lisp_Object font_spec, BOOL isMatch)
458     Lisp_Object tem, list = Qnil;
459     NSFontDescriptor *fdesc, *desc;
460     NSMutableSet *fkeys;
461     NSArray *matchingDescs;
462     NSEnumerator *dEnum;
463     NSString *family;
464     NSSet *cFamilies;
465     BOOL foundItal = NO;
467     if (NSFONT_TRACE)
468       {
469         fprintf (stderr, "nsfont: %s for fontspec:\n    ",
470                  (isMatch ? "match" : "list"));
471         debug_print (font_spec);
472       }
474     /* If has non-unicode registry, give up. */
475     tem = AREF (font_spec, FONT_REGISTRY_INDEX);
476     if (! NILP (tem) && !EQ (tem, Qiso10646_1) && !EQ (tem, Qunicode_bmp))
477         return isMatch ? ns_fallback_entity () : Qnil;
479     cFamilies = ns_get_covering_families (ns_get_req_script (font_spec), 0.90);
481     fdesc = ns_spec_to_descriptor (font_spec);
482     fkeys = [NSMutableSet setWithArray: [[fdesc fontAttributes] allKeys]];
483     if (isMatch)
484         [fkeys removeObject: NSFontFamilyAttribute];
486     matchingDescs = [fdesc matchingFontDescriptorsWithMandatoryKeys: fkeys];
487     if (NSFONT_TRACE)
488         NSLog(@"Got desc %@ and found %d matching fonts from it: ", fdesc,
489               [matchingDescs count]);
491     for (dEnum = [matchingDescs objectEnumerator]; desc = [dEnum nextObject]; )
492       {
493         if (![cFamilies containsObject:
494                  [desc objectForKey: NSFontFamilyAttribute]])
495             continue;
496         list = Fcons (ns_descriptor_to_entity (desc,
497                                          AREF (font_spec, FONT_EXTRA_INDEX),
498                                          NULL), list);
499         if (ns_has_attribute (desc, NSFontSlantTrait))
500             foundItal = YES;
501       }
503     /* Add synthItal member if needed. */
504     family = [fdesc objectForKey: NSFontFamilyAttribute];
505     if (family != nil && !foundItal && XINT (Flength (list)) > 0
506         && (ns_attribute_value (fdesc, NSFontSlantTrait) == nil
507             || ns_has_attribute (fdesc, NSFontSlantTrait)))
508       {
509         NSFontDescriptor *sDesc = [[[NSFontDescriptor new]
510             fontDescriptorWithSymbolicTraits: NSFontItalicTrait]
511             fontDescriptorWithFamily: family];
512         list = Fcons (ns_descriptor_to_entity (sDesc,
513                                          AREF (font_spec, FONT_EXTRA_INDEX),
514                                          "synthItal"), list);
515       }
517     if (NSFONT_TRACE)
518         fprintf (stderr, "    Returning %d entities.\n", XINT (Flength (list)));
520     return list;
525 /* ==========================================================================
527     Font driver implementation
529    ========================================================================== */
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);
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   };
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)
574   Display_Info *dpyinfo = FRAME_NS_DISPLAY_INFO (frame);
575   return (dpyinfo->name_list_element);
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.
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)
594     return ns_findfonts (font_spec, NO);
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)
605     return ns_findfonts(font_spec, YES);
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)
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? */
623   if (NSFONT_TRACE)
624     fprintf (stderr, "nsfont: list families returning %d entries\n",
625             XINT (Flength (list)));
627   return list;
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)
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;
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];
660   if (NSFONT_TRACE)
661     {
662       fprintf (stderr, "nsfont: open size %d of fontentity:\n    ", pixel_size);
663       debug_print (font_entity);
664     }
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     }
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 (ns_has_attribute (fontDesc, NSFontWeightTrait))
678       traits |= NSBoldFontMask;
679   if (ns_has_attribute (fontDesc, NSFontSlantTrait))
680       traits |= NSItalicFontMask;
682   /* see http://cocoadev.com/forums/comments.php?DiscussionID=74 */
683   fixLeopardBug = traits & NSBoldFontMask ? 10 : 5;
684   nsfont = [fontMgr fontWithFamily: family
685                             traits: traits weight: fixLeopardBug
686                               size: pixel_size];
687   /* if didn't find, try synthetic italic */
688   if (nsfont == nil && synthItal)
689     {
690       nsfont = [fontMgr fontWithFamily: family
691                                 traits: traits & ~NSItalicFontMask
692                                 weight: fixLeopardBug size: pixel_size];
693     }
694 #ifdef NS_IMPL_COCOA
695   /* LastResort not really a family */
696   if (nsfont == nil && [@"LastResort" isEqualToString: family])
697       nsfont = [NSFont fontWithName: @"LastResort" size: pixel_size];
698 #endif
700   if (nsfont == nil)
701     {
702       message_with_string ("*** Warning: font in family '%s' not found",
703                           build_string ([family UTF8String]), 1);
704       nsfont = [NSFont userFixedPitchFontOfSize: pixel_size];
705     }
707   if (NSFONT_TRACE)
708     NSLog (@"%@\n", nsfont);
710   /* Check the cache */
711   cached = [fontCache objectForKey: nsfont];
712   if (cached != nil && !synthItal)
713     {
714       if (NSFONT_TRACE)
715         fprintf(stderr, "*** nsfont_open CACHE HIT!\n");
716       /* FIXME: Cast from (unsigned long) to Lisp_Object. */
717       XHASH (font_object) = [cached unsignedLongValue];
718       return font_object;
719     }
720   else
721     {
722       font_object = font_make_object (VECSIZE (struct nsfont_info),
723                                       font_entity, pixel_size);
724       if (!synthItal)
725         [fontCache setObject: [NSNumber numberWithUnsignedLong:
726                                           (unsigned long) XHASH (font_object)]
727                       forKey: nsfont];
728     }
730   font_info = (struct nsfont_info *) XFONT_OBJECT (font_object);
731   font = (struct font *) font_info;
732   if (!font)
733     return Qnil; /* FIXME: other terms do, but return Qnil causes segfault */
735   font_info->glyphs = (unsigned short **)
736     xmalloc (0x100 * sizeof (unsigned short *));
737   font_info->metrics = (struct font_metrics **)
738     xmalloc (0x100 * sizeof (struct font_metrics *));
739   if (!font_info->glyphs || !font_info->metrics)
740     return Qnil;
741   bzero (font_info->glyphs, 0x100 * sizeof (unsigned short *));
742   bzero (font_info->metrics, 0x100 * sizeof (struct font_metrics *));
744   BLOCK_INPUT;
746   /* for metrics */
747   sfont = [nsfont screenFont];
748   if (sfont == nil)
749     sfont = nsfont;
751   /* non-metric backend font struct fields */
752   font = (struct font *) font_info;
753   font->pixel_size = [sfont pointSize];
754   font->driver = &nsfont_driver;
755   font->encoding_type = FONT_ENCODING_NOT_DECIDED;
756   font->encoding_charset = -1;
757   font->repertory_charset = -1;
758   font->default_ascent = 0;
759   font->vertical_centering = 0;
760   font->baseline_offset = 0;
761   font->relative_compose = 0;
762   font->font_encoder = NULL;
764   font->props[FONT_FORMAT_INDEX] = Qns;
765   font->props[FONT_FILE_INDEX] = Qnil;
767   {
768     double expand, hshrink;
769     float full_height, min_height, hd;
770     const char *fontName = [[nsfont fontName] UTF8String];
771     int len = strlen (fontName);
773 #ifdef NS_IMPL_GNUSTEP
774     font_info->nsfont = sfont;
775 #else
776     font_info->nsfont = nsfont;
777 #endif
778     [font_info->nsfont retain];
780     /* set up ns_font (defined in nsgui.h) */
781     font_info->name = (char *)xmalloc (strlen (fontName) + 1);
782     bcopy (fontName, font_info->name, strlen (fontName) + 1);
783     font_info->bold = [fontMgr traitsOfFont: nsfont] & NSBoldFontMask;
784     font_info->ital =
785       synthItal || ([fontMgr traitsOfFont: nsfont] & NSItalicFontMask);
787     /* Metrics etc.; some fonts return an unusually large max advance, so we
788        only use it for fonts that have wide characters. */
789     font_info->width = ([sfont numberOfGlyphs] > 2000) ?
790       [sfont maximumAdvancement].width : ns_char_width (sfont, '0');
792     brect =  [sfont boundingRectForFont];
793     full_height = brect.size.height;
794     min_height = [sfont ascender] - [sfont descender];
795     hd = full_height - min_height;
797     /* standard height, similar to Carbon. Emacs.app: was 0.5 by default. */
798     expand = 0.0;
799     hshrink = 1.0;
801     font_info->underpos = 2; /*[sfont underlinePosition] is often clipped out */
802     font_info->underwidth = [sfont underlineThickness];
803     font_info->size = font->pixel_size;
804     font_info->voffset = lrint (hshrink * [sfont ascender] + expand * hd / 2);
806     /* max bounds */
807     font_info->max_bounds.ascent =
808       lrint (hshrink * [sfont ascender] + expand * hd/2);
809     font_info->max_bounds.descent =
810       -lrint (hshrink* [sfont descender] - expand*hd/2);
811     font_info->height =
812       font_info->max_bounds.ascent + font_info->max_bounds.descent;
813     font_info->max_bounds.width = lrint (font_info->width);
814     font_info->max_bounds.lbearing = lrint (brect.origin.x);
815     font_info->max_bounds.rbearing =
816       lrint (brect.size.width - font_info->width);
818 #ifdef NS_IMPL_COCOA
819     /* set up synthItal and the CG font */
820     font_info->synthItal = synthItal;
821     {
822       ATSFontRef atsFont = ATSFontFindFromPostScriptName
823         ((CFStringRef)[nsfont fontName], kATSOptionFlagsDefault);
825       if (atsFont == kATSFontRefUnspecified)
826         {
827           /* see if we can get it by dropping italic (then synthesizing) */
828           atsFont = ATSFontFindFromPostScriptName ((CFStringRef)
829               [[fontMgr convertFont: nsfont toNotHaveTrait: NSItalicFontMask]
830                 fontName], kATSOptionFlagsDefault);
831           if (atsFont != kATSFontRefUnspecified)
832               font_info->synthItal = YES;
833           else
834             {
835               /* last resort fallback */
836               atsFont = ATSFontFindFromPostScriptName
837                 ((CFStringRef)@"Monaco", kATSOptionFlagsDefault);
838             }
839         }
840       font_info->cgfont = CGFontCreateWithPlatformFont ((void*)&atsFont);
841     }
842 #endif
844     /* set up metrics portion of font struct */
845     font->ascent = [sfont ascender];
846     font->descent = -[sfont descender];
847     font->min_width = [sfont widthOfString: @"|"]; /* FIXME */
848     font->space_width = lrint (ns_char_width (sfont, ' '));
849     font->average_width = lrint (font_info->width);
850     font->max_width = lrint (font_info->max_bounds.width);
851     font->height = lrint (font_info->height);
852     font->underline_position = lrint (font_info->underpos);
853     font->underline_thickness = lrint (font_info->underwidth);
855     font->props[FONT_NAME_INDEX] = Ffont_xlfd_name (font_object, Qnil);
856     font->props[FONT_FULLNAME_INDEX] =
857       make_unibyte_string (font_info->name, strlen (font_info->name));
858   }
859   UNBLOCK_INPUT;
861   return font_object;
865 /* Close FONT on frame F. */
866 static void
867 nsfont_close (FRAME_PTR f, struct font *font)
869   struct nsfont_info *font_info = (struct nsfont_info *)font;
870   int i;
872   /* FIXME: this occurs apparently due to same failure to detect same font
873             that causes need for cache in nsfont_open () */
874   if (!font_info)
875       return;
877   for (i =0; i<0x100; i++)
878     {
879       if (font_info->glyphs[i])
880         xfree (font_info->glyphs[i]);
881       if (font_info->metrics[i])
882         xfree (font_info->metrics[i]);
883     }
884   [font_info->nsfont release];
885 #ifdef NS_IMPL_COCOA
886   CGFontRelease (font_info->cgfont);
887 #endif
888   xfree (font_info->name);
889   xfree (font_info);
893 /* If FONT_ENTITY has a glyph for character C (Unicode code point),
894    return 1.  If not, return 0.  If a font must be opened to check
895    it, return -1. */
896 static int
897 nsfont_has_char (Lisp_Object entity, int c)
899   return -1;
903 /* Return a glyph code of FONT for character C (Unicode code point).
904    If FONT doesn't have such a glyph, return FONT_INVALID_CODE. */
905 static unsigned int
906 nsfont_encode_char (struct font *font, int c)
908   struct nsfont_info *font_info = (struct nsfont_info *)font;
909   unsigned char high = (c & 0xff00) >> 8, low = c & 0x00ff;
910   unsigned short g;
912   if (c > 0xFFFF)
913     return FONT_INVALID_CODE;
915   /* did we already cache this block? */
916   if (!font_info->glyphs[high])
917     ns_uni_to_glyphs (font_info, high);
919   g = font_info->glyphs[high][low];
920   return g == 0xFFFF ? FONT_INVALID_CODE : g;
924 /* Perform the size computation of glyphs of FONT and fill in members
925    of METRICS.  The glyphs are specified by their glyph codes in
926    CODE (length NGLYPHS). */
927 static int
928 nsfont_text_extents (struct font *font, unsigned int *code, int nglyphs,
929                      struct font_metrics *metrics)
931   struct nsfont_info *font_info = (struct nsfont_info *)font;
932   struct font_metrics *pcm;
933   unsigned char high, low;
934   int totalWidth = 0;
935   int i;
937   bzero (metrics, sizeof (struct font_metrics));
939   for (i =0; i<nglyphs; i++)
940     {
941       /* get metrics for this glyph, filling cache if need be */
942       /* TODO: get metrics for whole string from an NSLayoutManager
943                (if not too slow) */
944       high = (code[i] & 0xFF00) >> 8;
945       low = code[i] & 0x00FF;
946       if (!font_info->metrics[high])
947         ns_glyph_metrics (font_info, high);
948       pcm = &(font_info->metrics[high][low]);
950       if (metrics->lbearing > totalWidth + pcm->lbearing)
951         metrics->lbearing = totalWidth + pcm->lbearing;
952       if (metrics->rbearing < totalWidth + pcm->rbearing)
953         metrics->rbearing = totalWidth + pcm->rbearing;
954       if (metrics->ascent < pcm->ascent)
955         metrics->ascent = pcm->ascent;
956       if (metrics->descent < pcm->descent)
957         metrics->descent = pcm->descent;
959       totalWidth += pcm->width;
960     }
962   metrics->width = totalWidth;
964   return totalWidth; /* not specified in doc, but xfont.c does it */
968 /* Draw glyphs between FROM and TO of S->char2b at (X Y) pixel
969    position of frame F with S->FACE and S->GC.  If WITH_BACKGROUND
970    is nonzero, fill the background in advance.  It is assured that
971    WITH_BACKGROUND is zero when (FROM > 0 || TO < S->nchars). */
972 static int
973 nsfont_draw (struct glyph_string *s, int from, int to, int x, int y,
974              int with_background)
975 /* NOTE: focus and clip must be set
976      also, currently assumed (true in nsterm.m call) from ==0, to ==nchars */
978   static char cbuf[1024];
979   char *c = cbuf;
980 #ifdef NS_IMPL_GNUSTEP
981   static float advances[1024];
982   float *adv = advances;
983 #else
984   static CGSize advances[1024];
985   CGSize *adv = advances;
986 #endif
987   struct face *face;
988   NSRect r;
989   struct nsfont_info *font = ns_tmp_font;
990   NSColor *col, *bgCol;
991   unsigned short *t = s->char2b;
992   int i, len;
993   char isComposite = s->first_glyph->type == COMPOSITE_GLYPH;
994   int end = isComposite ? s->cmp_to : s->nchars;
996   /* Select face based on input flags */
997   switch (ns_tmp_flags)
998     {
999     case NS_DUMPGLYPH_CURSOR:
1000       face = s->face;
1001       break;
1002     case NS_DUMPGLYPH_MOUSEFACE:
1003       face = FACE_FROM_ID (s->f,
1004                            FRAME_NS_DISPLAY_INFO (s->f)->mouse_face_face_id);
1005       if (!face)
1006         face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
1007       break;
1008     default:
1009       face = s->face;
1010     }
1012   r.origin.x = s->x;
1013   if (s->face->box != FACE_NO_BOX && s->first_glyph->left_box_line_p)
1014     r.origin.x += abs (s->face->box_line_width);
1016   r.origin.y = s->y;
1017   r.size.height = FONT_HEIGHT (font);
1019   /* Convert UTF-16 (?) to UTF-8 and determine advances.  Note if we just ask
1020      NS to render the string, it will come out differently from the individual
1021      character widths added up because of layout processing. */
1022   {
1023     XCharStruct *cs;
1024     int cwidth, twidth = 0;
1025     int hi, lo;
1026     /* FIXME: composition: no vertical displacement is considered. */
1027     t += s->cmp_from; /* advance into composition */
1028     for (i = s->cmp_from; i < end; i++, t++)
1029       {
1030         hi = (*t & 0xFF00) >> 8;
1031         lo = *t & 0x00FF;
1032         if (isComposite)
1033           {
1034             if (!s->first_glyph->u.cmp.automatic)
1035                 cwidth = s->cmp->offsets[i * 2] /* (H offset) */ - twidth;
1036             else
1037               {
1038                 Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
1039                 Lisp_Object glyph = LGSTRING_GLYPH (gstring, i);
1040                 if (NILP (LGLYPH_ADJUSTMENT (glyph)))
1041                     cwidth = LGLYPH_WIDTH (glyph);
1042                 else
1043                   {
1044                     cwidth = LGLYPH_WADJUST (glyph);
1045 #ifdef NS_IMPL_GNUSTEP
1046                     *(adv-1) += LGLYPH_XOFF (glyph);
1047 #else
1048                     (*(adv-1)).width += LGLYPH_XOFF (glyph);
1049 #endif
1050                   }
1051               }
1052           }
1053         else
1054           {
1055             if (!font->metrics[hi]) /* FIXME: why/how can we need this now? */
1056               ns_glyph_metrics (font, hi);
1057             cwidth = font->metrics[hi][lo].width;
1058           }
1059         twidth += cwidth;
1060 #ifdef NS_IMPL_GNUSTEP
1061         *adv++ = cwidth;
1062         CHAR_STRING_ADVANCE (*t, c); /* this converts the char to UTF-8 */
1063 #else
1064         (*adv++).width = cwidth;
1065 #endif
1066       }
1067     len = adv - advances;
1068     r.size.width = twidth;
1069     *c = 0;
1070   }
1072   /* fill background if requested */
1073   if (with_background && !isComposite)
1074     {
1075       NSRect br = r;
1076       int fibw = FRAME_INTERNAL_BORDER_WIDTH (s->f);
1077       int mbox_line_width = max (s->face->box_line_width, 0);
1079       if (s->row->full_width_p)
1080         {
1081           if (br.origin.x <= fibw + 1 + mbox_line_width)
1082             {
1083               br.size.width += br.origin.x - mbox_line_width;
1084               br.origin.x = mbox_line_width;
1085             }
1086           if (FRAME_PIXEL_WIDTH (s->f) - (br.origin.x + br.size.width)
1087                 <= fibw+1)
1088             br.size.width += fibw;
1089         }
1090       if (s->face->box == FACE_NO_BOX)
1091         {
1092           /* expand unboxed top row over internal border */
1093           if (br.origin.y <= fibw + 1 + mbox_line_width)
1094             {
1095               br.size.height += br.origin.y;
1096               br.origin.y = 0;
1097             }
1098         }
1099       else
1100         {
1101           int correction = abs (s->face->box_line_width)+1;
1102           br.origin.y += correction;
1103           br.size.height -= 2*correction;
1104           br.origin.x += correction;
1105           br.size.width -= 2*correction;
1106         }
1108       if (!s->face->stipple)
1109         [(NS_FACE_BACKGROUND (face) != 0
1110           ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
1111           : FRAME_BACKGROUND_COLOR (s->f)) set];
1112       else
1113         {
1114           struct ns_display_info *dpyinfo = FRAME_NS_DISPLAY_INFO (s->f);
1115           [[dpyinfo->bitmaps[face->stipple-1].img stippleMask] set];
1116         }
1117       NSRectFill (br);
1118     }
1121   /* set up for character rendering */
1122   r.origin.y += font->voffset + (s->height - font->height)/2;
1124   col = (NS_FACE_FOREGROUND (face) != 0
1125          ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f)
1126          : FRAME_FOREGROUND_COLOR (s->f));
1127   /* FIXME: find another way to pass this */
1128   bgCol = (ns_tmp_flags != NS_DUMPGLYPH_FOREGROUND ? nil
1129            : (NS_FACE_BACKGROUND (face) != 0
1130               ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
1131               : FRAME_BACKGROUND_COLOR (s->f)));
1133   /* render under GNUstep using DPS */
1134 #ifdef NS_IMPL_GNUSTEP
1135   {
1136     NSGraphicsContext *context = GSCurrentContext ();
1138     DPSgsave (context);
1139     [font->nsfont set];
1141     /* do erase if "foreground" mode */
1142     if (bgCol != nil)
1143       {
1144         [bgCol set];
1145         DPSmoveto (context, r.origin.x, r.origin.y);
1146 /*[context GSSetTextDrawingMode: GSTextFillStroke]; /// not implemented yet */
1147         DPSxshow (context, cbuf, advances, len);
1148         DPSstroke (context);
1149         [col set];
1150 /*[context GSSetTextDrawingMode: GSTextFill]; /// not implemented yet */
1151       }
1153     /* do underline */
1154     if (face->underline_p)
1155       {
1156         if (face->underline_color != 0)
1157           [ns_lookup_indexed_color (face->underline_color, s->f) set];
1158         else
1159           [col set];
1160         DPSmoveto (context, r.origin.x, r.origin.y + font->underpos);
1161         DPSlineto (context, r.origin.x+r.size.width, r.origin.y+font->underpos);
1162         if (face->underline_color != 0)
1163           [col set];
1164       }
1165     else
1166       [col set];
1168     /* draw with DPSxshow () */
1169     DPSmoveto (context, r.origin.x, r.origin.y);
1170     DPSxshow (context, cbuf, advances, len);
1171     DPSstroke (context);
1173     DPSgrestore (context);
1174     return to-from;
1175   }
1177 #else  /* NS_IMPL_COCOA */
1178   {
1179     CGContextRef gcontext =
1180       [[NSGraphicsContext currentContext] graphicsPort];
1181     static CGAffineTransform fliptf;
1182     static BOOL firstTime = YES;
1184     if (firstTime)
1185       {
1186         firstTime = NO;
1187         fliptf = CGAffineTransformMakeScale (1.0, -1.0);
1188       }
1190     CGContextSaveGState (gcontext);
1192     fliptf.c =  font->synthItal ? Fix2X (kATSItalicQDSkew) : 0.0;
1194     CGContextSetFont (gcontext, font->cgfont);
1195     CGContextSetFontSize (gcontext, font->size);
1196     if (ns_antialias_text == Qnil || font->size <= ns_antialias_threshold)
1197       CGContextSetShouldAntialias (gcontext, 0);
1198     else
1199       CGContextSetShouldAntialias (gcontext, 1);
1200     if (EQ (ns_use_qd_smoothing, Qt))
1201       CGContextSetFontRenderingMode (gcontext, 2); /* 0 is Cocoa, 2 is QD */
1203     CGContextSetTextMatrix (gcontext, fliptf);
1205     if (bgCol != nil)
1206       {
1207         /* foreground drawing; erase first to avoid overstrike */
1208         [bgCol set];
1209         CGContextSetTextDrawingMode (gcontext, kCGTextFillStroke);
1210         CGContextSetTextPosition (gcontext, r.origin.x, r.origin.y);
1211         CGContextShowGlyphsWithAdvances (gcontext, s->char2b, advances, len);
1212         CGContextSetTextDrawingMode (gcontext, kCGTextFill);
1213       }
1215     if (face->underline_p)
1216       {
1217         if (face->underline_color != 0)
1218           [ns_lookup_indexed_color (face->underline_color, s->f) set];
1219         else
1220           [col set];
1221         CGContextBeginPath (gcontext);
1222         CGContextMoveToPoint (gcontext,
1223                               r.origin.x, r.origin.y + font->underpos);
1224         CGContextAddLineToPoint (gcontext, r.origin.x + r.size.width,
1225                                 r.origin.y + font->underpos);
1226         CGContextStrokePath (gcontext);
1227         if (face->underline_color != 0)
1228           [col set];
1229       }
1230     else
1231       [col set];
1233     CGContextSetTextPosition (gcontext, r.origin.x, r.origin.y);
1234     CGContextShowGlyphsWithAdvances (gcontext, s->char2b + s->cmp_from,
1235                                     advances, len);
1237     if (face->overstrike)
1238       {
1239         CGContextSetTextPosition (gcontext, r.origin.x+0.5, r.origin.y);
1240         CGContextShowGlyphsWithAdvances (gcontext, s->char2b + s->cmp_from,
1241                                         advances, len);
1242       }
1244     CGContextRestoreGState (gcontext);
1245     return;
1246   }
1247 #endif  /* NS_IMPL_COCOA */
1253 /* ==========================================================================
1255     Font glyph and metrics caching functions
1257    ========================================================================== */
1259 /* Find and cache corresponding glyph codes for unicode values in given
1260    hi-byte block of 256. */
1261 static void
1262 ns_uni_to_glyphs (struct nsfont_info *font_info, unsigned char block)
1264 #ifdef NS_IMPL_COCOA
1265   static EmacsGlyphStorage *glyphStorage;
1266   static char firstTime = 1;
1267 #endif
1268   unichar *unichars = xmalloc (0x101 * sizeof (unichar));
1269   unsigned int i, g, idx;
1270   unsigned short *glyphs;
1272   if (NSFONT_TRACE)
1273     fprintf (stderr, "%p\tFinding glyphs for glyphs in block %d\n",
1274             font_info, block);
1276  BLOCK_INPUT;
1278 #ifdef NS_IMPL_COCOA
1279   if (firstTime)
1280     {
1281       firstTime = 0;
1282       glyphStorage = [[EmacsGlyphStorage alloc] initWithCapacity: 0x100];
1283     }
1284 #endif
1286   font_info->glyphs[block] = xmalloc (0x100 * sizeof (unsigned short));
1287   if (!unichars || !(font_info->glyphs[block]))
1288     abort ();
1290   /* create a string containing all unicode characters in this block */
1291   for (idx = block<<8, i =0; i<0x100; idx++, i++)
1292     if (idx < 0xD800 || idx > 0xDFFF)
1293       unichars[i] = idx;
1294     else
1295       unichars[i] = 0xFEFF;
1296   unichars[0x100] = 0;
1298   {
1299 #ifdef NS_IMPL_COCOA
1300     NSString *allChars = [[NSString alloc]
1301                                initWithCharactersNoCopy: unichars
1302                                                  length: 0x100
1303                                            freeWhenDone: NO];
1304     NSGlyphGenerator *glyphGenerator = [NSGlyphGenerator sharedGlyphGenerator];
1305     /*NSCharacterSet *coveredChars = [nsfont coveredCharacterSet]; */
1306     unsigned int numGlyphs = [font_info->nsfont numberOfGlyphs];
1307     unsigned int gInd =0, cInd =0;
1309     [glyphStorage setString: allChars font: font_info->nsfont];
1310     [glyphGenerator generateGlyphsForGlyphStorage: glyphStorage
1311                         desiredNumberOfCharacters: glyphStorage->maxChar
1312                                        glyphIndex: &gInd characterIndex: &cInd];
1313 #endif
1314     glyphs = font_info->glyphs[block];
1315     for (i =0; i<0x100; i++, glyphs++)
1316       {
1317 #ifdef NS_IMPL_GNUSTEP
1318         g = unichars[i];
1319 #else
1320         g = glyphStorage->cglyphs[i];
1321         /* TODO: is this a good check?  maybe need to use coveredChars.. */
1322         if (g > numGlyphs)
1323           g = 0xFFFF; /* hopefully unused... */
1324 #endif
1325         *glyphs = g;
1326       }
1328 #ifdef NS_IMPL_COCOA
1329     [allChars release];
1330 #endif
1331   }
1333   UNBLOCK_INPUT;
1334   xfree (unichars);
1338 /* Determine and cache metrics for corresponding glyph codes in given
1339    hi-byte block of 256. */
1340 static void
1341 ns_glyph_metrics (struct nsfont_info *font_info, unsigned char block)
1343   unsigned int i, g;
1344   unsigned int numGlyphs = [font_info->nsfont numberOfGlyphs];
1345   NSFont *sfont;
1346   struct font_metrics *metrics;
1348   if (NSFONT_TRACE)
1349     fprintf (stderr, "%p\tComputing metrics for glyphs in block %d\n",
1350             font_info, block);
1352 #ifdef NS_IMPL_GNUSTEP
1353   /* not implemented yet (as of startup 0.18), so punt */
1354   if (numGlyphs == 0)
1355     numGlyphs = 0x10000;
1356 #endif
1358  BLOCK_INPUT;
1359  sfont = [font_info->nsfont screenFont];
1361   font_info->metrics[block] = xmalloc (0x100 * sizeof (struct font_metrics));
1362   bzero (font_info->metrics[block], 0x100 * sizeof (struct font_metrics));
1363   if (!(font_info->metrics[block]))
1364     abort ();
1366   metrics = font_info->metrics[block];
1367   for (g = block<<8, i =0; i<0x100 && g < numGlyphs; g++, i++, metrics++)
1368     {
1369       float w, lb, rb;
1370       NSRect r = [sfont boundingRectForGlyph: g];
1372 #ifdef NS_IMPL_GNUSTEP
1373       {
1374         /* lord help us */
1375         NSString *s = [NSString stringWithFormat: @"%c", g];
1376         w = [sfont widthOfString: s];
1377       }
1378 #else
1379       w = [sfont advancementForGlyph: g].width;
1380 #endif
1381       w = max (w, 2.0);
1382       metrics->width = lrint (w);
1384       lb = r.origin.x;
1385       rb = r.size.width - w;
1386       if (lb < 0)
1387         metrics->lbearing = round (lb);
1388       if (font_info->ital)
1389         rb += 0.22 * font_info->height;
1390       metrics->rbearing = lrint (w + rb);
1392       metrics->descent = r.origin.y < 0 ? -r.origin.y : 0;
1393  /*lrint (hshrink * [sfont ascender] + expand * hd/2); */
1394       metrics->ascent = r.size.height - metrics->descent;
1395 /*-lrint (hshrink* [sfont descender] - expand * hd/2); */
1396     }
1397   UNBLOCK_INPUT;
1401 #ifdef NS_IMPL_COCOA
1402 /* helper for font glyph setup */
1403 @implementation EmacsGlyphStorage
1405 - init
1407   return [self initWithCapacity: 1024];
1410 - initWithCapacity: (unsigned long) c
1412   self = [super init];
1413   maxChar = 0;
1414   maxGlyph = 0;
1415   dict = [NSMutableDictionary new];
1416   cglyphs = (CGGlyph *)xmalloc (c * sizeof (CGGlyph));
1417   return self;
1420 - (void) dealloc
1422   if (attrStr != nil)
1423     [attrStr release];
1424   [dict release];
1425   xfree (cglyphs);
1426   [super dealloc];
1429 - (void) setString: (NSString *)str font: (NSFont *)font
1431   [dict setObject: font forKey: NSFontAttributeName];
1432   attrStr = [[NSAttributedString alloc] initWithString: str attributes: dict];
1433   maxChar = [str length];
1434   maxGlyph = 0;
1437 /* NSGlyphStorage protocol */
1438 - (unsigned int)layoutOptions
1440   return 0;
1443 - (NSAttributedString *)attributedString
1445   return attrStr;
1448 - (void)insertGlyphs: (const NSGlyph *)glyphs length: (unsigned int)length
1449         forStartingGlyphAtIndex: (unsigned int)glyphIndex
1450         characterIndex: (unsigned int)charIndex
1452   len = glyphIndex+length;
1453   for (i =glyphIndex; i<len; i++)
1454     cglyphs[i] = glyphs[i-glyphIndex];
1455   if (len > maxGlyph)
1456     maxGlyph = len;
1459 - (void)setIntAttribute: (int)attributeTag value: (int)val
1460         forGlyphAtIndex: (unsigned)glyphIndex
1462   return;
1465 @end
1466 #endif /* NS_IMPL_COCOA */
1469 /* Debugging */
1470 void
1471 ns_dump_glyphstring (struct glyph_string *s)
1473   int i;
1475   fprintf (stderr, "Glyph string len = %d at (%d, %d) overhang (%d, %d),"
1476 "overlap = %d, bg_filled = %d:",
1477            s->nchars, s->x, s->y, s->left_overhang, s->right_overhang,
1478            s->row->overlapping_p, s->background_filled_p);
1479   for (i =0; i<s->nchars; i++)
1480     fprintf (stderr, "%c", s->first_glyph[i].u.ch);
1481   fprintf (stderr, "\n");
1485 void
1486 syms_of_nsfont ()
1488   nsfont_driver.type = Qns;
1489   register_font_driver (&nsfont_driver, NULL);
1490   DEFSYM (Qapple, "apple");
1491   DEFSYM (Qroman, "roman");
1492   DEFSYM (Qmedium, "medium");
1495 // arch-tag: d6c3c6f0-62de-4978-8b1e-b7966fe02cae