Fix project-find-regexp in remote projects
[emacs.git] / src / nsfont.m
blobddbaea119678e0f2ed2b13917e80dff581fb0529
1 /* Font back-end driver for the GNUstep window system.
2    See font.h
3    Copyright (C) 2006-2024 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 (at
10 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 <https://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 "character.h"
37 #include "font.h"
38 #include "termchar.h"
39 #include "pdumper.h"
41 #import <Foundation/NSException.h>
42 #import <AppKit/NSFontDescriptor.h>
43 #import <AppKit/NSLayoutManager.h>
44 #import <GNUstepGUI/GSLayoutManager.h>
45 #import <GNUstepGUI/GSFontInfo.h>
47 #define NSFONT_TRACE 0
49 /* Structure used by GS `shape' functions for storing layout
50    information for each glyph.  Borrowed from macfont.h.  */
51 struct ns_glyph_layout
53   /* Range of indices of the characters composed into the group of
54      glyphs that share the cursor position with this glyph.  The
55      members `location' and `length' are in UTF-16 indices.  */
56   NSRange comp_range;
58   /* UTF-16 index in the source string for the first character
59      associated with this glyph.  */
60   NSUInteger string_index;
62   /* Horizontal and vertical adjustments of glyph position.  The
63      coordinate space is that of Core Text.  So, the `baseline_delta'
64      value is negative if the glyph should be placed below the
65      baseline.  */
66   CGFloat advance_delta, baseline_delta;
68   /* Typographical width of the glyph.  */
69   CGFloat advance;
71   /* Glyph ID of the glyph.  */
72   NSGlyph glyph_id;
76 enum lgstring_direction
77   {
78     DIR_R2L = -1, DIR_UNKNOWN = 0, DIR_L2R = 1
79   };
81 enum gs_font_slant
82   {
83     GS_FONT_SLANT_ITALIC,
84     GS_FONT_SLANT_REVERSE_ITALIC,
85     GS_FONT_SLANT_NORMAL
86   };
88 enum gs_font_weight
89   {
90     GS_FONT_WEIGHT_LIGHT,
91     GS_FONT_WEIGHT_BOLD,
92     GS_FONT_WEIGHT_NORMAL
93   };
95 enum gs_font_width
96   {
97     GS_FONT_WIDTH_CONDENSED,
98     GS_FONT_WIDTH_EXPANDED,
99     GS_FONT_WIDTH_NORMAL
100   };
102 enum gs_specified
103   {
104     GS_SPECIFIED_SLANT = 1,
105     GS_SPECIFIED_WEIGHT = 1 << 1,
106     GS_SPECIFIED_WIDTH = 1 << 2,
107     GS_SPECIFIED_FAMILY = 1 << 3,
108     GS_SPECIFIED_SPACING = 1 << 4
109   };
111 struct gs_font_data
113   int specified;
114   enum gs_font_slant slant;
115   enum gs_font_weight weight;
116   enum gs_font_width width;
117   bool monospace_p;
118   char *family_name;
121 static void
122 ns_done_font_data (struct gs_font_data *data)
124   if (data->specified & GS_SPECIFIED_FAMILY)
125     xfree (data->family_name);
128 static void
129 ns_get_font_data (NSFontDescriptor *desc, struct gs_font_data *dat)
131   NSNumber *tem;
132   NSFontSymbolicTraits traits = [desc symbolicTraits];
133   NSDictionary *dict = [desc objectForKey: NSFontTraitsAttribute];
134   NSString *family = [desc objectForKey: NSFontFamilyAttribute];
136   dat->specified = 0;
138   if (family != nil)
139     {
140       dat->specified |= GS_SPECIFIED_FAMILY;
141       dat->family_name = xstrdup ([family cStringUsingEncoding: NSUTF8StringEncoding]);
142     }
144   tem = [desc objectForKey: NSFontFixedAdvanceAttribute];
146   if ((tem != nil && [tem boolValue] != NO)
147       || (traits & NSFontMonoSpaceTrait))
148     {
149       dat->specified |= GS_SPECIFIED_SPACING;
150       dat->monospace_p = true;
151     }
152   else if (tem != nil && [tem boolValue] == NO)
153     {
154       dat->specified |= GS_SPECIFIED_SPACING;
155       dat->monospace_p = false;
156     }
158   if (traits & NSFontBoldTrait)
159     {
160       dat->specified |= GS_SPECIFIED_WEIGHT;
161       dat->weight = GS_FONT_WEIGHT_BOLD;
162     }
164   if (traits & NSFontItalicTrait)
165     {
166       dat->specified |= GS_SPECIFIED_SLANT;
167       dat->slant = GS_FONT_SLANT_ITALIC;
168     }
170   if (traits & NSFontCondensedTrait)
171     {
172       dat->specified |= GS_SPECIFIED_WIDTH;
173       dat->width = GS_FONT_WIDTH_CONDENSED;
174     }
175   else if (traits & NSFontExpandedTrait)
176     {
177       dat->specified |= GS_SPECIFIED_WIDTH;
178       dat->width = GS_FONT_WIDTH_EXPANDED;
179     }
181   if (dict != nil)
182     {
183       tem = [dict objectForKey: NSFontSlantTrait];
185       if (tem != nil)
186         {
187           dat->specified |= GS_SPECIFIED_SLANT;
189           dat->slant = [tem floatValue] > 0
190             ? GS_FONT_SLANT_ITALIC
191             : ([tem floatValue] < 0
192                ? GS_FONT_SLANT_REVERSE_ITALIC
193                : GS_FONT_SLANT_NORMAL);
194         }
196       tem = [dict objectForKey: NSFontWeightTrait];
198       if (tem != nil)
199         {
200           dat->specified |= GS_SPECIFIED_WEIGHT;
202           dat->weight = [tem floatValue] > 0
203             ? GS_FONT_WEIGHT_BOLD
204             : ([tem floatValue] < -0.4f
205                ? GS_FONT_WEIGHT_LIGHT
206                : GS_FONT_WEIGHT_NORMAL);
207         }
209       tem = [dict objectForKey: NSFontWidthTrait];
211       if (tem != nil)
212         {
213           dat->specified |= GS_SPECIFIED_WIDTH;
215           dat->width = [tem floatValue] > 0
216             ? GS_FONT_WIDTH_EXPANDED
217             : ([tem floatValue] < 0
218                ? GS_FONT_WIDTH_NORMAL
219                : GS_FONT_WIDTH_CONDENSED);
220         }
221     }
224 static bool
225 ns_font_descs_match_p (NSFontDescriptor *desc, NSFontDescriptor *target)
227   struct gs_font_data dat;
228   struct gs_font_data t;
230   ns_get_font_data (desc, &dat);
231   ns_get_font_data (target, &t);
233   if (!(t.specified & GS_SPECIFIED_WIDTH))
234     t.width = GS_FONT_WIDTH_NORMAL;
235   if (!(t.specified & GS_SPECIFIED_WEIGHT))
236     t.weight = GS_FONT_WEIGHT_NORMAL;
237   if (!(t.specified & GS_SPECIFIED_SPACING))
238     t.monospace_p = false;
239   if (!(t.specified & GS_SPECIFIED_SLANT))
240     t.slant = GS_FONT_SLANT_NORMAL;
242   if (!(t.specified & GS_SPECIFIED_FAMILY))
243     emacs_abort ();
245   bool match_p = true;
247   if (dat.specified & GS_SPECIFIED_WIDTH
248       && dat.width != t.width)
249     {
250       match_p = false;
251       goto gout;
252     }
254   if (dat.specified & GS_SPECIFIED_WEIGHT
255       && dat.weight != t.weight)
256     {
257       match_p = false;
258       goto gout;
259     }
261   if (dat.specified & GS_SPECIFIED_SPACING
262       && dat.monospace_p != t.monospace_p)
263     {
264       match_p = false;
265       goto gout;
266     }
268   if (dat.specified & GS_SPECIFIED_SLANT
269       && dat.monospace_p != t.monospace_p)
270     {
271       if (NSFONT_TRACE)
272         printf ("Matching monospace for %s: %d %d\n",
273                 t.family_name, dat.monospace_p,
274                 t.monospace_p);
275       match_p = false;
276       goto gout;
277     }
279   if (dat.specified & GS_SPECIFIED_FAMILY
280       && strcmp (dat.family_name, t.family_name))
281     match_p = false;
283  gout:
284   ns_done_font_data (&dat);
285   ns_done_font_data (&t);
287   return match_p;
290 /* Font glyph and metrics caching functions, implemented at end.  */
291 static void ns_uni_to_glyphs (struct nsfont_info *font_info,
292                               unsigned char block);
293 static void ns_glyph_metrics (struct nsfont_info *font_info,
294                               unsigned int block);
296 #define INVALID_GLYPH 0xFFFF
298 /* ==========================================================================
300     Utilities
302    ========================================================================== */
305 /* Extract family name from a font spec.  */
306 static NSString *
307 ns_get_family (Lisp_Object font_spec)
309   Lisp_Object tem = AREF (font_spec, FONT_FAMILY_INDEX);
310   if (NILP (tem))
311       return nil;
312   else
313     {
314       char *tmp = xlispstrdup (SYMBOL_NAME (tem));
315       NSString *family;
316       family = [NSString stringWithUTF8String: tmp];
317       xfree (tmp);
318       return family;
319     }
322 /* Converts FONT_WEIGHT, FONT_SLANT, FONT_WIDTH, plus family and script/lang
323    to NSFont descriptor.  Information under extra only needed for matching.  */
324 static NSFontDescriptor *
325 ns_spec_to_descriptor (Lisp_Object font_spec)
327   NSFontDescriptor *fdesc;
328   NSMutableDictionary *fdAttrs = [NSMutableDictionary new];
329   NSString *family = ns_get_family (font_spec);
330   NSMutableDictionary *tdict = [NSMutableDictionary new];
332   Lisp_Object tem;
334   tem = FONT_SLANT_SYMBOLIC (font_spec);
335   if (!NILP (tem))
336     {
337       if (EQ (tem, Qitalic) || EQ (tem, Qoblique))
338         [tdict setObject: [NSNumber numberWithFloat: 1.0]
339                   forKey: NSFontSlantTrait];
340       else if (EQ (tem, Qreverse_italic)
341                || EQ (tem, Qreverse_oblique))
342         [tdict setObject: [NSNumber numberWithFloat: -1.0]
343                   forKey: NSFontSlantTrait];
344       else
345         [tdict setObject: [NSNumber numberWithFloat: 0.0]
346                   forKey: NSFontSlantTrait];
347     }
349   tem = FONT_WIDTH_SYMBOLIC (font_spec);
350   if (!NILP (tem))
351     {
352       if (EQ (tem, Qcondensed))
353         [tdict setObject: [NSNumber numberWithFloat: -1.0]
354                   forKey: NSFontWidthTrait];
355       else if (EQ (tem, Qexpanded))
356         [tdict setObject: [NSNumber numberWithFloat: 1.0]
357                   forKey: NSFontWidthTrait];
358       else
359         [tdict setObject: [NSNumber numberWithFloat: 0.0]
360                   forKey: NSFontWidthTrait];
361     }
363   tem = FONT_WEIGHT_SYMBOLIC (font_spec);
365   if (!NILP (tem))
366     {
367       if (EQ (tem, Qbold))
368         {
369           [tdict setObject: [NSNumber numberWithFloat: 1.0]
370                     forKey: NSFontWeightTrait];
371         }
372       else if (EQ (tem, Qlight))
373         {
374           [tdict setObject: [NSNumber numberWithFloat: -1.0]
375                     forKey: NSFontWeightTrait];
376         }
377       else
378         {
379           [tdict setObject: [NSNumber numberWithFloat: 0.0]
380                     forKey: NSFontWeightTrait];
381         }
382     }
384   tem = AREF (font_spec, FONT_SPACING_INDEX);
386   if (family != nil)
387     [fdAttrs setObject: family
388                 forKey: NSFontFamilyAttribute];
390   if (FIXNUMP (tem))
391     {
392       if (XFIXNUM (tem) != FONT_SPACING_PROPORTIONAL)
393         [fdAttrs setObject: [NSNumber numberWithBool: YES]
394                     forKey: NSFontFixedAdvanceAttribute];
395       else
396         [fdAttrs setObject: [NSNumber numberWithBool: NO]
397                     forKey: NSFontFixedAdvanceAttribute];
398     }
400   /* Handle special families such as ``fixed'', ``monospace'' or
401      ``Sans Serif''.  */
403   if ([family isEqualToString: @"fixed"]
404       || [family isEqualToString: @"monospace"])
405     [fdAttrs setObject: [[NSFont userFixedPitchFontOfSize: 0] familyName]
406                 forKey: NSFontFamilyAttribute];
407   else if ([family isEqualToString: @"Sans Serif"])
408     [fdAttrs setObject: [[NSFont userFontOfSize: 0] familyName]
409                 forKey: NSFontFamilyAttribute];
411   [fdAttrs setObject: tdict forKey: NSFontTraitsAttribute];
413   fdesc = [[[NSFontDescriptor fontDescriptorWithFontAttributes: fdAttrs]
414              retain] autorelease];
416   [tdict release];
417   [fdAttrs release];
418   return fdesc;
422 /* Converts NSFont descriptor to FONT_WEIGHT, FONT_SLANT, FONT_WIDTH, etc.  */
423 static Lisp_Object
424 ns_descriptor_to_entity (NSFontDescriptor *desc,
425                          Lisp_Object extra,
426                          const char *style)
428   Lisp_Object font_entity = font_make_entity ();
429   struct gs_font_data data;
430   ns_get_font_data (desc, &data);
432   ASET (font_entity, FONT_TYPE_INDEX, Qns);
433   ASET (font_entity, FONT_FOUNDRY_INDEX, Qns);
434   if (data.specified & GS_SPECIFIED_FAMILY)
435     ASET (font_entity, FONT_FAMILY_INDEX, intern (data.family_name));
436   ASET (font_entity, FONT_ADSTYLE_INDEX, style ? intern (style) : Qnil);
437   ASET (font_entity, FONT_REGISTRY_INDEX, Qiso10646_1);
439   if (data.specified & GS_SPECIFIED_WEIGHT)
440     {
441       FONT_SET_STYLE (font_entity, FONT_WEIGHT_INDEX,
442                       data.weight == GS_FONT_WEIGHT_BOLD
443                       ? Qbold : (data.weight == GS_FONT_WEIGHT_LIGHT
444                                  ? Qlight : Qnormal));
445     }
446   else
447     FONT_SET_STYLE (font_entity, FONT_WEIGHT_INDEX, Qnormal);
449   if (data.specified & GS_SPECIFIED_SLANT)
450     {
451       FONT_SET_STYLE (font_entity, FONT_SLANT_INDEX,
452                       data.slant == GS_FONT_SLANT_ITALIC
453                       ? Qitalic : (data.slant == GS_FONT_SLANT_REVERSE_ITALIC
454                                    ? Qreverse_italic : Qnormal));
455     }
456   else
457     FONT_SET_STYLE (font_entity, FONT_SLANT_INDEX, Qnormal);
459   if (data.specified & GS_SPECIFIED_WIDTH)
460     {
461       FONT_SET_STYLE (font_entity, FONT_WIDTH_INDEX,
462                       data.width == GS_FONT_WIDTH_CONDENSED
463                       ? Qcondensed : (data.width == GS_FONT_WIDTH_EXPANDED
464                                       ? Qexpanded : Qnormal));
465     }
466   else
467     FONT_SET_STYLE (font_entity, FONT_WIDTH_INDEX, Qnormal);
469   ASET (font_entity, FONT_SIZE_INDEX, make_fixnum (0));
470   ASET (font_entity, FONT_AVGWIDTH_INDEX, make_fixnum (0));
471   ASET (font_entity, FONT_SPACING_INDEX,
472         make_fixnum ((data.specified & GS_SPECIFIED_SPACING && data.monospace_p)
473                      ? FONT_SPACING_MONO : FONT_SPACING_PROPORTIONAL));
475   ASET (font_entity, FONT_EXTRA_INDEX, extra);
476   ASET (font_entity, FONT_OBJLIST_INDEX, Qnil);
478   if (NSFONT_TRACE)
479     {
480       fputs ("created font_entity:\n    ", stderr);
481       debug_print (font_entity);
482     }
484   ns_done_font_data (&data);
485   return font_entity;
489 /* Default font entity.  */
490 static Lisp_Object
491 ns_fallback_entity (void)
493   return ns_descriptor_to_entity ([[NSFont userFixedPitchFontOfSize: 1] fontDescriptor], Qnil, NULL);
497 /* Utility: get width of a char c in screen font SFONT.  */
498 static CGFloat
499 ns_char_width (NSFont *sfont, int c)
501   CGFloat w = -1.0;
502   NSString *cstr = [NSString stringWithFormat: @"%c", c];
504   if (w < 0.0)
505     {
506       NSDictionary *attrsDictionary =
507         [NSDictionary dictionaryWithObject: sfont forKey: NSFontAttributeName];
508       w = [cstr sizeWithAttributes: attrsDictionary].width;
509     }
511   return max (w, 1.0);
514 /* Return average width over ASCII printable characters for SFONT.  */
516 static NSString *ascii_printable;
518 static int
519 ns_ascii_average_width (NSFont *sfont)
521   CGFloat w = -1.0;
523   if (!ascii_printable)
524     {
525       char chars[96];
526       int ch;
527       for (ch = 0; ch < 95; ch++)
528         chars[ch] = ' ' + ch;
529       chars[95] = '\0';
531       ascii_printable = [[NSString alloc] initWithFormat: @"%s", chars];
532     }
534   if (w < (CGFloat) 0.0)
535     {
536       NSDictionary *attrsDictionary =
537         [NSDictionary dictionaryWithObject: sfont forKey: NSFontAttributeName];
538       w = [ascii_printable sizeWithAttributes: attrsDictionary].width;
539     }
541   return lrint (w / (CGFloat) 95.0);
545 /* Return whether set1 covers set2 to a reasonable extent given by pct.
547    The GNUstep bitmap representation doesn't match Apple's
548    description.  It appears to be a single block of bytes, not broken
549    up into planes, where the last byte contains the highest character
550    the character set supports.  */
551 static BOOL
552 ns_charset_covers(NSCharacterSet *set1, NSCharacterSet *set2, float pct)
554   NSData *font = [set1 bitmapRepresentation];
555   NSData *script = [set2 bitmapRepresentation];
557   uint8_t *fontPlane = (uint8_t *)[font bytes];
558   uint8_t *scriptPlane = (uint8_t *)[script bytes];
560   int covered = 0, total = 0;
562   for (ptrdiff_t b = 0 ; b < [script length] ; b++)
563     for (int i = 0 ; i < 8 ; i++)
564       {
565         if (*(scriptPlane + b) & (1 << i))
566           {
567             total++;
569             if (b < [font length]
570                 && *(fontPlane + b) & (1 << i))
571               covered++;
572           }
573       }
575   return (float)covered / total >= 1.0F - pct;
579 /* Convert :lang property to a script.  Use of :lang property by font backend
580    seems to be limited for now (2009/05) to ja, zh, and ko.  */
581 static NSString
582 *ns_lang_to_script (Lisp_Object lang)
584     if (!strcmp (SSDATA (SYMBOL_NAME (lang)), "ja"))
585         return @"han";
586     /* NOTE: ja given for any hanzi that's also a kanji, but Chinese fonts
587              have more characters.  */
588     else if (!strcmp (SSDATA (SYMBOL_NAME (lang)), "zh"))
589         return @"han";
590     else if (!strcmp (SSDATA (SYMBOL_NAME (lang)), "ko"))
591         return @"hangul";
592     else
593         return @"";
597 /* Convert OTF 4-letter script code to emacs script name.  (Why can't
598    everyone just use some standard Unicode names for these?)  */
599 static NSString
600 *ns_otf_to_script (Lisp_Object otf)
602     Lisp_Object script = assq_no_quit (XCAR (otf), Votf_script_alist);
603     return CONSP (script)
604         ? [NSString stringWithLispString: SYMBOL_NAME (XCDR (script))]
605         : @"";
609 /* Convert a font registry.  */
610 static NSString
611 *ns_registry_to_script (char *reg)
613     Lisp_Object script, r, rts = Vns_reg_to_script;
614     while (CONSP (rts))
615       {
616         r = XCAR (XCAR (rts));
617         if (!strncmp (SSDATA (r), reg, SBYTES (r)))
618           {
619             script = XCDR (XCAR (rts));
620             return [NSString stringWithLispString: SYMBOL_NAME (script)];
621           }
622         rts = XCDR (rts);
623       }
624     return  @"";
628 /* Searches the :script, :lang, and :otf extra-bundle properties of the spec,
629    plus registry regular property, for something that can be mapped to a
630    Unicode script.  Empty string returned if no script spec found.  */
631 static NSString
632 *ns_get_req_script (Lisp_Object font_spec)
634     Lisp_Object reg = AREF (font_spec, FONT_REGISTRY_INDEX);
635     Lisp_Object extra = AREF (font_spec, FONT_EXTRA_INDEX);
637     /* The extra-bundle properties have priority.  */
638     for ( ; CONSP (extra); extra = XCDR (extra))
639       {
640         Lisp_Object tmp = XCAR (extra);
641         if (CONSP (tmp))
642           {
643             Lisp_Object key = XCAR (tmp), val = XCDR (tmp);
644             if (EQ (key, QCscript) && SYMBOLP (val))
645                 return [NSString stringWithLispString: SYMBOL_NAME (val)];
646             if (EQ (key, QClang) && SYMBOLP (val))
647                 return ns_lang_to_script (val);
648             if (EQ (key, QCotf) && CONSP (val) && SYMBOLP (XCAR (val)))
649                 return ns_otf_to_script (val);
650           }
651       }
653     /* If we get here, check the charset portion of the registry.  */
654     if (! NILP (reg))
655       {
656         /* XXX: iso10646 is passed in for non-ascii latin-1 characters
657            (which causes box rendering if we don't treat it like iso8858-1)
658            but also for ascii (which causes unnecessary font substitution).  */
659 #if 0
660         if (EQ (reg, Qiso10646_1))
661           reg = Qiso8859_1;
662 #endif
663         return ns_registry_to_script (SSDATA (SYMBOL_NAME (reg)));
664       }
666     return @"";
670 /* This small function is static in fontset.c.  If it can be made public for
671    all ports, remove this, but otherwise it doesn't seem worth the ifdefs.  */
672 static void
673 accumulate_script_ranges (Lisp_Object arg, Lisp_Object range, Lisp_Object val)
675     if (EQ (XCAR (arg), val))
676       {
677         if (CONSP (range))
678           XSETCDR (arg, Fcons (Fcons (XCAR (range), XCDR (range)), XCDR (arg)));
679         else
680           XSETCDR (arg, Fcons (Fcons (range, range), XCDR (arg)));
681       }
685 /* Use the Unicode range information in Vchar_script_table to convert a script
686    name into an NSCharacterSet.  */
687 static NSCharacterSet
688 *ns_script_to_charset (NSString *scriptName)
690     NSMutableCharacterSet *charset = [NSMutableCharacterSet new];
691     Lisp_Object script = intern ([scriptName UTF8String]);
692     Lisp_Object script_list = XCHAR_TABLE (Vchar_script_table)->extras[0];
694     if (! NILP (Fmemq (script, script_list)))
695       {
696         Lisp_Object ranges, range_list;
698         ranges = list1 (script);
699         map_char_table (accumulate_script_ranges, Qnil, Vchar_script_table,
700                         ranges);
701         range_list = Fnreverse (XCDR (ranges));
702         if (! NILP (range_list))
703           {
704             for (; CONSP (range_list); range_list = XCDR (range_list))
705               {
706                 int start = XFIXNUM (XCAR (XCAR (range_list)));
707                 int end = XFIXNUM (XCDR (XCAR (range_list)));
708                 if (NSFONT_TRACE)
709                     debug_print (XCAR (range_list));
710                 if (end < 0x10000)
711                     [charset addCharactersInRange:
712                         NSMakeRange (start, end-start)];
713               }
714           }
715       }
716     return charset;
720 /* Return an array of font families containing characters for the given
721    script, for the given coverage criterion, including at least LastResort.
722    Results are cached by script for faster access.
723    If none are found, we reduce the percentage and try again, until 5%.
724    This provides a font with at least some characters if such can be found.
725    We don't use isSupersetOfSet: because (a) it doesn't work on Tiger, and
726    (b) need approximate match as fonts covering full Unicode ranges are rare.  */
727 static NSSet
728 *ns_get_covering_families (NSString *script, float pct)
730     static NSMutableDictionary *scriptToFamilies = nil;
731     NSMutableSet *families;
733     if (NSFONT_TRACE)
734         NSLog(@"Request covering families for script: '%@'", script);
736     if (scriptToFamilies == nil)
737         scriptToFamilies = [[NSMutableDictionary alloc] init];
739     if ((families = [scriptToFamilies objectForKey: script]) == nil)
740       {
741         NSFontManager *fontMgr = [NSFontManager sharedFontManager];
742         NSArray *allFamilies = [fontMgr availableFontFamilies];
744         if ([script length] == 0)
745             families = [NSMutableSet setWithArray: allFamilies];
746         else
747           {
748             NSCharacterSet *charset = ns_script_to_charset (script);
749             NSString *family;
750             families = [NSMutableSet setWithCapacity: 10];
751             while (1)
752               {
753                 NSEnumerator *allFamiliesEnum = [allFamilies objectEnumerator];
754                 while ((family = [allFamiliesEnum nextObject]))
755                   {
756                     NSCharacterSet *fset = [[fontMgr fontWithFamily: family
757                         traits: 0 weight: 5 size: 12.0] coveredCharacterSet];
758                     /* Some fonts on macOS, maybe many on GNUstep, return nil.  */
759                     if (fset == nil)
760                       fset = [NSCharacterSet characterSetWithRange:
761                                                NSMakeRange (0, 127)];
762                     if (ns_charset_covers(fset, charset, pct))
763                         [families addObject: family];
764                   }
765                 pct -= 0.2F;
766                 if ([families count] > 0 || pct < 0.05F)
767                     break;
768               }
769             [charset release];
770           }
771         [scriptToFamilies setObject: families forKey: script];
772       }
774     if (NSFONT_TRACE)
775       NSLog(@"    returning %lu families", (unsigned long)[families count]);
776     return families;
779 /* GNUstep font matching is very mediocre (it can't even compare
780    symbolic styles correctly), which is why our own font matching
781    mechanism must be implemented.  */
783 /* Implementation for list and match.  */
784 static Lisp_Object
785 ns_findfonts (Lisp_Object font_spec, BOOL isMatch)
787   Lisp_Object tem, list = Qnil;
788   NSFontDescriptor *fdesc;
789   NSArray *all_descs;
790   GSFontEnumerator *enumerator = [GSFontEnumerator sharedEnumerator];
792   NSSet *cFamilies;
794   block_input ();
795   if (NSFONT_TRACE)
796     {
797       fprintf (stderr, "nsfont: %s for fontspec:\n    ",
798                (isMatch ? "match" : "list"));
799       debug_print (font_spec);
800     }
802   cFamilies = ns_get_covering_families (ns_get_req_script (font_spec), 0.90);
804   fdesc = ns_spec_to_descriptor (font_spec);
805   all_descs = [enumerator availableFontDescriptors];
807   for (NSFontDescriptor *desc in all_descs)
808     {
809       if (![cFamilies containsObject:
810                   [desc objectForKey: NSFontFamilyAttribute]])
811         continue;
812       if (!ns_font_descs_match_p (fdesc, desc))
813         continue;
815       tem = ns_descriptor_to_entity (desc,
816                                      AREF (font_spec, FONT_EXTRA_INDEX),
817                                      NULL);
818       if (isMatch)
819         return tem;
820       list = Fcons (tem, list);
821     }
823   unblock_input ();
825   /* Return something if was a match and nothing found.  */
826   if (isMatch)
827     return ns_fallback_entity ();
829   if (NSFONT_TRACE)
830     fprintf (stderr, "    Returning %"pD"d entities.\n",
831              list_length (list));
833   return list;
838 /* ==========================================================================
840     Font driver implementation
842    ========================================================================== */
845 /* Return a cache of font-entities on FRAME.  The cache must be a
846    cons whose cdr part is the actual cache area.  */
847 static Lisp_Object
848 nsfont_get_cache (struct frame *frame)
850   Display_Info *dpyinfo = FRAME_DISPLAY_INFO (frame);
851   return (dpyinfo->name_list_element);
855 /* List fonts exactly matching with FONT_SPEC on FRAME.  The value is a
856    **list** of font-entities.  This and match () are sole APIs that allocate
857    font-entities.  Properties to be considered (2009/05/19) are:
858    regular: foundry, family, adstyle, registry
859    extended: script, lang, otf
860   "Extended" properties are not part of the vector but get stored as
861    lisp properties under FONT_EXTRA_INDEX.
863    The returned entities should have type set (to 'ns), plus the following:
864    foundry, family, adstyle, registry,
865    weight, slant, width, size (0 if scalable),
866    dpi, spacing, avgwidth (0 if scalable)  */
867 static Lisp_Object
868 nsfont_list (struct frame *f, Lisp_Object font_spec)
870   return ns_findfonts (font_spec, NO);
874 /* Return a font entity most closely matching with FONT_SPEC on
875    FRAME.  The closeness is determined by the font backend, thus
876    `face-font-selection-order' is ignored here.
877    Properties to be considered are same as for list().  */
878 static Lisp_Object
879 nsfont_match (struct frame *f, Lisp_Object font_spec)
881   return ns_findfonts (font_spec, YES);
885 /* List available families.  The value is a list of family names
886    (symbols).  */
887 static Lisp_Object
888 nsfont_list_family (struct frame *f)
890   Lisp_Object list = Qnil;
891   NSEnumerator *families;
892   NSString *family;
894   block_input ();
895   families = [[[NSFontManager sharedFontManager] availableFontFamilies]
896                objectEnumerator];
897   while ((family = [families nextObject]))
898       list = Fcons (intern ([family UTF8String]), list);
900   if (NSFONT_TRACE)
901     fprintf (stderr, "nsfont: list families returning %"pD"d entries\n",
902              list_length (list));
904   unblock_input ();
905   return list;
909 /* Open a font specified by FONT_ENTITY on frame F.  If the font is
910    scalable, open it with PIXEL_SIZE.  */
911 static Lisp_Object
912 nsfont_open (struct frame *f, Lisp_Object font_entity, int pixel_size)
914   struct nsfont_info *font_info;
915   struct font *font;
916   NSFontDescriptor *fontDesc = ns_spec_to_descriptor (font_entity);
917   NSFontManager *fontMgr = [NSFontManager sharedFontManager];
918   NSString *family;
919   NSFont *nsfont, *sfont;
920   NSRect brect;
921   Lisp_Object font_object;
922   Lisp_Object tem;
924   block_input ();
926   if (NSFONT_TRACE)
927     {
928       fprintf (stderr, "nsfont: open size %d of fontentity:\n    ", pixel_size);
929       debug_print (font_entity);
930     }
932   if (pixel_size <= 0)
933     {
934       /* try to get it out of frame params */
935       tem = get_frame_param (f, Qfontsize);
936       pixel_size = NILP (tem) ? 0 : XFIXNAT (tem);
937     }
939   tem = AREF (font_entity, FONT_ADSTYLE_INDEX);
940   family = ns_get_family (font_entity);
941   if (family == nil)
942     family = [[NSFont userFixedPitchFontOfSize: 0] familyName];
944   nsfont = [NSFont fontWithDescriptor: fontDesc
945                                  size: pixel_size];
947   if (nsfont == nil)
948     nsfont = [NSFont userFixedPitchFontOfSize: pixel_size];
950   if (NSFONT_TRACE)
951     NSLog (@"%@\n", nsfont);
953   font_object = font_make_object (VECSIZE (struct nsfont_info),
954                                   font_entity, pixel_size);
955   ASET (font_object, FONT_TYPE_INDEX, Qns);
956   font_info = (struct nsfont_info *) XFONT_OBJECT (font_object);
957   font = (struct font *) font_info;
958   if (!font)
959     {
960       unblock_input ();
961       return Qnil;
962     }
964   font_info->glyphs = xzalloc (0x100 * sizeof *font_info->glyphs);
965   font_info->metrics = xzalloc (0x100 * sizeof *font_info->metrics);
967   /* for metrics */
968   sfont = [nsfont screenFont];
970   if (sfont == nil)
971     sfont = nsfont;
973   /* non-metric backend font struct fields */
974   font = (struct font *) font_info;
975   font->pixel_size = [sfont pointSize];
976   font->driver = &nsfont_driver;
977   font->encoding_charset = -1;
978   font->repertory_charset = -1;
979   font->default_ascent = 0;
980   font->vertical_centering = 0;
981   font->baseline_offset = 0;
982   font->relative_compose = 0;
984   {
985     const char *fontName = [[nsfont fontName] UTF8String];
987     /* The values specified by fonts are not always exact. For
988      * example, a 6x8 font could specify that the descender is
989      * -2.00000405... (represented by 0xc000000220000000).  Without
990      * adjustment, the code below would round the descender to -3,
991      * resulting in a font that would be one pixel higher than
992      * intended.  */
993     CGFloat adjusted_descender = [sfont descender] + 0.0001;
995     font_info->nsfont = sfont;
996     [font_info->nsfont retain];
998     /* set up ns_font (defined in nsgui.h) */
999     font_info->name = xstrdup (fontName);
1000     font_info->bold = [fontMgr traitsOfFont: nsfont] & NSBoldFontMask;
1001     font_info->ital =
1002       ([fontMgr traitsOfFont: nsfont] & NSItalicFontMask);
1004     /* Metrics etc.; some fonts return an unusually large max advance, so we
1005        only use it for fonts that have wide characters.  */
1006     font_info->width = ([sfont numberOfGlyphs] > 2000) ?
1007       [sfont maximumAdvancement].width : ns_char_width (sfont, '0');
1009     brect =  [sfont boundingRectForFont];
1011     font_info->underpos = [sfont underlinePosition];
1012     font_info->underwidth = [sfont underlineThickness];
1013     font_info->size = font->pixel_size;
1015     /* max bounds */
1016     font->ascent = font_info->max_bounds.ascent = lrint ([sfont ascender]);
1017     /* Descender is usually negative.  Use floor to avoid
1018        clipping descenders.  */
1019     font->descent =
1020       font_info->max_bounds.descent = -lrint (floor(adjusted_descender));
1021     font_info->height =
1022       font_info->max_bounds.ascent + font_info->max_bounds.descent;
1023     font_info->max_bounds.width = lrint (font_info->width);
1024     font_info->max_bounds.lbearing = lrint (brect.origin.x);
1025     font_info->max_bounds.rbearing =
1026       lrint (brect.size.width - (CGFloat) font_info->width);
1028     /* set up metrics portion of font struct */
1029     font->space_width = lrint (ns_char_width (sfont, ' '));
1030     font->max_width = lrint (font_info->max_bounds.width);
1031     font->min_width = font->space_width;  /* Approximate.  */
1032     font->average_width = ns_ascii_average_width (sfont);
1034     font->height = lrint (font_info->height);
1035     font->underline_position = lrint (font_info->underpos);
1036     font->underline_thickness = lrint (font_info->underwidth);
1038     font->props[FONT_NAME_INDEX] = Ffont_xlfd_name (font_object, Qnil, Qnil);
1039     font->props[FONT_FULLNAME_INDEX] = build_unibyte_string (font_info->name);
1040   }
1041   unblock_input ();
1043   return font_object;
1047 /* Close FONT.  */
1048 static void
1049 nsfont_close (struct font *font)
1051   struct nsfont_info *font_info = (struct nsfont_info *) font;
1053   /* FIXME: font_info may be NULL due to same failure to detect
1054      same font that causes need for cache in nsfont_open.  */
1055   if (font_info && font_info->name)
1056     {
1057       int i;
1059       for (i = 0; i < 0x100; i++)
1060         {
1061           xfree (font_info->glyphs[i]);
1062           xfree (font_info->metrics[i]);
1063         }
1064       xfree (font_info->glyphs);
1065       xfree (font_info->metrics);
1066       [font_info->nsfont release];
1067       xfree (font_info->name);
1068       font_info->name = NULL;
1069     }
1073 /* If FONT_ENTITY has a glyph for character C (Unicode code point),
1074    return 1.  If not, return 0.  If a font must be opened to check
1075    it, return -1.  */
1076 static int
1077 nsfont_has_char (Lisp_Object entity, int c)
1079   return -1;
1083 /* Return a glyph code of FONT for character C (Unicode code point).
1084    If FONT doesn't have such a glyph, return FONT_INVALID_CODE.  */
1085 static unsigned int
1086 nsfont_encode_char (struct font *font, int c)
1088   struct nsfont_info *font_info = (struct nsfont_info *)font;
1089   unsigned char high = (c & 0xff00) >> 8, low = c & 0x00ff;
1090   unsigned int g;
1092   if (c > 0xFFFF)
1093     return FONT_INVALID_CODE;
1095   /* Did we already cache this block?  */
1096   if (!font_info->glyphs[high])
1097     ns_uni_to_glyphs (font_info, high);
1099   g = font_info->glyphs[high][low];
1100   return g == INVALID_GLYPH ? FONT_INVALID_CODE : g;
1104 /* Perform the size computation of glyphs of FONT and fill in members
1105    of METRICS.  The glyphs are specified by their glyph codes in
1106    CODE (length NGLYPHS).  */
1107 static void
1108 nsfont_text_extents (struct font *font, const unsigned int *code,
1109                      int nglyphs, struct font_metrics *metrics)
1111   struct nsfont_info *font_info = (struct nsfont_info *)font;
1112   struct font_metrics *pcm;
1113   unsigned char high, low;
1114   int totalWidth = 0;
1115   int i;
1117   memset (metrics, 0, sizeof (struct font_metrics));
1119   for (i = 0; i < nglyphs; i++)
1120     {
1121       /* get metrics for this glyph, filling cache if need be */
1122       /* TODO: get metrics for whole string from an NSLayoutManager
1123                (if not too slow) */
1124       high = (code[i] & 0xFF00) >> 8;
1125       low = code[i] & 0x00FF;
1126       if (!font_info->metrics[high])
1127         ns_glyph_metrics (font_info, high);
1128       pcm = &(font_info->metrics[high][low]);
1130       if (metrics->lbearing > totalWidth + pcm->lbearing)
1131         metrics->lbearing = totalWidth + pcm->lbearing;
1132       if (metrics->rbearing < totalWidth + pcm->rbearing)
1133         metrics->rbearing = totalWidth + pcm->rbearing;
1134       if (metrics->ascent < pcm->ascent)
1135         metrics->ascent = pcm->ascent;
1136       if (metrics->descent < pcm->descent)
1137         metrics->descent = pcm->descent;
1139       totalWidth += pcm->width;
1140     }
1142   metrics->width = totalWidth;
1146 /* Draw glyphs between FROM and TO of S->char2b at (X Y) pixel
1147    position of frame F with S->FACE and S->GC.  If WITH_BACKGROUND,
1148    fill the background in advance.  It is assured that WITH_BACKGROUND
1149    is false when (FROM > 0 || TO < S->nchars).  */
1150 static int
1151 nsfont_draw (struct glyph_string *s, int from, int to, int x, int y,
1152              bool with_background)
1154   NSGlyph *c = alloca ((to - from) * sizeof *c);
1156   struct face *face;
1157   NSRect r;
1158   struct nsfont_info *font;
1159   NSColor *col;
1160   int len = to - from;
1161   char isComposite = s->first_glyph->type == COMPOSITE_GLYPH;
1163   block_input ();
1165   font = (struct nsfont_info *) s->font;
1166   if (font == NULL)
1167     font = (struct nsfont_info *)FRAME_FONT (s->f);
1169   face = s->face;
1171   r.origin.x = x;
1172   r.origin.y = y;
1173   r.size.height = FONT_HEIGHT (font);
1175   for (int i = 0; i < len; ++i)
1176     c[i] = s->char2b[i + from];
1178   /* Fill background if requested.  */
1179   if (with_background && !isComposite)
1180     {
1181       NSRect br = NSMakeRect (x, y - FONT_BASE (s->font),
1182                               s->width, FONT_HEIGHT (s->font));
1183       if (s->hl != DRAW_CURSOR)
1184         [(NS_FACE_BACKGROUND (face) != 0
1185           ? [NSColor colorWithUnsignedLong:NS_FACE_BACKGROUND (face)]
1186           : FRAME_BACKGROUND_COLOR (s->f)) set];
1187       else
1188         [FRAME_CURSOR_COLOR (s->f) set];
1189       NSRectFill (br);
1190     }
1192   /* set up for character rendering */
1193   if (s->hl == DRAW_CURSOR)
1194     col = FRAME_BACKGROUND_COLOR (s->f);
1195   else
1196     col = (NS_FACE_FOREGROUND (face) != 0
1197            ? [NSColor colorWithUnsignedLong:NS_FACE_FOREGROUND (face)]
1198            : FRAME_FOREGROUND_COLOR (s->f));
1200   /* render under GNUstep using DPS */
1201   {
1202     NSGraphicsContext *context = [NSGraphicsContext currentContext];
1203     [font->nsfont set];
1204     [col set];
1205     DPSmoveto (context, r.origin.x, r.origin.y);
1206     GSShowGlyphs (context, c, len);
1207   }
1209   unblock_input ();
1210   return to-from;
1213 static NSUInteger
1214 ns_font_shape (NSFont *font, NSString *string,
1215                struct ns_glyph_layout *glyph_layouts, NSUInteger glyph_len,
1216                enum lgstring_direction dir)
1218   NSUInteger i;
1219   NSUInteger result = 0;
1220   NSTextStorage *textStorage;
1221   NSLayoutManager *layoutManager;
1222   NSTextContainer *textContainer;
1223   NSUInteger stringLength;
1224   NSPoint spaceLocation;
1225   /* numberOfGlyphs can't actually be 0, but this pacifies GCC */
1226   NSUInteger used, numberOfGlyphs = 0;
1228   textStorage = [[NSTextStorage alloc] initWithString:string];
1229   layoutManager = [[NSLayoutManager alloc] init];
1230   textContainer = [[NSTextContainer alloc] init];
1232   /* Append a trailing space to measure baseline position.  */
1233   [textStorage appendAttributedString:([[[NSAttributedString alloc]
1234                                           initWithString:@" "] autorelease])];
1235   [textStorage setFont:font];
1236   [textContainer setLineFragmentPadding:0];
1238   [layoutManager addTextContainer:textContainer];
1239   [textContainer release];
1240   [textStorage addLayoutManager:layoutManager];
1241   [layoutManager release];
1243   if (!(textStorage && layoutManager && textContainer))
1244     emacs_abort ();
1246   stringLength = [string length];
1248   /* Force layout.  */
1249   (void) [layoutManager glyphRangeForTextContainer:textContainer];
1251   spaceLocation = [layoutManager locationForGlyphAtIndex:stringLength];
1253   /* Remove the appended trailing space because otherwise it may
1254      generate a wrong result for a right-to-left text.  */
1255   [textStorage beginEditing];
1256   [textStorage deleteCharactersInRange:(NSMakeRange (stringLength, 1))];
1257   [textStorage endEditing];
1258   (void) [layoutManager glyphRangeForTextContainer:textContainer];
1260   i = 0;
1261   while (i < stringLength)
1262     {
1263       NSRange range;
1264       NSFont *fontInTextStorage =
1265         [textStorage attribute: NSFontAttributeName
1266                        atIndex:i
1267                      longestEffectiveRange: &range
1268                        inRange: NSMakeRange (0, stringLength)];
1270       if (!(fontInTextStorage == font
1271             || [[fontInTextStorage fontName] isEqualToString:[font fontName]]))
1272         break;
1273       i = NSMaxRange (range);
1274     }
1275   if (i < stringLength)
1276     /* Make the test `used <= glyph_len' below fail if textStorage
1277        contained some fonts other than the specified one.  */
1278     used = glyph_len + 1;
1279   else
1280     {
1281       NSRange range = NSMakeRange (0, stringLength);
1283       range = [layoutManager glyphRangeForCharacterRange:range
1284                                     actualCharacterRange:NULL];
1285       numberOfGlyphs = NSMaxRange (range);
1286       used = numberOfGlyphs;
1287       for (i = 0; i < numberOfGlyphs; i++)
1288         if ([layoutManager notShownAttributeForGlyphAtIndex:i])
1289           used--;
1290     }
1292   if (0 < used && used <= glyph_len)
1293     {
1294       NSUInteger glyphIndex, prevGlyphIndex;
1295       NSUInteger *permutation;
1296       NSRange compRange, range;
1297       CGFloat totalAdvance;
1299       glyphIndex = 0;
1300       while ([layoutManager notShownAttributeForGlyphAtIndex:glyphIndex])
1301         glyphIndex++;
1303       permutation = NULL;
1304 #define RIGHT_TO_LEFT_P permutation
1306       /* Fill the `comp_range' member of struct mac_glyph_layout, and
1307          setup a permutation for right-to-left text.  */
1308       compRange = NSMakeRange (0, 0);
1309       for (range = NSMakeRange (0, 0); NSMaxRange (range) < used;
1310            range.length++)
1311         {
1312           struct ns_glyph_layout *gl = glyph_layouts + NSMaxRange (range);
1313           NSUInteger characterIndex =
1314             [layoutManager characterIndexForGlyphAtIndex:glyphIndex];
1316           gl->string_index = characterIndex;
1318           if (characterIndex >= NSMaxRange (compRange))
1319             {
1320               compRange.location = NSMaxRange (compRange);
1321               do
1322                 {
1323                   NSRange characterRange =
1324                     [string
1325                       rangeOfComposedCharacterSequenceAtIndex:characterIndex];
1327                   compRange.length =
1328                     NSMaxRange (characterRange) - compRange.location;
1329                   [layoutManager glyphRangeForCharacterRange:compRange
1330                                         actualCharacterRange:&characterRange];
1331                   characterIndex = NSMaxRange (characterRange) - 1;
1332                 }
1333               while (characterIndex >= NSMaxRange (compRange));
1335               if (RIGHT_TO_LEFT_P)
1336                 for (i = 0; i < range.length; i++)
1337                   permutation[range.location + i] = NSMaxRange (range) - i - 1;
1339               range = NSMakeRange (NSMaxRange (range), 0);
1340             }
1342           gl->comp_range.location = compRange.location;
1343           gl->comp_range.length = compRange.length;
1345           while (++glyphIndex < numberOfGlyphs)
1346             if (![layoutManager notShownAttributeForGlyphAtIndex:glyphIndex])
1347               break;
1348         }
1349       if (RIGHT_TO_LEFT_P)
1350         for (i = 0; i < range.length; i++)
1351           permutation[range.location + i] = NSMaxRange (range) - i - 1;
1353       /* Then fill the remaining members.  */
1354       glyphIndex = prevGlyphIndex = 0;
1355       while ([layoutManager notShownAttributeForGlyphAtIndex:glyphIndex])
1356         glyphIndex++;
1358       if (!RIGHT_TO_LEFT_P)
1359         totalAdvance = 0;
1360       else
1361         {
1362           NSUInteger nrects;
1363           NSRect *glyphRects =
1364             [layoutManager
1365               rectArrayForGlyphRange:(NSMakeRange (0, numberOfGlyphs))
1366               withinSelectedGlyphRange:(NSMakeRange (NSNotFound, 0))
1367                      inTextContainer:textContainer rectCount:&nrects];
1369           totalAdvance = NSMaxX (glyphRects[0]);
1370         }
1372       for (i = 0; i < used; i++)
1373         {
1374           struct ns_glyph_layout *gl;
1375           NSPoint location;
1376           NSUInteger nextGlyphIndex;
1377           NSRange glyphRange;
1378           NSRect *glyphRects;
1379           NSUInteger nrects;
1381           if (!RIGHT_TO_LEFT_P)
1382             gl = glyph_layouts + i;
1383           else
1384             {
1385               NSUInteger dest = permutation[i];
1387               gl = glyph_layouts + dest;
1388               if (i < dest)
1389                 {
1390                   NSUInteger tmp = gl->string_index;
1392                   gl->string_index = glyph_layouts[i].string_index;
1393                   glyph_layouts[i].string_index = tmp;
1394                 }
1395             }
1396           gl->glyph_id = [layoutManager glyphAtIndex: glyphIndex];
1398           location = [layoutManager locationForGlyphAtIndex:glyphIndex];
1399           gl->baseline_delta = spaceLocation.y - location.y;
1401           for (nextGlyphIndex = glyphIndex + 1; nextGlyphIndex < numberOfGlyphs;
1402                nextGlyphIndex++)
1403             if (![layoutManager
1404                    notShownAttributeForGlyphAtIndex:nextGlyphIndex])
1405               break;
1407           if (!RIGHT_TO_LEFT_P)
1408             {
1409               CGFloat maxX;
1411               if (prevGlyphIndex == 0)
1412                 glyphRange = NSMakeRange (0, nextGlyphIndex);
1413               else
1414                 glyphRange = NSMakeRange (glyphIndex,
1415                                           nextGlyphIndex - glyphIndex);
1416               glyphRects =
1417                 [layoutManager
1418                   rectArrayForGlyphRange:glyphRange
1419                   withinSelectedGlyphRange:(NSMakeRange (NSNotFound, 0))
1420                          inTextContainer:textContainer rectCount:&nrects];
1421               maxX = max (NSMaxX (glyphRects[0]), totalAdvance);
1422               gl->advance_delta = location.x - totalAdvance;
1423               gl->advance = maxX - totalAdvance;
1424               totalAdvance = maxX;
1425             }
1426           else
1427             {
1428               CGFloat minX;
1430               if (nextGlyphIndex == numberOfGlyphs)
1431                 glyphRange = NSMakeRange (prevGlyphIndex,
1432                                           numberOfGlyphs - prevGlyphIndex);
1433               else
1434                 glyphRange = NSMakeRange (prevGlyphIndex,
1435                                           glyphIndex + 1 - prevGlyphIndex);
1436               glyphRects =
1437                 [layoutManager
1438                   rectArrayForGlyphRange:glyphRange
1439                   withinSelectedGlyphRange:(NSMakeRange (NSNotFound, 0))
1440                          inTextContainer:textContainer rectCount:&nrects];
1441               minX = min (NSMinX (glyphRects[0]), totalAdvance);
1442               gl->advance = totalAdvance - minX;
1443               totalAdvance = minX;
1444               gl->advance_delta = location.x - totalAdvance;
1445             }
1447           prevGlyphIndex = glyphIndex + 1;
1448           glyphIndex = nextGlyphIndex;
1449         }
1451       if (RIGHT_TO_LEFT_P)
1452         xfree (permutation);
1454 #undef RIGHT_TO_LEFT_P
1456       result = used;
1457     }
1458   [textStorage release];
1460   return result;
1463 static Lisp_Object
1464 nsfont_shape (Lisp_Object lgstring, Lisp_Object direction)
1466   struct font *font = CHECK_FONT_GET_OBJECT (LGSTRING_FONT (lgstring));
1467   struct nsfont_info *font_info = (struct nsfont_info *) font;
1468   struct ns_glyph_layout *glyph_layouts;
1469   NSFont *nsfont = font_info->nsfont;
1470   ptrdiff_t glyph_len, len, i;
1471   Lisp_Object tem;
1472   unichar *mb_buf;
1473   NSUInteger used;
1475   glyph_len = LGSTRING_GLYPH_LEN (lgstring);
1476   for (i = 0; i < glyph_len; ++i)
1477     {
1478       tem = LGSTRING_GLYPH (lgstring, i);
1480       if (NILP (tem))
1481         break;
1482     }
1484   len = i;
1486   if (INT_MAX / 2 < len)
1487     memory_full (SIZE_MAX);
1489   block_input ();
1491   mb_buf = alloca (len * sizeof *mb_buf);
1493   for (i = 0; i < len; ++i)
1494     {
1495       uint32_t c = LGLYPH_CHAR (LGSTRING_GLYPH (lgstring, i));
1496       mb_buf[i] = (unichar) c;
1497     }
1499   NSString *string = [NSString stringWithCharacters: mb_buf
1500                                              length: len];
1501   unblock_input ();
1503   if (!string)
1504     return Qnil;
1506   block_input ();
1508   enum lgstring_direction dir = DIR_UNKNOWN;
1510   if (EQ (direction, QL2R))
1511     dir = DIR_L2R;
1512   else if (EQ (direction, QR2L))
1513     dir = DIR_R2L;
1514   glyph_layouts = alloca (sizeof (struct ns_glyph_layout) * glyph_len);
1515   used = ns_font_shape (nsfont, string, glyph_layouts, glyph_len, dir);
1517   for (i = 0; i < used; i++)
1518     {
1519       Lisp_Object lglyph = LGSTRING_GLYPH (lgstring, i);
1520       struct ns_glyph_layout *gl = glyph_layouts + i;
1521       EMACS_INT from, to;
1522       struct font_metrics metrics;
1524       if (NILP (lglyph))
1525         {
1526           lglyph = LGLYPH_NEW ();
1527           LGSTRING_SET_GLYPH (lgstring, i, lglyph);
1528         }
1530       from = gl->comp_range.location;
1531       LGLYPH_SET_FROM (lglyph, from);
1533       to = gl->comp_range.location + gl->comp_range.length;
1534       LGLYPH_SET_TO (lglyph, to - 1);
1536       /* LGLYPH_CHAR is used in `describe-char' for checking whether
1537          the composition is trivial.  */
1538       {
1539         UTF32Char c;
1541         if (mb_buf[gl->string_index] >= 0xD800
1542             && mb_buf[gl->string_index] < 0xDC00)
1543           c = (((mb_buf[gl->string_index] - 0xD800) << 10)
1544                + (mb_buf[gl->string_index + 1] - 0xDC00) + 0x10000);
1545         else
1546           c = mb_buf[gl->string_index];
1548         LGLYPH_SET_CHAR (lglyph, c);
1549       }
1551       {
1552         unsigned long cc = gl->glyph_id;
1553         LGLYPH_SET_CODE (lglyph, cc);
1554       }
1556       nsfont_text_extents (font, &gl->glyph_id, 1, &metrics);
1557       LGLYPH_SET_WIDTH (lglyph, metrics.width);
1558       LGLYPH_SET_LBEARING (lglyph, metrics.lbearing);
1559       LGLYPH_SET_RBEARING (lglyph, metrics.rbearing);
1560       LGLYPH_SET_ASCENT (lglyph, metrics.ascent);
1561       LGLYPH_SET_DESCENT (lglyph, metrics.descent);
1562     }
1563   unblock_input ();
1565   return make_fixnum (used);
1569 /* ==========================================================================
1571     Font glyph and metrics caching functions
1573    ========================================================================== */
1575 static NSGlyph
1576 ns_uni_to_glyphs_1 (struct nsfont_info *info, unsigned int c)
1578   unichar characters[] = { c };
1579   NSString *string =
1580     [NSString stringWithCharacters: characters
1581                             length: 1];
1582   NSDictionary *attributes =
1583     [NSDictionary dictionaryWithObjectsAndKeys:
1584                     info->nsfont, NSFontAttributeName, nil];
1585   NSTextStorage *storage = [[NSTextStorage alloc] initWithString: string
1586                                                       attributes: attributes];
1587   NSTextContainer *text_container = [[NSTextContainer alloc] init];
1588   NSLayoutManager *manager = [[NSLayoutManager alloc] init];
1590   [manager addTextContainer: text_container];
1591   [text_container release]; /* Retained by manager */
1592   [storage addLayoutManager: manager];
1593   [manager release]; /* Retained by storage */
1595   NSFont *font_in_storage = [storage attribute: NSFontAttributeName
1596                                        atIndex:0
1597                                 effectiveRange: NULL];
1598   NSGlyph glyph = FONT_INVALID_CODE;
1600   if ((font_in_storage == info->nsfont
1601        || [[font_in_storage fontName] isEqualToString: [info->nsfont fontName]]))
1602     {
1603       @try
1604         {
1605           glyph = [manager glyphAtIndex: 0];
1606         }
1607       @catch (NSException *e)
1608         {
1609           /* GNUstep bug? */
1610           glyph = 'X';
1611         }
1612     }
1614   [storage release];
1616   return glyph;
1619 /* Find and cache corresponding glyph codes for unicode values in given
1620    hi-byte block of 256.  */
1621 static void
1622 ns_uni_to_glyphs (struct nsfont_info *font_info, unsigned char block)
1624   unichar *unichars = xmalloc (0x101 * sizeof (unichar));
1625   unsigned int i, g, idx;
1626   unsigned int *glyphs;
1628   if (NSFONT_TRACE)
1629     fprintf (stderr, "%p\tFinding glyphs for glyphs in block %d\n",
1630             font_info, block);
1632   block_input ();
1634   font_info->glyphs[block] = xmalloc (0x100 * sizeof (unsigned int));
1635   if (!unichars || !(font_info->glyphs[block]))
1636     emacs_abort ();
1638   /* Create a string containing all Unicode characters in this block.  */
1639   for (idx = block<<8, i = 0; i < 0x100; idx++, i++)
1640     if (idx < 0xD800 || idx > 0xDFFF)
1641       unichars[i] = idx;
1642     else
1643       unichars[i] = 0xFEFF;
1644   unichars[0x100] = 0;
1646   {
1647     glyphs = font_info->glyphs[block];
1648     for (i = 0; i < 0x100; i++, glyphs++)
1649       {
1650         g = unichars[i];
1651         NSGlyph glyph = ns_uni_to_glyphs_1 (font_info, g);
1652         *glyphs = glyph;
1653       }
1654   }
1656   unblock_input ();
1657   xfree (unichars);
1661 /* Determine and cache metrics for glyphs in given hi-byte block of
1662    256.  */
1663 static void
1664 ns_glyph_metrics (struct nsfont_info *font_info, unsigned int block)
1666   unsigned int i;
1667   NSGlyph g;
1668   unsigned int numGlyphs = [font_info->nsfont numberOfGlyphs];
1669   NSFont *sfont;
1670   struct font_metrics *metrics;
1672   if (NSFONT_TRACE)
1673     fprintf (stderr, "%p\tComputing metrics for glyphs in block %u\n",
1674             font_info, block);
1676   /* not implemented yet (as of startup 0.18), so punt */
1677   if (numGlyphs == 0)
1678     numGlyphs = 0x10000;
1680   block_input ();
1681   sfont = [font_info->nsfont screenFont];
1683   font_info->metrics[block] = xzalloc (0x100 * sizeof (struct font_metrics));
1684   if (!(font_info->metrics[block]))
1685     emacs_abort ();
1687   metrics = font_info->metrics[block];
1688   for (g = block<<8, i =0; i<0x100 && g < numGlyphs; g++, i++, metrics++)
1689     {
1690       CGFloat w, lb, rb;
1691       NSRect r = [sfont boundingRectForGlyph: g];
1693       w = max ([sfont advancementForGlyph: g].width, 2.0);
1694       metrics->width = lrint (w);
1696       lb = NSMinX (r);
1697       rb = NSMaxX (r);
1699       metrics->rbearing = lrint (rb);
1700       metrics->lbearing = lrint (lb);
1702       metrics->descent = - NSMaxY (r);
1703       metrics->ascent = - NSMinY (r);
1704     }
1705   unblock_input ();
1709 /* Debugging */
1710 void
1711 ns_dump_glyphstring (struct glyph_string *s)
1713   int i;
1715   fprintf (stderr, ("Glyph string len = %d at (%d, %d) overhang (%d, %d),"
1716                     "overlap = %d, bg_filled = %d:"),
1717            s->nchars, s->x, s->y, s->left_overhang, s->right_overhang,
1718            s->row->overlapping_p, s->background_filled_p);
1719   for (i =0; i<s->nchars; i++)
1720     putc (s->first_glyph[i].u.ch, stderr);
1721   putc ('\n', stderr);
1724 static void syms_of_nsfont_for_pdumper (void);
1726 struct font_driver const nsfont_driver =
1727   {
1728   .type = LISPSYM_INITIALLY (Qns),
1729   .case_sensitive = true,
1730   .get_cache = nsfont_get_cache,
1731   .list = nsfont_list,
1732   .match = nsfont_match,
1733   .list_family = nsfont_list_family,
1734   .open_font = nsfont_open,
1735   .close_font = nsfont_close,
1736   .has_char = nsfont_has_char,
1737   .encode_char = nsfont_encode_char,
1738   .text_extents = nsfont_text_extents,
1739   .shape = nsfont_shape,
1740   .draw = nsfont_draw,
1741   };
1743 void
1744 syms_of_nsfont (void)
1746   DEFSYM (Qcondensed, "condensed");
1747   DEFSYM (Qmedium, "medium");
1749   DEFVAR_LISP ("ns-reg-to-script", Vns_reg_to_script,
1750     doc: /* Internal map of font registry to Unicode script.  */);
1751   Vns_reg_to_script = Qnil;
1753   pdumper_do_now_and_after_load (syms_of_nsfont_for_pdumper);
1755   /* Font slant styles.  */
1756   DEFSYM (Qreverse_italic, "reverse-italic");
1757   DEFSYM (Qreverse_oblique, "reverse-oblique");
1758   DEFSYM (Qexpanded, "expanded");
1761 static void
1762 syms_of_nsfont_for_pdumper (void)
1764   register_font_driver (&nsfont_driver, NULL);