Mercurial > emacs
diff src/nsfont.m @ 96676:336d4f3f5eca
adding forgotten file src/nsfont.m for emacs.app merge
author | Adrian Robert <Adrian.B.Robert@gmail.com> |
---|---|
date | Tue, 15 Jul 2008 18:35:28 +0000 |
parents | |
children | e869d0172660 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/nsfont.m Tue Jul 15 18:35:28 2008 +0000 @@ -0,0 +1,1495 @@ +/* Font back-end driver for the NeXT/Open/GNUstep and MacOSX window system. + See font.h + Copyright (C) 2006, 2007, 2008 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. + +Author: Adrian Robert (arobert@cogsci.ucsd.edu) +*/ + +#include "config.h" + +#include "lisp.h" +#include "dispextern.h" +#include "composite.h" +#include "blockinput.h" +#include "charset.h" +#include "frame.h" +#include "window.h" +#include "fontset.h" +#include "nsterm.h" +#include "frame.h" +#include "character.h" +#include "font.h" + +#define NSFONT_TRACE 0 + +extern Lisp_Object Qns; +extern Lisp_Object Qnormal, Qbold, Qitalic, Qcondensed, Qexpanded; +static Lisp_Object Qapple, Qroman, Qmedium; +extern Lisp_Object ns_expand_space; +extern Lisp_Object Qappend; +extern int ns_antialias_text, ns_use_qd_smoothing; +extern float ns_antialias_threshold; +extern int ns_tmp_flags; +extern struct nsfont_info *ns_tmp_font; + +/* font glyph and metrics caching functions, implemented at end */ +static void ns_uni_to_glyphs (struct nsfont_info *font_info, + unsigned char block); +static void ns_glyph_metrics (struct nsfont_info *font_info, + unsigned char block); + + +/* ========================================================================== + + Utilities + + ========================================================================== */ + + +/* Replace spaces w/another character so emacs core font parsing routines + aren't thrown off. */ +static void +nsfont_escape_name (char *name) +{ + int i =0, len =strlen (name); + for ( ; i<len; i++) + if (name[i] == ' ') + name[i] = '_'; +} + + +/* Reconstruct spaces in a font family name passed through emacs. */ +static void +nsfont_unescape_name (char *name) +{ + int i =0, len =strlen (name); + for ( ; i<len; i++) + if (name[i] == '_') + name[i] = ' '; +} + + +/* Extract family name from a font spec. */ +static NSString * +nsfont_get_family (Lisp_Object font_spec) +{ + Lisp_Object tem = AREF (font_spec, FONT_FAMILY_INDEX); + if (NILP (tem)) + return nil; + else + { + char *tmp = strdup (SDATA (SYMBOL_NAME (tem))); + NSString *family; + nsfont_unescape_name (tmp); + /* PENDING: this seems to be needed only for font names that are + hard-coded into emacs, like 'helvetica' for splash screen */ + if (tmp) + tmp[0] = toupper (tmp[0]); + family = [NSString stringWithUTF8String: tmp]; + free (tmp); + return family; + } +} + + +/* Converts FONT_WEIGHT, FONT_SLANT, FONT_WIDTH to NSFont traits. */ +/* PENDING (20080601): The font backend's strategy for handling font + styles continues to evolve. When/if this stabilizes, we + can change the code here to be more sophisticated and accurate. + For now, we rely on "normal/plain" style being numeric 100. */ +#define STYLE_REF 100 +static unsigned int +nsfont_spec_to_traits (Lisp_Object font_spec) +{ + unsigned int traits = 0; + int n; + + n = FONT_WEIGHT_NUMERIC (font_spec); + if (n != -1) + traits |= (n > STYLE_REF) ? NSBoldFontMask : NSUnboldFontMask; + + n = FONT_SLANT_NUMERIC (font_spec); + if (n != -1) + traits |= (n > STYLE_REF) ? NSItalicFontMask : NSUnitalicFontMask; + + n = FONT_WIDTH_NUMERIC (font_spec); + if (n > -1) + { + if (n < STYLE_REF - 10) + traits |= NSCondensedFontMask; + else if (n > STYLE_REF + 10) + traits |= NSExpandedFontMask; + } + +/*fprintf (stderr, " returning traits = %u\n", traits); */ + return traits; +} + + +/* Converts NSArray of PS name, non-family part, weight, and traits to a + font backend font-entity. */ +static Lisp_Object +nsfont_fmember_to_entity (NSString *family, NSArray *famMember) +{ + Lisp_Object font_entity = font_make_entity (); + unsigned int traits = [[famMember objectAtIndex: 3] unsignedIntValue]; +/* NSString *psName = [famMember objectAtIndex: 0]; */ + NSMutableString *suffix = [[famMember objectAtIndex: 1] mutableCopy]; + char *escapedFamily = [family UTF8String]; + + nsfont_escape_name (escapedFamily); + [suffix replaceOccurrencesOfString: @" " withString: @"" options: 0 + range: NSMakeRange (0, [suffix length])]; + + ASET (font_entity, FONT_TYPE_INDEX, Qns); + ASET (font_entity, FONT_FOUNDRY_INDEX, Qapple); + ASET (font_entity, FONT_FAMILY_INDEX, intern (escapedFamily)); + ASET (font_entity, FONT_ADSTYLE_INDEX, intern ([suffix UTF8String])); + ASET (font_entity, FONT_REGISTRY_INDEX, Qiso10646_1); + + FONT_SET_STYLE (font_entity, FONT_WEIGHT_INDEX, + traits & NSBoldFontMask ? Qbold : Qmedium); + FONT_SET_STYLE (font_entity, FONT_SLANT_INDEX, + traits & NSItalicFontMask ? Qitalic : Qnormal); /*XXX: should be Qroman */ + FONT_SET_STYLE (font_entity, FONT_WIDTH_INDEX, + traits & NSCondensedFontMask ? Qcondensed : + traits & NSExpandedFontMask ? Qexpanded : Qnormal); + + ASET (font_entity, FONT_SIZE_INDEX, make_number (0)); + ASET (font_entity, FONT_EXTRA_INDEX, Qnil); + ASET (font_entity, FONT_OBJLIST_INDEX, Qnil); + + if (NSFONT_TRACE) + { + fprintf (stderr, "created font_entity:\n "); + debug_print (font_entity); + } + + [suffix release]; + return font_entity; +} + + +/* Computes Hamming distance btwn two "vectors" of 0's and 1's. */ +static int +nsfont_trait_distance (unsigned int traits1, unsigned int traits2) +{ + int i, d =0; + for (i =0; i<sizeof (unsigned int)*8; i++) + { + d += (traits1 & 0x1) ^ (traits2 & 0x1); + traits1 >> 1; + traits2 >> 1; + } + return d; +} + + +/* Default font entity based on Monaco. */ +static Lisp_Object +nsfont_fallback_entity () +{ + NSString *family = [[NSFont userFixedPitchFontOfSize: 0] familyName]; + NSArray *famMemberSpec = [NSArray arrayWithObjects: family, @"", + [NSNumber numberWithUnsignedInt: 5], + [NSNumber numberWithUnsignedInt: 0], nil]; + return nsfont_fmember_to_entity (family, famMemberSpec); +} + + +/* ========================================================================== + + Font driver implementation + + ========================================================================== */ + +static Lisp_Object nsfont_get_cache (FRAME_PTR frame); +static Lisp_Object nsfont_list (Lisp_Object frame, Lisp_Object font_spec); +static Lisp_Object nsfont_match (Lisp_Object frame, Lisp_Object font_spec); +static Lisp_Object nsfont_list_family (Lisp_Object frame); +static Lisp_Object nsfont_open (FRAME_PTR f, Lisp_Object font_entity, + int pixel_size); +static void nsfont_close (FRAME_PTR f, struct font *font); +static int nsfont_has_char (Lisp_Object entity, int c); +static unsigned int nsfont_encode_char (struct font *font, int c); +static int nsfont_text_extents (struct font *font, unsigned int *code, + int nglyphs, struct font_metrics *metrics); +static int nsfont_draw (struct glyph_string *s, int from, int to, int x, int y, + int with_background); + +struct font_driver nsfont_driver = + { + (Lisp_Object) NULL, /* Qns */ + 1, /* case sensitive */ + nsfont_get_cache, + nsfont_list, + nsfont_match, + nsfont_list_family, + NULL, /*free_entity */ + nsfont_open, + nsfont_close, + NULL, /* prepare_face */ + NULL, /* done_face */ + nsfont_has_char, + nsfont_encode_char, + nsfont_text_extents, + nsfont_draw, + /* excluded: get_bitmap, free_bitmap, get_outline, free_outline, + anchor_point, otf_capability, otf_driver, + start_for_frame, end_for_frame, shape */ + }; + + +/* Return a cache of font-entities on FRAME. The cache must be a + cons whose cdr part is the actual cache area. */ +static Lisp_Object +nsfont_get_cache (FRAME_PTR frame) +{ + Display_Info *dpyinfo = FRAME_NS_DISPLAY_INFO (frame); + return (dpyinfo->name_list_element); +} + + +/* List fonts exactly matching with FONT_SPEC on FRAME. The value + is a vector of font-entities. This is the sole API that + allocates font-entities. */ +static Lisp_Object +nsfont_list (Lisp_Object frame, Lisp_Object font_spec) +{ + Lisp_Object list = Qnil; + Lisp_Object tem; + NSString *family; + NSArray *families; + NSEnumerator *famEnum; + NSFontManager *fontMgr; + unsigned int traits = nsfont_spec_to_traits (font_spec); + + if (NSFONT_TRACE) + { + fprintf (stderr, "nsfont: list for fontspec:\n "); + debug_print (font_spec); + } + + /* if has non-unicode registry, give up */ + tem = AREF (font_spec, FONT_REGISTRY_INDEX); + if (!EQ (tem, Qiso10646_1) && !EQ (tem, Qunicode_bmp)) + return Qnil; + + fontMgr = [NSFontManager sharedFontManager]; + + family = nsfont_get_family (font_spec); + + if (family != nil) + families = [NSArray arrayWithObject: family]; + else + families = [fontMgr availableFontFamilies]; + + for (famEnum = [families objectEnumerator]; family = [famEnum nextObject]; ) + { + NSEnumerator *fm; + NSArray *fmember, *firstMember = nil; + unsigned int mtraits; + BOOL foundItal = NO || (traits & NSUnitalicFontMask); + NSArray *famMembers = [fontMgr availableMembersOfFontFamily: family]; +#ifdef NS_IMPL_COCOA + /* LastResort is special: not a family but a font name only */ + if ([@"LastResort" isEqualToString: family] && [famMembers count] == 0) + { + famMembers = [NSArray arrayWithObject: [NSArray arrayWithObjects: + @"LastResort", @"", [NSNumber numberWithUnsignedInt: 5], + [NSNumber numberWithUnsignedInt: 0], nil]]; + } +#endif + + /* fmember = [postscriptName style weight traits] */ + fm = [famMembers objectEnumerator]; + while (fmember = [fm nextObject]) + { + mtraits = [[fmember objectAtIndex: 3] unsignedIntValue]; + if ((mtraits & traits) == traits) + { + list = Fcons (nsfont_fmember_to_entity (family, fmember), list); + if (mtraits & NSItalicFontMask) + foundItal = YES; + if (firstMember == nil) + firstMember = fmember; + } + } + if (foundItal == NO && firstMember != nil) + { + /* no italic member found; add a synthesized one */ + NSMutableArray *smember = [firstMember mutableCopy]; + [smember replaceObjectAtIndex: 1 withObject: @"synthItal" ]; + mtraits = [[fmember objectAtIndex: 3] unsignedIntValue]; + mtraits |= NSItalicFontMask; + [smember replaceObjectAtIndex: 3 + withObject: [NSNumber numberWithUnsignedInt: mtraits]]; +/*NSLog (@"-- adding synthItal member: %@", smember); */ + list = Fcons (nsfont_fmember_to_entity (family, smember), list); + [smember release]; + } + } + + if (NSFONT_TRACE) + fprintf (stderr, " Returning %d entities.\n", XINT (Flength (list))); + + return (NILP (list) ? Qnil : Fvconcat (1, &list));/* Qnil was null_vector */ +} + + +/* Return a font entity most closely maching with FONT_SPEC on + FRAME. The closeness is determined by the font backend, thus + `face-font-selection-order' is ignored here. */ +static Lisp_Object +nsfont_match (Lisp_Object frame, Lisp_Object font_spec) +{ + long traits = nsfont_spec_to_traits (font_spec); + NSFontManager *fontMgr = [NSFontManager sharedFontManager]; + NSString *family; + Lisp_Object tem; + + if (NSFONT_TRACE) + { + fprintf (stderr, "nsfont: match for fontspec:\n "); + debug_print (font_spec); + } + + /* if has non-unicode registry, just return fallback */ +#if 0 + tem = AREF (font_spec, FONT_ADSTYLE_INDEX); + if (!NILP (tem)) + fprintf (stderr, "adstyle: '%s'\n", SDATA (tem)); +#endif + tem = AREF (font_spec, FONT_REGISTRY_INDEX); + if (!EQ (tem, Qiso10646_1) && !EQ (tem, Qunicode_bmp)) + return nsfont_fallback_entity (); + + family = nsfont_get_family (font_spec); + + if (family != nil) + { + /* try to find close font in family */ + NSArray *famMembers = [fontMgr availableMembersOfFontFamily: family]; + NSEnumerator *fm = [famMembers objectEnumerator]; + NSArray *fmember; + int minDist = sizeof (unsigned int) * 8 + 1; + int bestMatchIdx = -1; + int i; + + for (i =0; fmember = [fm nextObject]; i++) + { + unsigned int mtraits = [[fmember objectAtIndex: 3] unsignedIntValue]; + int dist = nsfont_trait_distance ((mtraits & traits), traits); + if (dist < minDist) + { + bestMatchIdx = i; + minDist = dist; + } + } + if (bestMatchIdx != -1) + return nsfont_fmember_to_entity + (family, [famMembers objectAtIndex: bestMatchIdx]); + } + + /* no family that had members was given; find any font matching traits */ + { + NSArray *fontNames = [fontMgr availableFontNamesWithTraits: traits]; + if (fontNames && [fontNames count] > 0) + { + NSString *fontName = [fontNames objectAtIndex: 0]; + /*PENDING: is there a more efficient way to get family name? */ + NSFont *font = [NSFont fontWithName: fontName size: 0]; + if (font != nil) + { + /* now need to get suffix part of name.. */ + NSString *family = [font familyName]; + NSEnumerator *fm = [[fontMgr availableMembersOfFontFamily: family] + objectEnumerator]; + NSArray *fmember; + while (fmember = [fm nextObject]) + { + unsigned int mtraits = + [[fmember objectAtIndex: 3] unsignedIntValue]; + if (mtraits & traits == traits) + return nsfont_fmember_to_entity (family, fmember); + } + } + } + } + + /* if we get here, return the fallback */ + if (NSFONT_TRACE) + fprintf (stderr, " *** returning fallback\n"); + + return nsfont_fallback_entity (); +} + + +/* List available families. The value is a list of family names + (symbols). */ +static Lisp_Object +nsfont_list_family (Lisp_Object frame) +{ + Lisp_Object list = Qnil; + NSEnumerator *families = + [[[NSFontManager sharedFontManager] availableFontFamilies] + objectEnumerator]; + NSString *family; + while (family = [families nextObject]) + list = Fcons (intern ([family UTF8String]), list); + /*PENDING: escape the name? */ + + if (NSFONT_TRACE) + fprintf (stderr, "nsfont: list families returning %d entries\n", + XINT (Flength (list))); + + return list; +} + + +/* utility: get width of a char c in screen font sfont */ +static float +nsfont_char_width (NSFont *sfont, int c) +{ + float w; + NSString *cstr = [NSString stringWithFormat: @"%c", c]; +#ifdef NS_IMPL_COCOA + NSGlyph glyph = [sfont glyphWithName: cstr]; + if (glyph) + { + float w = [sfont advancementForGlyph: glyph].width; + if (w >= 1.5) + return w; + } +#endif + w = [sfont widthOfString: cstr]; + return max (w, 2.0); +} + + +/* Open a font specified by FONT_ENTITY on frame F. If the font is + scalable, open it with PIXEL_SIZE. */ +static Lisp_Object +nsfont_open (FRAME_PTR f, Lisp_Object font_entity, int pixel_size) +{ + BOOL synthItal; + struct nsfont_info *font_info; + struct font *font; + unsigned int traits = nsfont_spec_to_traits (font_entity); + NSFontManager *fontMgr = [NSFontManager sharedFontManager]; + NSString *family; + NSFont *nsfont, *sfont; + Lisp_Object tem; + NSRect brect; + Lisp_Object font_object; + int i; + int fixLeopardBug; +#if 0 + static NSMutableDictionary *fontCache = nil; + + /* 2008/03/08: The same font may end up being requested for different + entities, due to small differences in numeric values or other issues, + or for different copies of the same entity. Therefore we cache to + avoid creating multiple struct font objects (with metrics cache, etc.) + for the same NSFont object. + 2008/06/01: This is still an issue, but after font backend refactoring + caching will be more difficult, needs to be reworked before enabling. */ + if (fontCache == nil) + fontCache = [[NSMutableDictionary alloc] init]; +#endif + + font_object = font_make_object (VECSIZE (struct nsfont_info), font_entity, + pixel_size); + font_info = (struct nsfont_info *) XFONT_OBJECT (font_object); + font = (struct font *)font_info; + if (!font) + return NULL; /*PENDING: this copies w32, but causes a segfault */ + + if (NSFONT_TRACE) + { + fprintf (stderr, "nsfont: open size %d of fontentity:\n ", pixel_size); + debug_print (font_entity); + } + + if (pixel_size <= 0) + { + /* try to get it out of frame params */ + Lisp_Object tem = get_frame_param (f, Qfontsize); + pixel_size = NILP (tem) ? 0 : XFASTINT (tem); + } + + tem = AREF (font_entity, FONT_ADSTYLE_INDEX); + synthItal = !NILP (tem) && !strncmp ("synthItal", SDATA (SYMBOL_NAME (tem)), + 9); + family = nsfont_get_family (font_entity); + if (NSFONT_TRACE) + { + fprintf (stderr, "family: '%s'\ttraits = %ld\tbold = %d\n", + [family UTF8String], traits, traits & NSBoldFontMask); + } + + /* see http://cocoadev.com/forums/comments.php?DiscussionID =74 */ + fixLeopardBug = traits & NSBoldFontMask ? 10 : 5; + nsfont = [fontMgr fontWithFamily: family + traits: traits weight: fixLeopardBug + size: pixel_size]; + /* if didn't find, try synthetic italic */ + if (nsfont == nil && synthItal && (traits & NSItalicFontMask)) + { + nsfont = [fontMgr fontWithFamily: family + traits: traits & ~NSItalicFontMask + weight: fixLeopardBug size: pixel_size]; + } +#ifdef NS_IMPL_COCOA + /* LastResort not really a family */ + if (nsfont == nil && [@"LastResort" isEqualToString: family]) + { + nsfont = [NSFont fontWithName: @"LastResort" size: pixel_size]; + } +#endif + + if (nsfont == nil) + { + message_with_string ("*** Warning: font in family '%s' not found", + build_string ([family UTF8String]), 1); + nsfont = [NSFont userFixedPitchFontOfSize: pixel_size]; + if (!nsfont) + { + fprintf (stderr, "*** Emacs.app: unable to load backup font\n"); + return NULL; + } + } + +#if 0 + { + NSNumber *cached = [fontCache objectForKey: nsfont]; + if (cached != nil && !synthItal) + { +fprintf (stderr, "*** CACHE HIT!\n"); + struct font_info *existing = + (struct nsfont_info *)[cached unsignedLongValue]; + /* ... */ + } + else + { + if (!synthItal) + [fontCache + setObject: [NSNumber numberWithUnsignedLong: + (unsigned long)font_info] + forKey: nsfont]; + } + } +#endif + + font_info->glyphs = (unsigned short *) + xmalloc (0x100 * sizeof (unsigned short *)); + font_info->metrics = (struct font_metrics *) + xmalloc (0x100 * sizeof (struct font_metrics *)); + if (!font_info->glyphs || !font_info->metrics) + return NULL; + bzero (font_info->glyphs, 0x100 * sizeof (unsigned short *)); + bzero (font_info->metrics, 0x100 * sizeof (struct font_metrics *)); + + +BLOCK_INPUT; + + /* for metrics */ + sfont = [nsfont screenFont]; + if (sfont == nil) + sfont = nsfont; + + /* non-metric backend font struct fields */ + font = (struct font *) font_info; + font->pixel_size = [sfont pointSize]; + font->driver = &nsfont_driver; + font->encoding_type = FONT_ENCODING_NOT_DECIDED; + font->encoding_charset = -1; + font->repertory_charset = -1; + font->default_ascent = 0; + font->vertical_centering = 0; + font->baseline_offset = 0; + font->relative_compose = 0; + font->font_encoder = NULL; + + /*PENDING: does anything care about this? */ + font->props[FONT_FORMAT_INDEX] = Qns; + font->props[FONT_FILE_INDEX] = Qnil; + + { + double expand, shrink, hshrink; + float full_height, min_height, hd; + const char *fontName = [[nsfont fontName] UTF8String]; + int len = strlen (fontName); + +#ifdef NS_IMPL_GNUSTEP + font_info->nsfont = sfont; +#else + font_info->nsfont = nsfont; +#endif + [font_info->nsfont retain]; + + /* set up ns_font (defined in nsgui.h) */ + font_info->name = (char *)xmalloc (strlen (fontName)+1); + bcopy (fontName, font_info->name, strlen (fontName)+1); + font_info->bold = [fontMgr traitsOfFont: nsfont] & NSBoldFontMask; + font_info->ital = + synthItal || ([fontMgr traitsOfFont: nsfont] & NSItalicFontMask); + + /* Metrics etc.; some fonts return an unusually large max advance, so we + only use it for fonts that have wide characters. */ + font_info->width = ([sfont numberOfGlyphs] > 2000) ? + [sfont maximumAdvancement].width : nsfont_char_width (sfont, '0'); + + brect = [sfont boundingRectForFont]; + full_height = brect.size.height; + min_height = [sfont ascender] - [sfont descender]; + hd = full_height - min_height; + + if (!NUMBERP (ns_expand_space)) + error ("No expand space defined"); + + /* ns_expand_space = 0.0 is use standard height; less shrink, more expand */ + expand = XFLOATINT (ns_expand_space) + 0.5; + + if (expand < 0.0) + { + shrink = 1 + expand; + hshrink = 1 + expand / 2.0; + expand = 0.0; + } + else + shrink = hshrink = 1.0; + + font_info->underpos = 2; /*[sfont underlinePosition] is often clipped out */ + font_info->underwidth = [sfont underlineThickness]; + font_info->size = font->pixel_size; + font_info->voffset = lrint (hshrink * [sfont ascender] + expand * hd / 2); + + /* max bounds */ + font_info->max_bounds.ascent = + lrint (hshrink * [sfont ascender] + expand * hd/2); + font_info->max_bounds.descent = + -lrint (hshrink* [sfont descender] - expand*hd/2); + font_info->height = + font_info->max_bounds.ascent + font_info->max_bounds.descent; + font_info->max_bounds.width = lrint (font_info->width); + font_info->max_bounds.lbearing = lrint (brect.origin.x); + font_info->max_bounds.rbearing = + lrint (brect.size.width - font_info->width); + /*font_info->width + (font_info->ital ? 0.2 * font_info->height : 0); */ + +#ifdef NS_IMPL_COCOA + /* set up synthItal and the CG font */ + font_info->synthItal = synthItal; + { + ATSFontRef atsFont = ATSFontFindFromPostScriptName + ((CFStringRef)[nsfont fontName], kATSOptionFlagsDefault); + + if (atsFont == kATSFontRefUnspecified) + { + /* see if we can get it by dropping italic (then synthesizing) */ + atsFont = ATSFontFindFromPostScriptName ((CFStringRef) + [[fontMgr convertFont: nsfont toNotHaveTrait: NSItalicFontMask] + fontName], kATSOptionFlagsDefault); + if (atsFont != kATSFontRefUnspecified) + font_info->synthItal = YES; + else + { + /* last resort fallback */ + atsFont = ATSFontFindFromPostScriptName + ((CFStringRef)@"Monaco", kATSOptionFlagsDefault); + } + } + font_info->cgfont = CGFontCreateWithPlatformFont ((void*)&atsFont); + } +#endif + + /* set up metrics portion of font struct */ + font->ascent = [sfont ascender]; + font->descent = -[sfont descender]; + font->min_width = [sfont widthOfString: @"|"]; /* PENDING */ + font->space_width = lrint (nsfont_char_width (sfont, ' ')); + font->average_width = lrint (font_info->width); + font->max_width = lrint (font_info->max_bounds.width); + font->height = lrint (font_info->height); + font->underline_position = lrint (font_info->underpos); + font->underline_thickness = lrint (font_info->underwidth); + + font->props[FONT_NAME_INDEX] = Ffont_xlfd_name (font_object, Qnil); + font->props[FONT_FULLNAME_INDEX] = + make_unibyte_string (font_info->name, strlen (font_info->name)); + } + UNBLOCK_INPUT; + + return font_object; +} + + +/* Close FONT on frame F. */ +static void +nsfont_close (FRAME_PTR f, struct font *font) +{ + struct nsfont_info *font_info = (struct nsfont_info *)font; + int i; + + /* PENDING: this occurs apparently due to same failure to detect same font + that causes need for cache in nsfont_open () + (came after unicode-2 -> trunk) */ + if (!font_info) + return; + + for (i =0; i<0x100; i++) + { + if (font_info->glyphs[i]) + xfree (font_info->glyphs[i]); + if (font_info->metrics[i]) + xfree (font_info->metrics[i]); + } + [font_info->nsfont release]; +#ifdef NS_IMPL_COCOA + CGFontRelease (font_info->cgfont); +#endif + xfree (font_info->name); + xfree (font_info); +} + + +/* If FONT_ENTITY has a glyph for character C (Unicode code point), + return 1. If not, return 0. If a font must be opened to check + it, return -1. */ +static int +nsfont_has_char (Lisp_Object entity, int c) +{ + return -1; +} + + +/* Return a glyph code of FONT for character C (Unicode code point). + If FONT doesn't have such a glyph, return FONT_INVALID_CODE. */ +static unsigned int +nsfont_encode_char (struct font *font, int c) +{ + struct nsfont_info *font_info = (struct nsfont_info *)font; + unsigned char high = (c & 0xff00) >> 8, low = c & 0x00ff; + unsigned short g; + + if (c > 0xFFFF) + return FONT_INVALID_CODE; + + /* did we already cache this block? */ + if (!font_info->glyphs[high]) + ns_uni_to_glyphs (font_info, high); + + g = font_info->glyphs[high][low]; +/*fprintf (stderr, "mapping char %d -> %d\n", c, g); */ + return g == 0xFFFF ? FONT_INVALID_CODE : g; +} + + +/* Perform the size computation of glyphs of FONT and fill in members + of METRICS. The glyphs are specified by their glyph codes in + CODE (length NGLYPHS). */ +static int +nsfont_text_extents (struct font *font, unsigned int *code, int nglyphs, + struct font_metrics *metrics) +{ + struct nsfont_info *font_info = (struct nsfont_info *)font; + struct font_metrics *pcm; + unsigned char high, low; + int totalWidth = 0; + int i; + + bzero (metrics, sizeof (struct font_metrics)); + + for (i =0; i<nglyphs; i++) + { + /* get metrics for this glyph, filling cache if need be */ + /* PENDING: get metrics for whole string from an NSLayoutManager + (if not too slow) */ + high = (code[i] & 0xFF00) >> 8; + low = code[i] & 0x00FF; + if (!font_info->metrics[high]) + ns_glyph_metrics (font_info, high); + pcm = &(font_info->metrics[high][low]); + + if (metrics->lbearing > totalWidth + pcm->lbearing) + metrics->lbearing = totalWidth + pcm->lbearing; + if (metrics->rbearing < totalWidth + pcm->rbearing) + metrics->rbearing = totalWidth + pcm->rbearing; + if (metrics->ascent < pcm->ascent) + metrics->ascent = pcm->ascent; + if (metrics->descent < pcm->descent) + metrics->descent = pcm->descent; + + totalWidth += pcm->width; + } + + metrics->width = totalWidth; + + return totalWidth; /* not specified in doc, but xfont.c does it */ +} + + +/* Draw glyphs between FROM and TO of S->char2b at (X Y) pixel + position of frame F with S->FACE and S->GC. If WITH_BACKGROUND + is nonzero, fill the background in advance. It is assured that + WITH_BACKGROUND is zero when (FROM > 0 || TO < S->nchars). */ +static int +nsfont_draw (struct glyph_string *s, int from, int to, int x, int y, + int with_background) +/* NOTE: focus and clip must be set + also, currently assumed (true in nsterm.m call) from ==0, to ==nchars */ +{ + static char cbuf[1024]; + char *c = cbuf; +#ifdef NS_IMPL_GNUSTEP + static float advances[1024]; + float *adv = advances; +#else + static CGSize advances[1024]; + CGSize *adv = advances; +#endif + struct face *face; + NSRect r; + struct nsfont_info *font = ns_tmp_font; + NSColor *col, *bgCol; + unsigned short *t = s->char2b; + int i, len; + + /* Select face based on input flags */ + switch (ns_tmp_flags) + { + case NS_DUMPGLYPH_CURSOR: + face = s->face; + break; + case NS_DUMPGLYPH_MOUSEFACE: + face = FACE_FROM_ID (s->f, + FRAME_NS_DISPLAY_INFO (s->f)->mouse_face_face_id); + if (!face) + face = FACE_FROM_ID (s->f, MOUSE_FACE_ID); + break; + default: + face = s->face; + } + + r.origin.x = s->x; + if (s->face->box != FACE_NO_BOX && s->first_glyph->left_box_line_p) + r.origin.x += abs (s->face->box_line_width); + + r.origin.y = s->y; + r.size.height = FONT_HEIGHT (font); + + /* Convert UTF-16 (?) to UTF-8 and determine advances. Note if we just ask + NS to render the string, it will come out differently from the individual + character widths added up because of layout processing. */ + { + XCharStruct *cs; + int cwidth, twidth = 0; + int hi, lo; + char isComposite = 0; /* s->first_glyph->type == COMPOSITE_GLYPH; */ + /* PENDING: composition: no vertical displacement is considered. */ + t+= s->gidx; /* advance into composition */ + for (i =0; i<s->nchars - s->gidx; i++, t++) + { + hi = (*t & 0xFF00) >> 8; + lo = *t & 0x00FF; + if (isComposite) + { + cwidth = s->cmp->offsets[s->gidx++ * 2] - twidth; + } + else + { + if (!font->metrics[hi]) /*PENDING: why/how can we need this now? */ + ns_glyph_metrics (font, hi); + cwidth = font->metrics[hi][lo].width; + } + twidth += cwidth; +#ifdef NS_IMPL_GNUSTEP + *adv++ = cwidth; + CHAR_STRING_ADVANCE (*t, c); /* this converts the char to UTF-8 */ +#else + (*adv++).width = cwidth; +#endif + } + len = adv - advances; + r.size.width = twidth; + *c = 0; + } + + /* fill background if requested */ + if (with_background) + { + NSRect br = r; + int fibw = FRAME_INTERNAL_BORDER_WIDTH (s->f); + int mbox_line_width = max (s->face->box_line_width, 0); + + if (s->row->full_width_p) + { + if (br.origin.x <= fibw + 1 + mbox_line_width) + { + br.size.width += br.origin.x - mbox_line_width; + br.origin.x = mbox_line_width; + } + if (FRAME_PIXEL_WIDTH (s->f) - (br.origin.x + br.size.width) + <= fibw+1) + br.size.width += fibw; + } + if (s->face->box == FACE_NO_BOX) + { + /* expand unboxed top row over internal border */ + if (br.origin.y <= fibw + 1 + mbox_line_width) + { + br.size.height += br.origin.y; + br.origin.y = 0; + } + } + else + { + int correction = abs (s->face->box_line_width)+1; + br.origin.y += correction; + br.size.height -= 2*correction; + br.origin.x += correction; + br.size.width -= 2*correction; + } + + if (!s->face->stipple) + [(NS_FACE_BACKGROUND (face) != nil + ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f) + : FRAME_BACKGROUND_COLOR (s->f)) set]; + else + { + struct ns_display_info *dpyinfo = FRAME_NS_DISPLAY_INFO (s->f); + [[dpyinfo->bitmaps[face->stipple-1].img stippleMask] set]; + } + NSRectFill (br); + } + + + /* set up for character rendering */ + r.origin.y += font->voffset + (s->height - font->height)/2; + + col = (NS_FACE_FOREGROUND (face) != nil + ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f) + : FRAME_FOREGROUND_COLOR (s->f)); + /*PENDING: find another way to pass this */ + bgCol = (ns_tmp_flags != NS_DUMPGLYPH_FOREGROUND ? nil + : (NS_FACE_BACKGROUND (face) != nil + ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f) + : FRAME_BACKGROUND_COLOR (s->f))); + + /* render under GNUstep using DPS */ +#ifdef NS_IMPL_GNUSTEP + { + NSGraphicsContext *context = GSCurrentContext (); + + DPSgsave (context); + [font->nsfont set]; + + /* do erase if "foreground" mode */ + if (bgCol != nil) + { + [bgCol set]; + DPSmoveto (context, r.origin.x, r.origin.y); +/*[context GSSetTextDrawingMode: GSTextFillStroke]; /// not implemented yet */ + DPSxshow (context, cbuf, advances, len); + DPSstroke (context); + [col set]; +/*[context GSSetTextDrawingMode: GSTextFill]; /// not implemented yet */ + } + + /* do underline */ + if (face->underline_p) + { + if (face->underline_color != nil) + [ns_lookup_indexed_color (face->underline_color, s->f) set]; + else + [col set]; + DPSmoveto (context, r.origin.x, r.origin.y + font->underpos); + DPSlineto (context, r.origin.x+r.size.width, r.origin.y+font->underpos); + if (face->underline_color != nil) + [col set]; + } + else + [col set]; + + /* draw with DPSxshow () */ + DPSmoveto (context, r.origin.x, r.origin.y); + DPSxshow (context, cbuf, advances, len); + DPSstroke (context); + + DPSgrestore (context); + return to-from; + } + +#else /* NS_IMPL_COCOA */ + { + CGContextRef gcontext = + [[NSGraphicsContext currentContext] graphicsPort]; + static CGAffineTransform fliptf; + static BOOL firstTime = YES; + + if (firstTime) + { + firstTime = NO; + fliptf = CGAffineTransformMakeScale (1.0, -1.0); + } + + CGContextSaveGState (gcontext); + + fliptf.c = font->synthItal ? Fix2X (kATSItalicQDSkew) : 0.0; + + CGContextSetFont (gcontext, font->cgfont); + CGContextSetFontSize (gcontext, font->size); + if (ns_antialias_text == NO || font->size <= ns_antialias_threshold) + CGContextSetShouldAntialias (gcontext, 0); + else + CGContextSetShouldAntialias (gcontext, 1); + if (ns_use_qd_smoothing) + CGContextSetFontRenderingMode (gcontext, 2); /* 0 is Cocoa, 2 is QD */ + + CGContextSetTextMatrix (gcontext, fliptf); + + if (bgCol != nil) + { + /* foreground drawing; erase first to avoid overstrike */ + [bgCol set]; + CGContextSetTextDrawingMode (gcontext, kCGTextFillStroke); + CGContextSetTextPosition (gcontext, r.origin.x, r.origin.y); + CGContextShowGlyphsWithAdvances (gcontext, s->char2b, advances, len); + CGContextSetTextDrawingMode (gcontext, kCGTextFill); + } + + if (face->underline_p) + { + if (face->underline_color != nil) + [ns_lookup_indexed_color (face->underline_color, s->f) set]; + else + [col set]; + CGContextBeginPath (gcontext); + CGContextMoveToPoint (gcontext, + r.origin.x, r.origin.y + font->underpos); + CGContextAddLineToPoint (gcontext, r.origin.x + r.size.width, + r.origin.y + font->underpos); + CGContextStrokePath (gcontext); + if (face->underline_color != nil) + [col set]; + } + else + [col set]; + + CGContextSetTextPosition (gcontext, r.origin.x, r.origin.y); + CGContextShowGlyphsWithAdvances (gcontext, s->char2b + s->gidx, + advances, len); + + if (face->overstrike) + { + CGContextSetTextPosition (gcontext, r.origin.x+0.5, r.origin.y); + CGContextShowGlyphsWithAdvances (gcontext, s->char2b + s->gidx, + advances, len); + } + + CGContextRestoreGState (gcontext); + return; + } +#endif /* NS_IMPL_COCOA */ + +} + + +/* Auto-creates a fontset built around the font in font_object, + by creating an attributed string with characters from each + script, then requesting the NS text system to fix attributes + in range. */ +void nsfont_make_fontset_for_font (Lisp_Object name, Lisp_Object font_object) +{ + Lisp_Object script, famAndReg; + struct nsfont_info *font_info = + (struct nsfont_info *)XFONT_OBJECT (font_object); + + /* NS text system (and char buf) init */ + static NSTextStorage *store; + static NSLayoutManager *layout; + static NSRange range; + static NSMutableDictionary *attribs; + static Lisp_Object *scripts; + static int nscripts; + static int *scriptsNchars; + static BOOL firstTime = YES; + Lisp_Object regString = build_string ("iso10646-1"); + int i, j; + + if (firstTime == YES) + { + nscripts = XINT (Flength (Vscript_representative_chars)); + scriptsNchars = (int *) malloc (nscripts * sizeof (int)); + unsigned char *buf = malloc (4*nscripts*sizeof (char)); + Lisp_Object scriptsChars = Vscript_representative_chars; + unsigned char *tpos = buf; + + scripts = (Lisp_Object *) malloc (nscripts * sizeof (Lisp_Object)); + + for (i =0; i<nscripts; i++) + { + Lisp_Object sChars = XCAR (scriptsChars); + Lisp_Object chars = XCDR (sChars); + unsigned int ch, c =0; + scripts[i] = XCAR (sChars); + + while (CONSP (chars)) + { + ch = XUINT (XCAR (chars)); + chars = XCDR (chars); + CHAR_STRING_ADVANCE (ch, tpos); + c++; + } + scriptsNchars[i] = c; + + scriptsChars = XCDR (scriptsChars); + } + *tpos = '\0'; + + store = [[NSTextStorage alloc] init]; + layout = [[NSLayoutManager alloc] init]; + [store addLayoutManager: layout]; + [layout release]; + + [store beginEditing]; + [[store mutableString] appendString: + [NSString stringWithUTF8String: buf]]; + [store endEditing]; + + free (buf); + range = NSMakeRange (0, [store length]); + + attribs = [[NSMutableDictionary alloc] init]; + firstTime = NO; + } + + /* set the fonts */ + [store beginEditing]; + [store removeAttribute: NSFontAttributeName range: range]; + [attribs setObject: font_info->nsfont forKey: NSFontAttributeName]; + [store addAttributes: attribs range: range]; + [store endEditing]; + + /* read them out */ + { + NSMutableDictionary *map = + [NSMutableDictionary dictionaryWithCapacity: nscripts * 4]; + NSEnumerator *fonts; + NSFont *cfont = nil, *tfont; + NSNumber *n; + int idx = 0; + int max; + for (i =0; i<nscripts; i++) + { + [map removeAllObjects]; + for (j =0; j<scriptsNchars[i]; j++) + { + cfont = [store attribute: NSFontAttributeName atIndex: idx++ + effectiveRange: NULL]; + n = [map objectForKey: cfont]; + if (n == nil) + n = [NSNumber numberWithInt: 1]; + else + n = [NSNumber numberWithInt: [n intValue] + 1]; + [map setObject: n forKey: cfont]; + } + + /* majority rules */ + max = 0; + fonts = [map keyEnumerator]; + while (tfont = [fonts nextObject]) + { + n = [map objectForKey: tfont]; + if ([n intValue] > max) + { + cfont = tfont; + max = [n intValue]; + } + } + + if (cfont != nil) + { + char *family = [[cfont familyName] UTF8String]; + Lisp_Object famAndReg; + + nsfont_escape_name (family); + famAndReg = Fcons (build_string (family), regString); + + if (NSFONT_TRACE) + fprintf (stderr, "%s fontset: use '%s' for script '%s'\n", + font_info->name, family, + SDATA (SYMBOL_NAME (scripts[i]))); + + Fset_fontset_font (name, scripts[i], famAndReg, Qnil, Qnil); + } + else + { + fprintf (stderr, "%s fontset: none found for script '%s'\n", + font_info->name, SDATA (SYMBOL_NAME (scripts[i]))); + } + } /* for i = script */ + } +} + + + +/* ========================================================================== + + Font glyph and metrics caching functions + + ========================================================================== */ + +/* Find and cache corresponding glyph codes for unicode values in given + hi-byte block of 256. */ +static void +ns_uni_to_glyphs (struct nsfont_info *font_info, unsigned char block) +{ +#ifdef NS_IMPL_COCOA + static EmacsGlyphStorage *glyphStorage; + static char firstTime = 1; +#endif + unichar *unichars = xmalloc (0x101 * sizeof (unichar)); + unsigned int i, g, idx; + unsigned short *glyphs; + + if (NSFONT_TRACE) + fprintf (stderr, "%p\tFinding glyphs for glyphs in block %d\n", + font_info, block); + + BLOCK_INPUT; + +#ifdef NS_IMPL_COCOA + if (firstTime) + { + firstTime = 0; + glyphStorage = [[EmacsGlyphStorage alloc] initWithCapacity: 0x100]; + } +#endif + + font_info->glyphs[block] = xmalloc (0x100 * sizeof (unsigned short)); + if (!unichars || !(font_info->glyphs[block])) + abort (); + + /* create a string containing all unicode characters in this block */ + for (idx = block<<8, i =0; i<0x100; idx++, i++) + if (idx < 0xD800 || idx > 0xDFFF) + unichars[i] = idx; + else + unichars[i] = 0xFEFF; + unichars[0x100] = 0; + + { +#ifdef NS_IMPL_COCOA + NSString *allChars = [[NSString alloc] + initWithCharactersNoCopy: unichars + length: 0x100 + freeWhenDone: NO]; + NSGlyphGenerator *glyphGenerator = [NSGlyphGenerator sharedGlyphGenerator]; + /*NSCharacterSet *coveredChars = [nsfont coveredCharacterSet]; */ + unsigned int numGlyphs = [font_info->nsfont numberOfGlyphs]; + unsigned int gInd =0, cInd =0; + + [glyphStorage setString: allChars font: font_info->nsfont]; + [glyphGenerator generateGlyphsForGlyphStorage: glyphStorage + desiredNumberOfCharacters: glyphStorage->maxChar + glyphIndex: &gInd characterIndex: &cInd]; +#endif + glyphs = font_info->glyphs[block]; + for (i =0; i<0x100; i++, glyphs++) + { +#ifdef NS_IMPL_GNUSTEP + g = unichars[i]; +#else + g = glyphStorage->cglyphs[i]; + /*PENDING: is this a good check? maybe need to use coveredChars.. */ + if (g > numGlyphs) + g = 0xFFFF; /* hopefully unused... */ +#endif + *glyphs = g; + } + +#ifdef NS_IMPL_COCOA + [allChars release]; +#endif + } + + UNBLOCK_INPUT; + xfree (unichars); +} + + +/* Determine and cache metrics for corresponding glyph codes in given + hi-byte block of 256. */ +static void +ns_glyph_metrics (struct nsfont_info *font_info, unsigned char block) +{ + unsigned int i, g; + unsigned int numGlyphs = [font_info->nsfont numberOfGlyphs]; + NSFont *sfont; + struct font_metrics *metrics; + + if (NSFONT_TRACE) + fprintf (stderr, "%p\tComputing metrics for glyphs in block %d\n", + font_info, block); + +#ifdef NS_IMPL_GNUSTEP + /* not implemented yet (as of startup 0.18), so punt */ + if (numGlyphs == 0) + numGlyphs = 0x10000; +#endif + + BLOCK_INPUT; + sfont = [font_info->nsfont screenFont]; + + font_info->metrics[block] = xmalloc (0x100 * sizeof (struct font_metrics)); + bzero (font_info->metrics[block], 0x100 * sizeof (struct font_metrics)); + if (!(font_info->metrics[block])) + abort (); + + metrics = font_info->metrics[block]; + for (g = block<<8, i =0; i<0x100 && g < numGlyphs; g++, i++, metrics++) + { + float w, lb, rb; + NSRect r = [sfont boundingRectForGlyph: g]; + +#ifdef NS_IMPL_GNUSTEP + { + /* lord help us */ + NSString *s = [NSString stringWithFormat: @"%c", g]; + w = [sfont widthOfString: s]; + } +#else + w = [sfont advancementForGlyph: g].width; +#endif + w = max (w, 2.0); + metrics->width = lrint (w); + + lb = r.origin.x; + rb = r.size.width - w; + if (lb < 0) + metrics->lbearing = round (lb); + if (font_info->ital) + rb += 0.22 * font_info->height; + metrics->rbearing = lrint (w + rb); + + metrics->descent = r.origin.y < 0 ? -r.origin.y : 0; + /*lrint (hshrink * [sfont ascender] + expand * hd/2); */ + metrics->ascent = r.size.height - metrics->descent; +/*-lrint (hshrink* [sfont descender] - expand * hd/2); */ + } + UNBLOCK_INPUT; +} + + +#ifdef NS_IMPL_COCOA +/* helper for font glyph setup */ +@implementation EmacsGlyphStorage + +- init +{ + return [self initWithCapacity: 1024]; +} + +- initWithCapacity: (unsigned long) c +{ + self = [super init]; + maxChar = 0; + maxGlyph = 0; + dict = [NSMutableDictionary new]; + cglyphs = (CGGlyph *)xmalloc (c * sizeof (CGGlyph)); + return self; +} + +- (void) dealloc +{ + if (attrStr != nil) + [attrStr release]; + [dict release]; + xfree (cglyphs); + [super dealloc]; +} + +- (void) setString: (NSString *)str font: (NSFont *)font +{ + [dict setObject: font forKey: NSFontAttributeName]; + attrStr = [[NSAttributedString alloc] initWithString: str attributes: dict]; + maxChar = [str length]; + maxGlyph = 0; +} + +/* NSGlyphStorage protocol */ +- (unsigned int)layoutOptions +{ + return 0; +} + +- (NSAttributedString *)attributedString +{ + return attrStr; +} + +- (void)insertGlyphs: (const NSGlyph *)glyphs length: (unsigned int)length + forStartingGlyphAtIndex: (unsigned int)glyphIndex + characterIndex: (unsigned int)charIndex +{ + len = glyphIndex+length; + for (i =glyphIndex; i<len; i++) + cglyphs[i] = glyphs[i-glyphIndex]; + if (len > maxGlyph) + maxGlyph = len; +} + +- (void)setIntAttribute: (int)attributeTag value: (int)val + forGlyphAtIndex: (unsigned)glyphIndex +{ + return; +} + +@end +#endif /* NS_IMPL_COCOA */ + + +/* Debugging */ +void +dump_glyphstring (struct glyph_string *s) +{ + int i; + + fprintf (stderr, "Glyph string len = %d at (%d, %d) overhang (%d, %d), overlap = %d, bg_filled = %d:", + s->nchars, s->x, s->y, s->left_overhang, s->right_overhang, + s->row->overlapping_p, s->background_filled_p); + for (i =0; i<s->nchars; i++) + fprintf (stderr, "%c", s->first_glyph[i].u.ch); + fprintf (stderr, "\n"); +} + + + +void +syms_of_nsfont () +{ + nsfont_driver.type = Qns; + register_font_driver (&nsfont_driver, NULL); + DEFSYM (Qapple, "apple"); + DEFSYM (Qroman, "roman"); + DEFSYM (Qmedium, "medium"); +}