1 /* Font back-end driver for the NeXT/Open/GNUstep and MacOSX window system.
3 Copyright (C) 2006, 2007, 2008, 2009, 2010 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. */
29 #include "dispextern.h"
30 #include "composite.h"
31 #include "blockinput.h"
38 #include "character.h"
41 /* TODO: Drop once we can assume gnustep-gui 0.17.1. */
42 #ifdef NS_IMPL_GNUSTEP
43 #import <AppKit/NSFontDescriptor.h>
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,
61 static void ns_glyph_metrics (struct nsfont_info *font_info,
65 /* ==========================================================================
69 ========================================================================== */
72 /* Replace spaces w/another character so emacs core font parsing routines
75 ns_escape_name (char *name)
77 int i =0, len =strlen (name);
84 /* Reconstruct spaces in a font family name passed through emacs. */
86 ns_unescape_name (char *name)
88 int i =0, len =strlen (name);
95 /* Extract family name from a font spec. */
97 ns_get_family (Lisp_Object font_spec)
99 Lisp_Object tem = AREF (font_spec, FONT_FAMILY_INDEX);
104 char *tmp = strdup (SDATA (SYMBOL_NAME (tem)));
106 ns_unescape_name (tmp);
107 family = [NSString stringWithUTF8String: tmp];
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. */
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);
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];
157 fdesc = [fdesc fontDescriptorWithFamily: family];
162 /* Converts NSFont descriptor to FONT_WEIGHT, FONT_SLANT, FONT_WIDTH, etc.. */
164 ns_descriptor_to_entity (NSFontDescriptor *desc,
168 Lisp_Object font_entity = font_make_entity ();
169 /* NSString *psName = [desc postscriptName]; */
170 NSString *family = [desc objectForKey: NSFontFamilyAttribute];
171 unsigned int traits = [desc symbolicTraits];
174 /* Shouldn't happen, but on Tiger fallback desc gets name but no family. */
176 family = [desc objectForKey: NSFontNameAttribute];
178 family = [[NSFont userFixedPitchFontOfSize: 0] familyName];
180 escapedFamily = strdup ([family UTF8String]);
181 ns_escape_name (escapedFamily);
183 ASET (font_entity, FONT_TYPE_INDEX, Qns);
184 ASET (font_entity, FONT_FOUNDRY_INDEX, Qapple);
185 ASET (font_entity, FONT_FAMILY_INDEX, intern (escapedFamily));
186 ASET (font_entity, FONT_ADSTYLE_INDEX, style ? intern (style) : Qnil);
187 ASET (font_entity, FONT_REGISTRY_INDEX, Qiso10646_1);
189 FONT_SET_STYLE (font_entity, FONT_WEIGHT_INDEX,
190 traits & NSFontBoldTrait ? Qbold : Qmedium);
191 /* FONT_SET_STYLE (font_entity, FONT_WEIGHT_INDEX,
192 make_number (100 + 100
193 * ns_attribute_fvalue (desc, NSFontWeightTrait)));*/
194 FONT_SET_STYLE (font_entity, FONT_SLANT_INDEX,
195 traits & NSFontItalicTrait ? Qitalic : Qnormal);
196 /* FONT_SET_STYLE (font_entity, FONT_SLANT_INDEX,
197 make_number (100 + 100
198 * ns_attribute_fvalue (desc, NSFontSlantTrait)));*/
199 FONT_SET_STYLE (font_entity, FONT_WIDTH_INDEX,
200 traits & NSFontCondensedTrait ? Qcondensed :
201 traits & NSFontExpandedTrait ? Qexpanded : Qnormal);
202 /* FONT_SET_STYLE (font_entity, FONT_WIDTH_INDEX,
203 make_number (100 + 100
204 * ns_attribute_fvalue (desc, NSFontWidthTrait)));*/
206 ASET (font_entity, FONT_SIZE_INDEX, make_number (0));
207 ASET (font_entity, FONT_AVGWIDTH_INDEX, make_number (0));
208 ASET (font_entity, FONT_SPACING_INDEX,
209 make_number([desc symbolicTraits] & NSFontMonoSpaceTrait
210 ? FONT_SPACING_MONO : FONT_SPACING_PROPORTIONAL));
212 ASET (font_entity, FONT_EXTRA_INDEX, extra);
213 ASET (font_entity, FONT_OBJLIST_INDEX, Qnil);
217 fprintf (stderr, "created font_entity:\n ");
218 debug_print (font_entity);
221 free (escapedFamily);
226 /* Default font entity. */
228 ns_fallback_entity (void)
230 return ns_descriptor_to_entity ([[NSFont userFixedPitchFontOfSize: 0]
231 fontDescriptor], Qnil, NULL);
235 /* Utility: get width of a char c in screen font sfont */
237 ns_char_width (NSFont *sfont, int c)
240 NSString *cstr = [NSString stringWithFormat: @"%c", c];
242 NSGlyph glyph = [sfont glyphWithName: cstr];
245 float w = [sfont advancementForGlyph: glyph].width;
251 NSDictionary *attrsDictionary =
252 [NSDictionary dictionaryWithObject: sfont forKey: NSFontAttributeName];
253 w = [cstr sizeWithAttributes: attrsDictionary].width;
259 /* Return whether set1 covers set2 to a reasonable extent given by pct.
260 We check, out of each 16 unicode char range containing chars in set2,
261 whether at least one character is present in set1.
262 This must be true for pct of the pairs to consider it covering. */
264 ns_charset_covers(NSCharacterSet *set1, NSCharacterSet *set2, float pct)
266 const unsigned short *bytes1 = [[set1 bitmapRepresentation] bytes];
267 const unsigned short *bytes2 = [[set2 bitmapRepresentation] bytes];
268 int i, off = 0, tot = 0;
270 for (i=0; i<4096; i++, bytes1++, bytes2++)
274 if (*bytes1 == 0) // *bytes1 & *bytes2 != *bytes2
277 //fprintf(stderr, "off = %d\ttot = %d\n", off,tot);
278 return (float)off / tot < 1.0 - pct;
282 /* Convert :lang property to a script. Use of :lang property by font backend
283 seems to be limited for now (2009/05) to ja, zh, and ko. */
285 *ns_lang_to_script (Lisp_Object lang)
287 if (!strcmp (SDATA (SYMBOL_NAME (lang)), "ja"))
289 /* NOTE: ja given for any hanzi that's also a kanji, but Chinese fonts
290 have more characters. */
291 else if (!strcmp (SDATA (SYMBOL_NAME (lang)), "zh"))
293 else if (!strcmp (SDATA (SYMBOL_NAME (lang)), "ko"))
300 /* Convert OTF 4-letter script code to emacs script name. (Why can't
301 everyone just use some standard unicode names for these?) */
303 *ns_otf_to_script (Lisp_Object otf)
305 Lisp_Object script = assq_no_quit (XCAR (otf), Votf_script_alist);
306 return CONSP (script)
307 ? [NSString stringWithUTF8String: SDATA (SYMBOL_NAME XCDR ((script)))]
312 /* Convert a font registry, such as */
314 *ns_registry_to_script (char *reg)
316 Lisp_Object script, r, rts = Vns_reg_to_script;
319 r = XCAR (XCAR (rts));
320 if (!strncmp(SDATA(r), reg, strlen(SDATA(r))))
322 script = XCDR (XCAR (rts));
323 return [NSString stringWithUTF8String: SDATA (SYMBOL_NAME (script))];
331 /* Searches the :script, :lang, and :otf extra-bundle properties of the spec,
332 plus registry regular property, for something that can be mapped to a
333 unicode script. Empty string returned if no script spec found. */
335 *ns_get_req_script (Lisp_Object font_spec)
337 Lisp_Object reg = AREF (font_spec, FONT_REGISTRY_INDEX);
338 Lisp_Object extra = AREF (font_spec, FONT_EXTRA_INDEX);
340 /* The extra-bundle properties have priority. */
341 for ( ; CONSP (extra); extra = XCDR (extra))
343 Lisp_Object tmp = XCAR (extra);
346 Lisp_Object key = XCAR (tmp), val = XCDR (tmp);
347 if (EQ (key, QCscript) && SYMBOLP (val))
348 return [NSString stringWithUTF8String:
349 SDATA (SYMBOL_NAME (val))];
350 if (EQ (key, QClang) && SYMBOLP (val))
351 return ns_lang_to_script (val);
352 if (EQ (key, QCotf) && CONSP (val) && SYMBOLP (XCAR (val)))
353 return ns_otf_to_script (val);
357 /* If we get here, check the charset portion of the registry. */
360 /* XXX: iso10646 is passed in for non-ascii latin-1 characters
361 (which causes box rendering if we don't treat it like iso8858-1)
362 but also for ascii (which causes unnecessary font substitution). */
364 if (EQ (reg, Qiso10646_1))
367 return ns_registry_to_script (SDATA (SYMBOL_NAME (reg)));
374 /* This small function is static in fontset.c. If it can be made public for
375 all ports, remove this, but otherwise it doesn't seem worth the ifdefs. */
377 accumulate_script_ranges (Lisp_Object arg, Lisp_Object range, Lisp_Object val)
379 if (EQ (XCAR (arg), val))
382 XSETCDR (arg, Fcons (Fcons (XCAR (range), XCDR (range)), XCDR (arg)));
384 XSETCDR (arg, Fcons (Fcons (range, range), XCDR (arg)));
389 /* Use the unicode range information in Vchar_script_table to convert a script
390 name into an NSCharacterSet. */
391 static NSCharacterSet
392 *ns_script_to_charset (NSString *scriptName)
394 NSMutableCharacterSet *charset = [NSMutableCharacterSet new];
395 Lisp_Object script = intern ([scriptName UTF8String]);
396 Lisp_Object script_list = XCHAR_TABLE (Vchar_script_table)->extras[0];
398 if (! NILP (Fmemq (script, script_list)))
400 Lisp_Object ranges, range_list;
402 ranges = Fcons (script, Qnil);
403 map_char_table (accumulate_script_ranges, Qnil, Vchar_script_table,
405 range_list = Fnreverse (XCDR (ranges));
406 if (! NILP (range_list))
408 for (; CONSP (range_list); range_list = XCDR (range_list))
410 int start = XINT (XCAR (XCAR (range_list)));
411 int end = XINT (XCDR (XCAR (range_list)));
413 debug_print (XCAR (range_list));
415 [charset addCharactersInRange:
416 NSMakeRange (start, end-start)];
424 /* Return an array of font families containing characters for the given
425 script, for the given coverage criterion, including at least LastResort.
426 Results are cached by script for faster access.
427 If none are found, we reduce the percentage and try again, until 5%.
428 This provides a font with at least some characters if such can be found.
429 We don't use isSupersetOfSet: because (a) it doesn't work on Tiger, and
430 (b) need approximate match as fonts covering full unicode ranges are rare. */
432 *ns_get_covering_families (NSString *script, float pct)
434 static NSMutableDictionary *scriptToFamilies = nil;
435 NSMutableSet *families;
438 NSLog(@"Request covering families for script: '%@'", script);
440 if (scriptToFamilies == nil)
441 scriptToFamilies = [[NSMutableDictionary alloc] init];
443 if ((families = [scriptToFamilies objectForKey: script]) == nil)
445 NSFontManager *fontMgr = [NSFontManager sharedFontManager];
446 NSArray *allFamilies = [fontMgr availableFontFamilies];
448 if ([script length] == 0)
449 families = [NSMutableSet setWithArray: allFamilies];
452 NSCharacterSet *charset = ns_script_to_charset (script);
454 families = [NSMutableSet setWithCapacity: 10];
457 NSEnumerator *allFamiliesEnum = [allFamilies objectEnumerator];
458 while (family = [allFamiliesEnum nextObject])
460 NSCharacterSet *fset = [[fontMgr fontWithFamily: family
461 traits: 0 weight: 5 size: 12.0] coveredCharacterSet];
462 /* Some fonts on OS X, maybe many on GNUstep, return nil. */
464 fset = [NSCharacterSet characterSetWithRange:
465 NSMakeRange (0, 127)];
466 if (ns_charset_covers(fset, charset, pct))
467 [families addObject: family];
470 if ([families count] > 0 || pct < 0.05)
475 if ([families count] == 0)
476 [families addObject: @"LastResort"];
478 [scriptToFamilies setObject: families forKey: script];
482 NSLog(@" returning %d families", [families count]);
487 /* Implementation for list() and match(). List() can return nil, match()
488 must return something. Strategy is to drop family name from attribute
489 matching set for match. */
491 ns_findfonts (Lisp_Object font_spec, BOOL isMatch)
493 Lisp_Object tem, list = Qnil;
494 NSFontDescriptor *fdesc, *desc;
496 NSArray *matchingDescs;
504 fprintf (stderr, "nsfont: %s for fontspec:\n ",
505 (isMatch ? "match" : "list"));
506 debug_print (font_spec);
509 cFamilies = ns_get_covering_families (ns_get_req_script (font_spec), 0.90);
511 fdesc = ns_spec_to_descriptor (font_spec);
512 fkeys = [NSMutableSet setWithArray: [[fdesc fontAttributes] allKeys]];
514 [fkeys removeObject: NSFontFamilyAttribute];
516 matchingDescs = [fdesc matchingFontDescriptorsWithMandatoryKeys: fkeys];
518 NSLog(@"Got desc %@ and found %d matching fonts from it: ", fdesc,
519 [matchingDescs count]);
521 for (dEnum = [matchingDescs objectEnumerator]; desc = [dEnum nextObject]; )
523 if (![cFamilies containsObject:
524 [desc objectForKey: NSFontFamilyAttribute]])
526 tem = ns_descriptor_to_entity (desc,
527 AREF (font_spec, FONT_EXTRA_INDEX),
531 list = Fcons (tem, list);
532 if (fabs (ns_attribute_fvalue (desc, NSFontSlantTrait)) > 0.05)
536 /* Add synthItal member if needed. */
537 family = [fdesc objectForKey: NSFontFamilyAttribute];
538 if (family != nil && !foundItal && XINT (Flength (list)) > 0)
540 NSFontDescriptor *sDesc = [[[NSFontDescriptor new]
541 fontDescriptorWithSymbolicTraits: NSFontItalicTrait]
542 fontDescriptorWithFamily: family];
543 list = Fcons (ns_descriptor_to_entity (sDesc,
544 AREF (font_spec, FONT_EXTRA_INDEX),
548 /* Return something if was a match and nothing found. */
550 return ns_fallback_entity ();
553 fprintf (stderr, " Returning %ld entities.\n",
554 (long) XINT (Flength (list)));
561 /* ==========================================================================
563 Font driver implementation
565 ========================================================================== */
568 static Lisp_Object nsfont_get_cache (FRAME_PTR frame);
569 static Lisp_Object nsfont_list (Lisp_Object frame, Lisp_Object font_spec);
570 static Lisp_Object nsfont_match (Lisp_Object frame, Lisp_Object font_spec);
571 static Lisp_Object nsfont_list_family (Lisp_Object frame);
572 static Lisp_Object nsfont_open (FRAME_PTR f, Lisp_Object font_entity,
574 static void nsfont_close (FRAME_PTR f, struct font *font);
575 static int nsfont_has_char (Lisp_Object entity, int c);
576 static unsigned int nsfont_encode_char (struct font *font, int c);
577 static int nsfont_text_extents (struct font *font, unsigned int *code,
578 int nglyphs, struct font_metrics *metrics);
579 static int nsfont_draw (struct glyph_string *s, int from, int to, int x, int y,
580 int with_background);
582 struct font_driver nsfont_driver =
585 1, /* case sensitive */
590 NULL, /*free_entity */
593 NULL, /* prepare_face */
594 NULL, /* done_face */
599 /* excluded: get_bitmap, free_bitmap, get_outline, free_outline,
600 anchor_point, otf_capability, otf_driver,
601 start_for_frame, end_for_frame, shape */
605 /* Return a cache of font-entities on FRAME. The cache must be a
606 cons whose cdr part is the actual cache area. */
608 nsfont_get_cache (FRAME_PTR frame)
610 Display_Info *dpyinfo = FRAME_NS_DISPLAY_INFO (frame);
611 return (dpyinfo->name_list_element);
615 /* List fonts exactly matching with FONT_SPEC on FRAME. The value is a
616 **list** of font-entities. This and match () are sole APIs that allocate
617 font-entities. Properties to be considered (2009/05/19) are:
618 regular: foundry, family, adstyle, registry
619 extended: script, lang, otf
620 "Extended" properties are not part of the vector but get stored as
621 lisp properties under FONT_EXTRA_INDEX.
623 The returned entities should have type set (to 'ns), plus the following:
624 foundry, family, adstyle, registry,
625 weight, slant, width, size (0 if scalable),
626 dpi, spacing, avgwidth (0 if scalable) */
628 nsfont_list (Lisp_Object frame, Lisp_Object font_spec)
630 return ns_findfonts (font_spec, NO);
634 /* Return a font entity most closely maching with FONT_SPEC on
635 FRAME. The closeness is determined by the font backend, thus
636 `face-font-selection-order' is ignored here.
637 Properties to be considered are same as for list(). */
639 nsfont_match (Lisp_Object frame, Lisp_Object font_spec)
641 return ns_findfonts(font_spec, YES);
645 /* List available families. The value is a list of family names
648 nsfont_list_family (Lisp_Object frame)
650 Lisp_Object list = Qnil;
651 NSEnumerator *families =
652 [[[NSFontManager sharedFontManager] availableFontFamilies]
655 while (family = [families nextObject])
656 list = Fcons (intern ([family UTF8String]), list);
657 /* FIXME: escape the name? */
660 fprintf (stderr, "nsfont: list families returning %ld entries\n",
661 (long) XINT (Flength (list)));
667 /* Open a font specified by FONT_ENTITY on frame F. If the font is
668 scalable, open it with PIXEL_SIZE. */
670 nsfont_open (FRAME_PTR f, Lisp_Object font_entity, int pixel_size)
673 unsigned int traits = 0;
674 struct nsfont_info *font_info;
676 NSFontDescriptor *fontDesc = ns_spec_to_descriptor (font_entity);
677 NSFontManager *fontMgr = [NSFontManager sharedFontManager];
679 NSFont *nsfont, *sfont;
682 Lisp_Object font_object;
685 static NSMutableDictionary *fontCache = nil;
688 /* 2008/03/08: The same font may end up being requested for different
689 entities, due to small differences in numeric values or other issues,
690 or for different copies of the same entity. Therefore we cache to
691 avoid creating multiple struct font objects (with metrics cache, etc.)
692 for the same NSFont object. */
693 if (fontCache == nil)
694 fontCache = [[NSMutableDictionary alloc] init];
698 fprintf (stderr, "nsfont: open size %d of fontentity:\n ", pixel_size);
699 debug_print (font_entity);
704 /* try to get it out of frame params */
705 Lisp_Object tem = get_frame_param (f, Qfontsize);
706 pixel_size = NILP (tem) ? 0 : XFASTINT (tem);
709 tem = AREF (font_entity, FONT_ADSTYLE_INDEX);
710 synthItal = !NILP (tem) && !strncmp ("synthItal", SDATA (SYMBOL_NAME (tem)),
712 family = ns_get_family (font_entity);
714 family = [[NSFont userFixedPitchFontOfSize: 0] familyName];
715 /* Should be > 0.23 as some font descriptors (e.g. Terminus) set to that
716 when setting family in ns_spec_to_descriptor(). */
717 if (ns_attribute_fvalue (fontDesc, NSFontWeightTrait) > 0.50)
718 traits |= NSBoldFontMask;
719 if (fabs (ns_attribute_fvalue (fontDesc, NSFontSlantTrait) > 0.05))
720 traits |= NSItalicFontMask;
722 /* see http://cocoadev.com/forums/comments.php?DiscussionID=74 */
723 fixLeopardBug = traits & NSBoldFontMask ? 10 : 5;
724 nsfont = [fontMgr fontWithFamily: family
725 traits: traits weight: fixLeopardBug
727 /* if didn't find, try synthetic italic */
728 if (nsfont == nil && synthItal)
730 nsfont = [fontMgr fontWithFamily: family
731 traits: traits & ~NSItalicFontMask
732 weight: fixLeopardBug size: pixel_size];
735 /* LastResort not really a family */
736 if (nsfont == nil && [@"LastResort" isEqualToString: family])
737 nsfont = [NSFont fontWithName: @"LastResort" size: pixel_size];
742 message_with_string ("*** Warning: font in family '%s' not found",
743 build_string ([family UTF8String]), 1);
744 nsfont = [NSFont userFixedPitchFontOfSize: pixel_size];
748 NSLog (@"%@\n", nsfont);
750 /* Check the cache */
751 cached = [fontCache objectForKey: nsfont];
752 if (cached != nil && !synthItal)
755 fprintf(stderr, "*** nsfont_open CACHE HIT!\n");
756 /* FIXME: Cast from (unsigned long) to Lisp_Object. */
757 XHASH (font_object) = [cached unsignedLongValue];
762 font_object = font_make_object (VECSIZE (struct nsfont_info),
763 font_entity, pixel_size);
765 [fontCache setObject: [NSNumber numberWithUnsignedLong:
766 (unsigned long) XHASH (font_object)]
770 font_info = (struct nsfont_info *) XFONT_OBJECT (font_object);
771 font = (struct font *) font_info;
773 return Qnil; /* FIXME: other terms do, but return Qnil causes segfault */
775 font_info->glyphs = (unsigned short **)
776 xmalloc (0x100 * sizeof (unsigned short *));
777 font_info->metrics = (struct font_metrics **)
778 xmalloc (0x100 * sizeof (struct font_metrics *));
779 if (!font_info->glyphs || !font_info->metrics)
781 memset (font_info->glyphs, 0, 0x100 * sizeof (unsigned short *));
782 memset (font_info->metrics, 0, 0x100 * sizeof (struct font_metrics *));
787 sfont = [nsfont screenFont];
791 /* non-metric backend font struct fields */
792 font = (struct font *) font_info;
793 font->pixel_size = [sfont pointSize];
794 font->driver = &nsfont_driver;
795 font->encoding_type = FONT_ENCODING_NOT_DECIDED;
796 font->encoding_charset = -1;
797 font->repertory_charset = -1;
798 font->default_ascent = 0;
799 font->vertical_centering = 0;
800 font->baseline_offset = 0;
801 font->relative_compose = 0;
802 font->font_encoder = NULL;
804 font->props[FONT_FORMAT_INDEX] = Qns;
805 font->props[FONT_FILE_INDEX] = Qnil;
808 double expand, hshrink;
809 float full_height, min_height, hd;
810 const char *fontName = [[nsfont fontName] UTF8String];
811 int len = strlen (fontName);
813 #ifdef NS_IMPL_GNUSTEP
814 font_info->nsfont = sfont;
816 font_info->nsfont = nsfont;
818 [font_info->nsfont retain];
820 /* set up ns_font (defined in nsgui.h) */
821 font_info->name = (char *)xmalloc (strlen (fontName)+1);
822 strcpy (font_info->name, fontName);
823 font_info->bold = [fontMgr traitsOfFont: nsfont] & NSBoldFontMask;
825 synthItal || ([fontMgr traitsOfFont: nsfont] & NSItalicFontMask);
827 /* Metrics etc.; some fonts return an unusually large max advance, so we
828 only use it for fonts that have wide characters. */
829 font_info->width = ([sfont numberOfGlyphs] > 2000) ?
830 [sfont maximumAdvancement].width : ns_char_width (sfont, '0');
832 brect = [sfont boundingRectForFont];
833 full_height = brect.size.height;
834 min_height = [sfont ascender] - [sfont descender];
835 hd = full_height - min_height;
837 /* standard height, similar to Carbon. Emacs.app: was 0.5 by default. */
841 font_info->underpos = 2; /*[sfont underlinePosition] is often clipped out */
842 font_info->underwidth = [sfont underlineThickness];
843 font_info->size = font->pixel_size;
844 font_info->voffset = lrint (hshrink * [sfont ascender] + expand * hd / 2);
847 font_info->max_bounds.ascent =
848 lrint (hshrink * [sfont ascender] + expand * hd/2);
849 /* [sfont descender] is usually negative. Use floor to avoid
850 clipping descenders. */
851 font_info->max_bounds.descent =
852 -lrint (floor(hshrink* [sfont descender] - expand*hd/2));
854 font_info->max_bounds.ascent + font_info->max_bounds.descent;
855 font_info->max_bounds.width = lrint (font_info->width);
856 font_info->max_bounds.lbearing = lrint (brect.origin.x);
857 font_info->max_bounds.rbearing =
858 lrint (brect.size.width - font_info->width);
861 /* set up synthItal and the CG font */
862 font_info->synthItal = synthItal;
864 ATSFontRef atsFont = ATSFontFindFromPostScriptName
865 ((CFStringRef)[nsfont fontName], kATSOptionFlagsDefault);
867 if (atsFont == kATSFontRefUnspecified)
869 /* see if we can get it by dropping italic (then synthesizing) */
870 atsFont = ATSFontFindFromPostScriptName ((CFStringRef)
871 [[fontMgr convertFont: nsfont toNotHaveTrait: NSItalicFontMask]
872 fontName], kATSOptionFlagsDefault);
873 if (atsFont != kATSFontRefUnspecified)
874 font_info->synthItal = YES;
877 /* last resort fallback */
878 atsFont = ATSFontFindFromPostScriptName
879 ((CFStringRef)@"Monaco", kATSOptionFlagsDefault);
882 font_info->cgfont = CGFontCreateWithPlatformFont ((void*)&atsFont);
886 /* set up metrics portion of font struct */
887 font->ascent = lrint([sfont ascender]);
888 font->descent = -lrint(floor([sfont descender]));
889 font->min_width = ns_char_width(sfont, '|');
890 font->space_width = lrint (ns_char_width (sfont, ' '));
891 font->average_width = lrint (font_info->width);
892 font->max_width = lrint (font_info->max_bounds.width);
893 font->height = lrint (font_info->height);
894 font->underline_position = lrint (font_info->underpos);
895 font->underline_thickness = lrint (font_info->underwidth);
897 font->props[FONT_NAME_INDEX] = Ffont_xlfd_name (font_object, Qnil);
898 font->props[FONT_FULLNAME_INDEX] =
899 make_unibyte_string (font_info->name, strlen (font_info->name));
907 /* Close FONT on frame F. */
909 nsfont_close (FRAME_PTR f, struct font *font)
911 struct nsfont_info *font_info = (struct nsfont_info *)font;
914 /* FIXME: this occurs apparently due to same failure to detect same font
915 that causes need for cache in nsfont_open () */
919 for (i =0; i<0x100; i++)
921 xfree (font_info->glyphs[i]);
922 xfree (font_info->metrics[i]);
924 [font_info->nsfont release];
926 CGFontRelease (font_info->cgfont);
928 xfree (font_info->name);
933 /* If FONT_ENTITY has a glyph for character C (Unicode code point),
934 return 1. If not, return 0. If a font must be opened to check
937 nsfont_has_char (Lisp_Object entity, int c)
943 /* Return a glyph code of FONT for character C (Unicode code point).
944 If FONT doesn't have such a glyph, return FONT_INVALID_CODE. */
946 nsfont_encode_char (struct font *font, int c)
948 struct nsfont_info *font_info = (struct nsfont_info *)font;
949 unsigned char high = (c & 0xff00) >> 8, low = c & 0x00ff;
953 return FONT_INVALID_CODE;
955 /* did we already cache this block? */
956 if (!font_info->glyphs[high])
957 ns_uni_to_glyphs (font_info, high);
959 g = font_info->glyphs[high][low];
960 return g == 0xFFFF ? FONT_INVALID_CODE : g;
964 /* Perform the size computation of glyphs of FONT and fill in members
965 of METRICS. The glyphs are specified by their glyph codes in
966 CODE (length NGLYPHS). */
968 nsfont_text_extents (struct font *font, unsigned int *code, int nglyphs,
969 struct font_metrics *metrics)
971 struct nsfont_info *font_info = (struct nsfont_info *)font;
972 struct font_metrics *pcm;
973 unsigned char high, low;
977 memset (metrics, 0, sizeof (struct font_metrics));
979 for (i =0; i<nglyphs; i++)
981 /* get metrics for this glyph, filling cache if need be */
982 /* TODO: get metrics for whole string from an NSLayoutManager
984 high = (code[i] & 0xFF00) >> 8;
985 low = code[i] & 0x00FF;
986 if (!font_info->metrics[high])
987 ns_glyph_metrics (font_info, high);
988 pcm = &(font_info->metrics[high][low]);
990 if (metrics->lbearing > totalWidth + pcm->lbearing)
991 metrics->lbearing = totalWidth + pcm->lbearing;
992 if (metrics->rbearing < totalWidth + pcm->rbearing)
993 metrics->rbearing = totalWidth + pcm->rbearing;
994 if (metrics->ascent < pcm->ascent)
995 metrics->ascent = pcm->ascent;
996 if (metrics->descent < pcm->descent)
997 metrics->descent = pcm->descent;
999 totalWidth += pcm->width;
1002 metrics->width = totalWidth;
1004 return totalWidth; /* not specified in doc, but xfont.c does it */
1008 /* Draw glyphs between FROM and TO of S->char2b at (X Y) pixel
1009 position of frame F with S->FACE and S->GC. If WITH_BACKGROUND
1010 is nonzero, fill the background in advance. It is assured that
1011 WITH_BACKGROUND is zero when (FROM > 0 || TO < S->nchars). */
1013 nsfont_draw (struct glyph_string *s, int from, int to, int x, int y,
1014 int with_background)
1015 /* NOTE: focus and clip must be set
1016 also, currently assumed (true in nsterm.m call) from ==0, to ==nchars */
1018 static char cbuf[1024];
1020 #ifdef NS_IMPL_GNUSTEP
1021 static float advances[1024];
1022 float *adv = advances;
1024 static CGSize advances[1024];
1025 CGSize *adv = advances;
1029 struct nsfont_info *font = ns_tmp_font;
1030 NSColor *col, *bgCol;
1031 unsigned short *t = s->char2b;
1033 char isComposite = s->first_glyph->type == COMPOSITE_GLYPH;
1034 int end = isComposite ? s->cmp_to : s->nchars;
1036 /* Select face based on input flags */
1037 switch (ns_tmp_flags)
1039 case NS_DUMPGLYPH_CURSOR:
1042 case NS_DUMPGLYPH_MOUSEFACE:
1043 face = FACE_FROM_ID (s->f,
1044 FRAME_NS_DISPLAY_INFO (s->f)->mouse_face_face_id);
1046 face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
1053 if (s->face->box != FACE_NO_BOX && s->first_glyph->left_box_line_p)
1054 r.origin.x += abs (s->face->box_line_width);
1057 r.size.height = FONT_HEIGHT (font);
1059 /* Convert UTF-16 (?) to UTF-8 and determine advances. Note if we just ask
1060 NS to render the string, it will come out differently from the individual
1061 character widths added up because of layout processing. */
1064 int cwidth, twidth = 0;
1066 /* FIXME: composition: no vertical displacement is considered. */
1067 t += s->cmp_from; /* advance into composition */
1068 for (i = s->cmp_from; i < end; i++, t++)
1070 hi = (*t & 0xFF00) >> 8;
1074 if (!s->first_glyph->u.cmp.automatic)
1075 cwidth = s->cmp->offsets[i * 2] /* (H offset) */ - twidth;
1078 Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
1079 Lisp_Object glyph = LGSTRING_GLYPH (gstring, i);
1080 if (NILP (LGLYPH_ADJUSTMENT (glyph)))
1081 cwidth = LGLYPH_WIDTH (glyph);
1084 cwidth = LGLYPH_WADJUST (glyph);
1085 #ifdef NS_IMPL_GNUSTEP
1086 *(adv-1) += LGLYPH_XOFF (glyph);
1088 (*(adv-1)).width += LGLYPH_XOFF (glyph);
1095 if (!font->metrics[hi]) /* FIXME: why/how can we need this now? */
1096 ns_glyph_metrics (font, hi);
1097 cwidth = font->metrics[hi][lo].width;
1100 #ifdef NS_IMPL_GNUSTEP
1102 CHAR_STRING_ADVANCE (*t, c); /* this converts the char to UTF-8 */
1104 (*adv++).width = cwidth;
1107 len = adv - advances;
1108 r.size.width = twidth;
1112 /* fill background if requested */
1113 if (with_background && !isComposite)
1116 int fibw = FRAME_INTERNAL_BORDER_WIDTH (s->f);
1117 int mbox_line_width = max (s->face->box_line_width, 0);
1119 if (s->row->full_width_p)
1121 if (br.origin.x <= fibw + 1 + mbox_line_width)
1123 br.size.width += br.origin.x - mbox_line_width;
1124 br.origin.x = mbox_line_width;
1126 if (FRAME_PIXEL_WIDTH (s->f) - (br.origin.x + br.size.width)
1128 br.size.width += fibw;
1130 if (s->face->box == FACE_NO_BOX)
1132 /* expand unboxed top row over internal border */
1133 if (br.origin.y <= fibw + 1 + mbox_line_width)
1135 br.size.height += br.origin.y;
1141 int correction = abs (s->face->box_line_width)+1;
1142 br.origin.y += correction;
1143 br.size.height -= 2*correction;
1144 br.origin.x += correction;
1145 br.size.width -= 2*correction;
1148 if (!s->face->stipple)
1149 [(NS_FACE_BACKGROUND (face) != 0
1150 ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
1151 : FRAME_BACKGROUND_COLOR (s->f)) set];
1154 struct ns_display_info *dpyinfo = FRAME_NS_DISPLAY_INFO (s->f);
1155 [[dpyinfo->bitmaps[face->stipple-1].img stippleMask] set];
1161 /* set up for character rendering */
1162 r.origin.y += font->voffset + (s->height - font->height)/2;
1164 col = (NS_FACE_FOREGROUND (face) != 0
1165 ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f)
1166 : FRAME_FOREGROUND_COLOR (s->f));
1167 /* FIXME: find another way to pass this */
1168 bgCol = (ns_tmp_flags != NS_DUMPGLYPH_FOREGROUND ? nil
1169 : (NS_FACE_BACKGROUND (face) != 0
1170 ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
1171 : FRAME_BACKGROUND_COLOR (s->f)));
1173 /* render under GNUstep using DPS */
1174 #ifdef NS_IMPL_GNUSTEP
1176 NSGraphicsContext *context = GSCurrentContext ();
1181 /* do erase if "foreground" mode */
1185 DPSmoveto (context, r.origin.x, r.origin.y);
1186 /*[context GSSetTextDrawingMode: GSTextFillStroke]; /// not implemented yet */
1187 DPSxshow (context, cbuf, advances, len);
1188 DPSstroke (context);
1190 /*[context GSSetTextDrawingMode: GSTextFill]; /// not implemented yet */
1194 if (face->underline_p)
1196 if (face->underline_color != 0)
1197 [ns_lookup_indexed_color (face->underline_color, s->f) set];
1200 DPSmoveto (context, r.origin.x, r.origin.y + font->underpos);
1201 DPSlineto (context, r.origin.x+r.size.width, r.origin.y+font->underpos);
1202 if (face->underline_color != 0)
1208 /* draw with DPSxshow () */
1209 DPSmoveto (context, r.origin.x, r.origin.y);
1210 DPSxshow (context, cbuf, advances, len);
1211 DPSstroke (context);
1213 DPSgrestore (context);
1217 #else /* NS_IMPL_COCOA */
1219 CGContextRef gcontext =
1220 [[NSGraphicsContext currentContext] graphicsPort];
1221 static CGAffineTransform fliptf;
1222 static BOOL firstTime = YES;
1227 fliptf = CGAffineTransformMakeScale (1.0, -1.0);
1230 CGContextSaveGState (gcontext);
1232 fliptf.c = font->synthItal ? Fix2X (kATSItalicQDSkew) : 0.0;
1234 CGContextSetFont (gcontext, font->cgfont);
1235 CGContextSetFontSize (gcontext, font->size);
1236 if (NILP (ns_antialias_text) || font->size <= ns_antialias_threshold)
1237 CGContextSetShouldAntialias (gcontext, 0);
1239 CGContextSetShouldAntialias (gcontext, 1);
1241 CGContextSetTextMatrix (gcontext, fliptf);
1245 /* foreground drawing; erase first to avoid overstrike */
1247 CGContextSetTextDrawingMode (gcontext, kCGTextFillStroke);
1248 CGContextSetTextPosition (gcontext, r.origin.x, r.origin.y);
1249 CGContextShowGlyphsWithAdvances (gcontext, s->char2b, advances, len);
1250 CGContextSetTextDrawingMode (gcontext, kCGTextFill);
1253 if (face->underline_p)
1255 if (face->underline_color != 0)
1256 [ns_lookup_indexed_color (face->underline_color, s->f) set];
1259 CGContextBeginPath (gcontext);
1260 CGContextMoveToPoint (gcontext,
1261 r.origin.x, r.origin.y + font->underpos);
1262 CGContextAddLineToPoint (gcontext, r.origin.x + r.size.width,
1263 r.origin.y + font->underpos);
1264 CGContextStrokePath (gcontext);
1265 if (face->underline_color != 0)
1271 CGContextSetTextPosition (gcontext, r.origin.x, r.origin.y);
1272 CGContextShowGlyphsWithAdvances (gcontext, s->char2b + s->cmp_from,
1275 if (face->overstrike)
1277 CGContextSetTextPosition (gcontext, r.origin.x+0.5, r.origin.y);
1278 CGContextShowGlyphsWithAdvances (gcontext, s->char2b + s->cmp_from,
1282 CGContextRestoreGState (gcontext);
1285 #endif /* NS_IMPL_COCOA */
1291 /* ==========================================================================
1293 Font glyph and metrics caching functions
1295 ========================================================================== */
1297 /* Find and cache corresponding glyph codes for unicode values in given
1298 hi-byte block of 256. */
1300 ns_uni_to_glyphs (struct nsfont_info *font_info, unsigned char block)
1302 #ifdef NS_IMPL_COCOA
1303 static EmacsGlyphStorage *glyphStorage;
1304 static char firstTime = 1;
1306 unichar *unichars = xmalloc (0x101 * sizeof (unichar));
1307 unsigned int i, g, idx;
1308 unsigned short *glyphs;
1311 fprintf (stderr, "%p\tFinding glyphs for glyphs in block %d\n",
1316 #ifdef NS_IMPL_COCOA
1320 glyphStorage = [[EmacsGlyphStorage alloc] initWithCapacity: 0x100];
1324 font_info->glyphs[block] = xmalloc (0x100 * sizeof (unsigned short));
1325 if (!unichars || !(font_info->glyphs[block]))
1328 /* create a string containing all unicode characters in this block */
1329 for (idx = block<<8, i =0; i<0x100; idx++, i++)
1330 if (idx < 0xD800 || idx > 0xDFFF)
1333 unichars[i] = 0xFEFF;
1334 unichars[0x100] = 0;
1337 #ifdef NS_IMPL_COCOA
1338 NSString *allChars = [[NSString alloc]
1339 initWithCharactersNoCopy: unichars
1342 NSGlyphGenerator *glyphGenerator = [NSGlyphGenerator sharedGlyphGenerator];
1343 /*NSCharacterSet *coveredChars = [nsfont coveredCharacterSet]; */
1344 unsigned int numGlyphs = [font_info->nsfont numberOfGlyphs];
1345 NSUInteger gInd =0, cInd =0;
1347 [glyphStorage setString: allChars font: font_info->nsfont];
1348 [glyphGenerator generateGlyphsForGlyphStorage: glyphStorage
1349 desiredNumberOfCharacters: glyphStorage->maxChar
1350 glyphIndex: &gInd characterIndex: &cInd];
1352 glyphs = font_info->glyphs[block];
1353 for (i =0; i<0x100; i++, glyphs++)
1355 #ifdef NS_IMPL_GNUSTEP
1358 g = glyphStorage->cglyphs[i];
1359 /* TODO: is this a good check? maybe need to use coveredChars.. */
1361 g = 0xFFFF; /* hopefully unused... */
1366 #ifdef NS_IMPL_COCOA
1376 /* Determine and cache metrics for corresponding glyph codes in given
1377 hi-byte block of 256. */
1379 ns_glyph_metrics (struct nsfont_info *font_info, unsigned char block)
1382 unsigned int numGlyphs = [font_info->nsfont numberOfGlyphs];
1384 struct font_metrics *metrics;
1387 fprintf (stderr, "%p\tComputing metrics for glyphs in block %d\n",
1390 #ifdef NS_IMPL_GNUSTEP
1391 /* not implemented yet (as of startup 0.18), so punt */
1393 numGlyphs = 0x10000;
1397 sfont = [font_info->nsfont screenFont];
1399 font_info->metrics[block] = xmalloc (0x100 * sizeof (struct font_metrics));
1400 memset (font_info->metrics[block], 0, 0x100 * sizeof (struct font_metrics));
1401 if (!(font_info->metrics[block]))
1404 metrics = font_info->metrics[block];
1405 for (g = block<<8, i =0; i<0x100 && g < numGlyphs; g++, i++, metrics++)
1408 NSRect r = [sfont boundingRectForGlyph: g];
1410 w = max ([sfont advancementForGlyph: g].width, 2.0);
1411 metrics->width = lrint (w);
1414 rb = r.size.width - w;
1416 metrics->lbearing = round (lb);
1417 if (font_info->ital)
1418 rb += 0.22 * font_info->height;
1419 metrics->rbearing = lrint (w + rb);
1421 metrics->descent = r.origin.y < 0 ? -r.origin.y : 0;
1422 /*lrint (hshrink * [sfont ascender] + expand * hd/2); */
1423 metrics->ascent = r.size.height - metrics->descent;
1424 /*-lrint (hshrink* [sfont descender] - expand * hd/2); */
1430 #ifdef NS_IMPL_COCOA
1431 /* helper for font glyph setup */
1432 @implementation EmacsGlyphStorage
1436 return [self initWithCapacity: 1024];
1439 - initWithCapacity: (unsigned long) c
1441 self = [super init];
1444 dict = [NSMutableDictionary new];
1445 cglyphs = (CGGlyph *)xmalloc (c * sizeof (CGGlyph));
1458 - (void) setString: (NSString *)str font: (NSFont *)font
1460 [dict setObject: font forKey: NSFontAttributeName];
1461 attrStr = [[NSAttributedString alloc] initWithString: str attributes: dict];
1462 maxChar = [str length];
1466 /* NSGlyphStorage protocol */
1467 - (NSUInteger)layoutOptions
1472 - (NSAttributedString *)attributedString
1477 - (void)insertGlyphs: (const NSGlyph *)glyphs length: (NSUInteger)length
1478 forStartingGlyphAtIndex: (NSUInteger)glyphIndex
1479 characterIndex: (NSUInteger)charIndex
1481 len = glyphIndex+length;
1482 for (i =glyphIndex; i<len; i++)
1483 cglyphs[i] = glyphs[i-glyphIndex];
1488 - (void)setIntAttribute: (NSInteger)attributeTag value: (NSInteger)val
1489 forGlyphAtIndex: (NSUInteger)glyphIndex
1495 #endif /* NS_IMPL_COCOA */
1500 ns_dump_glyphstring (struct glyph_string *s)
1504 fprintf (stderr, "Glyph string len = %d at (%d, %d) overhang (%d, %d),"
1505 "overlap = %d, bg_filled = %d:",
1506 s->nchars, s->x, s->y, s->left_overhang, s->right_overhang,
1507 s->row->overlapping_p, s->background_filled_p);
1508 for (i =0; i<s->nchars; i++)
1509 fprintf (stderr, "%c", s->first_glyph[i].u.ch);
1510 fprintf (stderr, "\n");
1515 syms_of_nsfont (void)
1517 nsfont_driver.type = Qns;
1518 register_font_driver (&nsfont_driver, NULL);
1519 DEFSYM (Qapple, "apple");
1520 DEFSYM (Qroman, "roman");
1521 DEFSYM (Qmedium, "medium");
1522 DEFVAR_LISP ("ns-reg-to-script", &Vns_reg_to_script,
1523 doc: /* Internal use: maps font registry to unicode script. */);
1526 // arch-tag: d6c3c6f0-62de-4978-8b1e-b7966fe02cae