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