mirror of
https://git.savannah.gnu.org/git/emacs.git
synced 2024-11-23 07:19:15 +00:00
6c21e306f2
(CG_SET_FILL_COLOR_WITH_FACE_FOREGROUND) (CG_SET_FILL_COLOR_WITH_FACE_BACKGROUND) (CG_SET_STROKE_COLOR_WITH_FACE_FOREGROUND): Modified from *_WITH_GC_* to take face and f as parameters. (macfont_draw): Check for DRAW_MOUSE_FACE and set face accordingly. Use *_WITH_FACE_*, and pass face as parameter. Fixes: debbugs:16425
4064 lines
114 KiB
Mathematica
4064 lines
114 KiB
Mathematica
/* Font driver on Mac OSX Core text.
|
||
Copyright (C) 2009-2014 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 of the License, 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. If not, see <http://www.gnu.org/licenses/>.
|
||
|
||
Original author: YAMAMOTO Mitsuharu
|
||
*/
|
||
|
||
#include <config.h>
|
||
|
||
#include "lisp.h"
|
||
#include "dispextern.h"
|
||
#include "frame.h"
|
||
#include "blockinput.h"
|
||
#include "character.h"
|
||
#include "charset.h"
|
||
#include "composite.h"
|
||
#include "fontset.h"
|
||
#include "font.h"
|
||
#include "termchar.h"
|
||
#include "nsgui.h"
|
||
#include "nsterm.h"
|
||
#include "macfont.h"
|
||
#include "macuvs.h"
|
||
|
||
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050
|
||
|
||
#include <libkern/OSByteOrder.h>
|
||
|
||
static struct font_driver macfont_driver;
|
||
|
||
/* Core Text, for Mac OS X 10.5 and later. */
|
||
static Lisp_Object Qmac_ct;
|
||
|
||
static double mac_ctfont_get_advance_width_for_glyph (CTFontRef, CGGlyph);
|
||
static CGRect mac_ctfont_get_bounding_rect_for_glyph (CTFontRef, CGGlyph);
|
||
static CFArrayRef mac_ctfont_create_available_families (void);
|
||
static Boolean mac_ctfont_equal_in_postscript_name (CTFontRef, CTFontRef);
|
||
static CTLineRef mac_ctfont_create_line_with_string_and_font (CFStringRef,
|
||
CTFontRef);
|
||
static CFComparisonResult mac_font_family_compare (const void *,
|
||
const void *, void *);
|
||
static Boolean mac_ctfont_descriptor_supports_languages (CTFontDescriptorRef,
|
||
CFArrayRef);
|
||
static CFStringRef mac_ctfont_create_preferred_family_for_attributes (CFDictionaryRef);
|
||
static CFIndex mac_ctfont_shape (CTFontRef, CFStringRef,
|
||
struct mac_glyph_layout *, CFIndex);
|
||
static CFArrayRef
|
||
mac_font_copy_default_descriptors_for_language (CFStringRef language);
|
||
|
||
static CFStringRef
|
||
mac_font_copy_default_name_for_charset_and_languages (CFCharacterSetRef charset,
|
||
CFArrayRef languages);
|
||
|
||
#if USE_CT_GLYPH_INFO
|
||
static CGGlyph mac_ctfont_get_glyph_for_cid (CTFontRef,
|
||
CTCharacterCollection,
|
||
CGFontIndex);
|
||
#endif
|
||
|
||
/* The font property key specifying the font design destination. The
|
||
value is an unsigned integer code: 0 for WYSIWYG, and 1 for Video
|
||
text. (See the documentation of X Logical Font Description
|
||
Conventions.) In the Mac font driver, 1 means the screen font is
|
||
used for calculating some glyph metrics. You can see the
|
||
difference with Monaco 8pt or 9pt, for example. */
|
||
static Lisp_Object QCdestination;
|
||
|
||
/* The boolean-valued font property key specifying the use of
|
||
leading. */
|
||
static Lisp_Object QCminspace;
|
||
|
||
struct macfont_metrics;
|
||
|
||
/* The actual structure for Mac font that can be cast to struct font. */
|
||
|
||
struct macfont_info
|
||
{
|
||
struct font font;
|
||
FontRef macfont;
|
||
CGFontRef cgfont;
|
||
ScreenFontRef screen_font;
|
||
struct macfont_cache *cache;
|
||
struct macfont_metrics **metrics;
|
||
short metrics_nrows;
|
||
bool_bf synthetic_italic_p : 1;
|
||
bool_bf synthetic_bold_p : 1;
|
||
unsigned spacing : 2;
|
||
unsigned antialias : 2;
|
||
bool_bf color_bitmap_p : 1;
|
||
};
|
||
|
||
/* Values for the `spacing' member in `struct macfont_info'. */
|
||
|
||
enum
|
||
{
|
||
MACFONT_SPACING_PROPORTIONAL,
|
||
MACFONT_SPACING_MONO,
|
||
MACFONT_SPACING_SYNTHETIC_MONO,
|
||
};
|
||
|
||
/* Values for the `antialias' member in `struct macfont_info'. */
|
||
|
||
enum
|
||
{
|
||
MACFONT_ANTIALIAS_DEFAULT,
|
||
MACFONT_ANTIALIAS_OFF,
|
||
MACFONT_ANTIALIAS_ON,
|
||
};
|
||
|
||
enum {FONT_SLANT_SYNTHETIC_ITALIC = 200}; /* FC_SLANT_ITALIC + 100 */
|
||
enum {FONT_WEIGHT_SYNTHETIC_BOLD = 200}; /* FC_WEIGHT_BOLD */
|
||
enum {FONT_SPACING_SYNTHETIC_MONO = FONT_SPACING_MONO};
|
||
|
||
static const CGAffineTransform synthetic_italic_atfm = {1, 0, 0.25, 1, 0, 0};
|
||
static const CGFloat synthetic_bold_factor = 0.024;
|
||
|
||
static Boolean cfnumber_get_font_symbolic_traits_value (CFNumberRef,
|
||
FontSymbolicTraits *);
|
||
static void macfont_store_descriptor_attributes (FontDescriptorRef,
|
||
Lisp_Object);
|
||
static Lisp_Object macfont_descriptor_entity (FontDescriptorRef,
|
||
Lisp_Object,
|
||
FontSymbolicTraits);
|
||
static CFStringRef macfont_create_family_with_symbol (Lisp_Object);
|
||
static int macfont_glyph_extents (struct font *, CGGlyph,
|
||
struct font_metrics *, CGFloat *, int);
|
||
static CFMutableDictionaryRef macfont_create_attributes_with_spec (Lisp_Object);
|
||
static Boolean macfont_supports_charset_and_languages_p (FontDescriptorRef,
|
||
CFCharacterSetRef,
|
||
Lisp_Object,
|
||
CFArrayRef);
|
||
static CFIndex macfont_closest_traits_index (CFArrayRef,
|
||
FontSymbolicTraits);
|
||
static CFDataRef mac_font_copy_uvs_table (FontRef);
|
||
static void mac_font_get_glyphs_for_variants (CFDataRef, UTF32Char,
|
||
const UTF32Char [],
|
||
CGGlyph [], CFIndex);
|
||
|
||
/* From CFData to a lisp string. Always returns a unibyte string. */
|
||
|
||
static Lisp_Object
|
||
cfdata_to_lisp (CFDataRef data)
|
||
{
|
||
CFIndex len = CFDataGetLength (data);
|
||
Lisp_Object result = make_uninit_string (len);
|
||
|
||
CFDataGetBytes (data, CFRangeMake (0, len), SDATA (result));
|
||
|
||
return result;
|
||
}
|
||
|
||
|
||
|
||
/* From CFString to a lisp string. Returns a unibyte string
|
||
containing a UTF-8 byte sequence. */
|
||
|
||
static Lisp_Object
|
||
cfstring_to_lisp_nodecode (CFStringRef string)
|
||
{
|
||
Lisp_Object result = Qnil;
|
||
CFDataRef data;
|
||
const char *s = CFStringGetCStringPtr (string, kCFStringEncodingUTF8);
|
||
|
||
if (s)
|
||
{
|
||
CFIndex i, length = CFStringGetLength (string);
|
||
|
||
for (i = 0; i < length; i++)
|
||
if (CFStringGetCharacterAtIndex (string, i) == 0)
|
||
break;
|
||
|
||
if (i == length)
|
||
return make_unibyte_string (s, strlen (s));
|
||
}
|
||
|
||
data = CFStringCreateExternalRepresentation (NULL, string,
|
||
kCFStringEncodingUTF8, '?');
|
||
if (data)
|
||
{
|
||
result = cfdata_to_lisp (data);
|
||
CFRelease (data);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/* Lisp string containing a UTF-8 byte sequence to CFString. Unlike
|
||
cfstring_create_with_utf8_cstring, this function preserves NUL
|
||
characters. */
|
||
|
||
static CFStringRef
|
||
cfstring_create_with_string_noencode (Lisp_Object s)
|
||
{
|
||
CFStringRef string = CFStringCreateWithBytes (NULL, SDATA (s), SBYTES (s),
|
||
kCFStringEncodingUTF8, false);
|
||
|
||
if (string == NULL)
|
||
/* Failed to interpret as UTF 8. Fall back on Mac Roman. */
|
||
string = CFStringCreateWithBytes (NULL, SDATA (s), SBYTES (s),
|
||
kCFStringEncodingMacRoman, false);
|
||
|
||
return string;
|
||
}
|
||
|
||
static CGFloat
|
||
mac_screen_font_get_advance_width_for_glyph (ScreenFontRef font, CGGlyph glyph)
|
||
{
|
||
NSSize advancement = [(NSFont *)font advancementForGlyph:glyph];
|
||
|
||
return advancement.width;
|
||
}
|
||
|
||
static CGGlyph
|
||
mac_font_get_glyph_for_cid (FontRef font, CharacterCollection collection,
|
||
CGFontIndex cid)
|
||
{
|
||
#if USE_CT_GLYPH_INFO
|
||
return mac_ctfont_get_glyph_for_cid ((CTFontRef) font, collection, cid);
|
||
#else
|
||
{
|
||
CGGlyph result = kCGFontIndexInvalid;
|
||
NSFont *nsFont = (NSFont *) font;
|
||
unichar characters[] = {0xfffd};
|
||
NSString *string =
|
||
[NSString stringWithCharacters:characters
|
||
length:(sizeof (characters)
|
||
/ sizeof (characters[0]))];
|
||
NSGlyphInfo *glyphInfo =
|
||
[NSGlyphInfo glyphInfoWithCharacterIdentifier:cid
|
||
collection:collection
|
||
baseString:string];
|
||
NSDictionary *attributes =
|
||
[NSDictionary dictionaryWithObjectsAndKeys:nsFont,NSFontAttributeName,
|
||
glyphInfo,NSGlyphInfoAttributeName,nil];
|
||
NSTextStorage *textStorage =
|
||
[[NSTextStorage alloc] initWithString:string
|
||
attributes:attributes];
|
||
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
|
||
NSTextContainer *textContainer = [[NSTextContainer alloc] init];
|
||
NSFont *fontInTextStorage;
|
||
|
||
[layoutManager addTextContainer:textContainer];
|
||
[textContainer release];
|
||
[textStorage addLayoutManager:layoutManager];
|
||
[layoutManager release];
|
||
|
||
/* Force layout. */
|
||
(void) [layoutManager glyphRangeForTextContainer:textContainer];
|
||
|
||
fontInTextStorage = [textStorage attribute:NSFontAttributeName atIndex:0
|
||
effectiveRange:NULL];
|
||
if (fontInTextStorage == nsFont
|
||
|| [[fontInTextStorage fontName] isEqualToString:[nsFont fontName]])
|
||
{
|
||
NSGlyph glyph = [layoutManager glyphAtIndex:0];
|
||
|
||
if (glyph < [nsFont numberOfGlyphs])
|
||
result = glyph;
|
||
}
|
||
|
||
[textStorage release];
|
||
|
||
return result;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
static ScreenFontRef
|
||
mac_screen_font_create_with_name (CFStringRef name, CGFloat size)
|
||
{
|
||
NSFont *result, *font;
|
||
|
||
font = [NSFont fontWithName:((NSString *) name) size:size];
|
||
result = [font screenFont];
|
||
|
||
return (ScreenFontRef)[result retain];
|
||
}
|
||
|
||
|
||
static Boolean
|
||
mac_screen_font_get_metrics (ScreenFontRef font, CGFloat *ascent,
|
||
CGFloat *descent, CGFloat *leading)
|
||
{
|
||
NSFont *nsFont = [(NSFont *)font printerFont];
|
||
NSTextStorage *textStorage;
|
||
NSLayoutManager *layoutManager;
|
||
NSTextContainer *textContainer;
|
||
NSRect usedRect;
|
||
NSPoint spaceLocation;
|
||
CGFloat descender;
|
||
|
||
textStorage = [[NSTextStorage alloc] initWithString:@" "];
|
||
layoutManager = [[NSLayoutManager alloc] init];
|
||
textContainer = [[NSTextContainer alloc] init];
|
||
|
||
[textStorage setFont:nsFont];
|
||
[textContainer setLineFragmentPadding:0];
|
||
[layoutManager setUsesScreenFonts:YES];
|
||
|
||
[layoutManager addTextContainer:textContainer];
|
||
[textContainer release];
|
||
[textStorage addLayoutManager:layoutManager];
|
||
[layoutManager release];
|
||
|
||
if (!(textStorage && layoutManager && textContainer))
|
||
{
|
||
[textStorage release];
|
||
|
||
return false;
|
||
}
|
||
|
||
usedRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:0
|
||
effectiveRange:NULL];
|
||
spaceLocation = [layoutManager locationForGlyphAtIndex:0];
|
||
[textStorage release];
|
||
|
||
*ascent = spaceLocation.y;
|
||
*descent = NSHeight (usedRect) - spaceLocation.y;
|
||
*leading = 0;
|
||
descender = [nsFont descender];
|
||
if (- descender < *descent)
|
||
{
|
||
*leading = *descent + descender;
|
||
*descent = - descender;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
static CFIndex
|
||
mac_font_shape_1 (NSFont *font, NSString *string,
|
||
struct mac_glyph_layout *glyph_layouts, CFIndex glyph_len,
|
||
BOOL screen_font_p)
|
||
{
|
||
NSUInteger i;
|
||
CFIndex result = 0;
|
||
NSTextStorage *textStorage;
|
||
NSLayoutManager *layoutManager;
|
||
NSTextContainer *textContainer;
|
||
NSUInteger stringLength;
|
||
NSPoint spaceLocation;
|
||
NSUInteger used, numberOfGlyphs;
|
||
|
||
textStorage = [[NSTextStorage alloc] initWithString:string];
|
||
layoutManager = [[NSLayoutManager alloc] init];
|
||
textContainer = [[NSTextContainer alloc] init];
|
||
|
||
/* Append a trailing space to measure baseline position. */
|
||
[textStorage appendAttributedString:([[[NSAttributedString alloc]
|
||
initWithString:@" "] autorelease])];
|
||
[textStorage setFont:font];
|
||
[textContainer setLineFragmentPadding:0];
|
||
[layoutManager setUsesScreenFonts:screen_font_p];
|
||
|
||
[layoutManager addTextContainer:textContainer];
|
||
[textContainer release];
|
||
[textStorage addLayoutManager:layoutManager];
|
||
[layoutManager release];
|
||
|
||
if (!(textStorage && layoutManager && textContainer))
|
||
{
|
||
[textStorage release];
|
||
|
||
return 0;
|
||
}
|
||
|
||
stringLength = [string length];
|
||
|
||
/* Force layout. */
|
||
(void) [layoutManager glyphRangeForTextContainer:textContainer];
|
||
|
||
spaceLocation = [layoutManager locationForGlyphAtIndex:stringLength];
|
||
|
||
/* Remove the appended trailing space because otherwise it may
|
||
generate a wrong result for a right-to-left text. */
|
||
[textStorage beginEditing];
|
||
[textStorage deleteCharactersInRange:(NSMakeRange (stringLength, 1))];
|
||
[textStorage endEditing];
|
||
(void) [layoutManager glyphRangeForTextContainer:textContainer];
|
||
|
||
i = 0;
|
||
while (i < stringLength)
|
||
{
|
||
NSRange range;
|
||
NSFont *fontInTextStorage =
|
||
[textStorage attribute:NSFontAttributeName atIndex:i
|
||
longestEffectiveRange:&range
|
||
inRange:(NSMakeRange (0, stringLength))];
|
||
|
||
if (!(fontInTextStorage == font
|
||
|| [[fontInTextStorage fontName] isEqualToString:[font fontName]]))
|
||
break;
|
||
i = NSMaxRange (range);
|
||
}
|
||
if (i < stringLength)
|
||
/* Make the test `used <= glyph_len' below fail if textStorage
|
||
contained some fonts other than the specified one. */
|
||
used = glyph_len + 1;
|
||
else
|
||
{
|
||
NSRange range = NSMakeRange (0, stringLength);
|
||
|
||
range = [layoutManager glyphRangeForCharacterRange:range
|
||
actualCharacterRange:NULL];
|
||
numberOfGlyphs = NSMaxRange (range);
|
||
used = numberOfGlyphs;
|
||
for (i = 0; i < numberOfGlyphs; i++)
|
||
if ([layoutManager notShownAttributeForGlyphAtIndex:i])
|
||
used--;
|
||
}
|
||
|
||
if (0 < used && used <= glyph_len)
|
||
{
|
||
NSUInteger glyphIndex, prevGlyphIndex;
|
||
unsigned char bidiLevel;
|
||
NSUInteger *permutation;
|
||
NSRange compRange, range;
|
||
CGFloat totalAdvance;
|
||
|
||
glyphIndex = 0;
|
||
while ([layoutManager notShownAttributeForGlyphAtIndex:glyphIndex])
|
||
glyphIndex++;
|
||
|
||
/* For now we assume the direction is not changed within the
|
||
string. */
|
||
[layoutManager getGlyphsInRange:(NSMakeRange (glyphIndex, 1))
|
||
glyphs:NULL characterIndexes:NULL
|
||
glyphInscriptions:NULL elasticBits:NULL
|
||
bidiLevels:&bidiLevel];
|
||
if (bidiLevel & 1)
|
||
permutation = xmalloc (sizeof (NSUInteger) * used);
|
||
else
|
||
permutation = NULL;
|
||
|
||
#define RIGHT_TO_LEFT_P permutation
|
||
|
||
/* Fill the `comp_range' member of struct mac_glyph_layout, and
|
||
setup a permutation for right-to-left text. */
|
||
compRange = NSMakeRange (0, 0);
|
||
for (range = NSMakeRange (0, 0); NSMaxRange (range) < used;
|
||
range.length++)
|
||
{
|
||
struct mac_glyph_layout *gl = glyph_layouts + NSMaxRange (range);
|
||
NSUInteger characterIndex =
|
||
[layoutManager characterIndexForGlyphAtIndex:glyphIndex];
|
||
|
||
gl->string_index = characterIndex;
|
||
|
||
if (characterIndex >= NSMaxRange (compRange))
|
||
{
|
||
compRange.location = NSMaxRange (compRange);
|
||
do
|
||
{
|
||
NSRange characterRange =
|
||
[string
|
||
rangeOfComposedCharacterSequenceAtIndex:characterIndex];
|
||
|
||
compRange.length =
|
||
NSMaxRange (characterRange) - compRange.location;
|
||
[layoutManager glyphRangeForCharacterRange:compRange
|
||
actualCharacterRange:&characterRange];
|
||
characterIndex = NSMaxRange (characterRange) - 1;
|
||
}
|
||
while (characterIndex >= NSMaxRange (compRange));
|
||
|
||
if (RIGHT_TO_LEFT_P)
|
||
for (i = 0; i < range.length; i++)
|
||
permutation[range.location + i] = NSMaxRange (range) - i - 1;
|
||
|
||
range = NSMakeRange (NSMaxRange (range), 0);
|
||
}
|
||
|
||
gl->comp_range.location = compRange.location;
|
||
gl->comp_range.length = compRange.length;
|
||
|
||
while (++glyphIndex < numberOfGlyphs)
|
||
if (![layoutManager notShownAttributeForGlyphAtIndex:glyphIndex])
|
||
break;
|
||
}
|
||
if (RIGHT_TO_LEFT_P)
|
||
for (i = 0; i < range.length; i++)
|
||
permutation[range.location + i] = NSMaxRange (range) - i - 1;
|
||
|
||
/* Then fill the remaining members. */
|
||
glyphIndex = prevGlyphIndex = 0;
|
||
while ([layoutManager notShownAttributeForGlyphAtIndex:glyphIndex])
|
||
glyphIndex++;
|
||
|
||
if (!RIGHT_TO_LEFT_P)
|
||
totalAdvance = 0;
|
||
else
|
||
{
|
||
NSUInteger nrects;
|
||
NSRect *glyphRects =
|
||
[layoutManager
|
||
rectArrayForGlyphRange:(NSMakeRange (0, numberOfGlyphs))
|
||
withinSelectedGlyphRange:(NSMakeRange (NSNotFound, 0))
|
||
inTextContainer:textContainer rectCount:&nrects];
|
||
|
||
totalAdvance = NSMaxX (glyphRects[0]);
|
||
}
|
||
|
||
for (i = 0; i < used; i++)
|
||
{
|
||
struct mac_glyph_layout *gl;
|
||
NSPoint location;
|
||
NSUInteger nextGlyphIndex;
|
||
NSRange glyphRange;
|
||
NSRect *glyphRects;
|
||
NSUInteger nrects;
|
||
|
||
if (!RIGHT_TO_LEFT_P)
|
||
gl = glyph_layouts + i;
|
||
else
|
||
{
|
||
NSUInteger dest = permutation[i];
|
||
|
||
gl = glyph_layouts + dest;
|
||
if (i < dest)
|
||
{
|
||
CFIndex tmp = gl->string_index;
|
||
|
||
gl->string_index = glyph_layouts[i].string_index;
|
||
glyph_layouts[i].string_index = tmp;
|
||
}
|
||
}
|
||
gl->glyph_id = [layoutManager glyphAtIndex:glyphIndex];
|
||
|
||
location = [layoutManager locationForGlyphAtIndex:glyphIndex];
|
||
gl->baseline_delta = spaceLocation.y - location.y;
|
||
|
||
for (nextGlyphIndex = glyphIndex + 1; nextGlyphIndex < numberOfGlyphs;
|
||
nextGlyphIndex++)
|
||
if (![layoutManager
|
||
notShownAttributeForGlyphAtIndex:nextGlyphIndex])
|
||
break;
|
||
|
||
if (!RIGHT_TO_LEFT_P)
|
||
{
|
||
CGFloat maxX;
|
||
|
||
if (prevGlyphIndex == 0)
|
||
glyphRange = NSMakeRange (0, nextGlyphIndex);
|
||
else
|
||
glyphRange = NSMakeRange (glyphIndex,
|
||
nextGlyphIndex - glyphIndex);
|
||
glyphRects =
|
||
[layoutManager
|
||
rectArrayForGlyphRange:glyphRange
|
||
withinSelectedGlyphRange:(NSMakeRange (NSNotFound, 0))
|
||
inTextContainer:textContainer rectCount:&nrects];
|
||
maxX = max (NSMaxX (glyphRects[0]), totalAdvance);
|
||
gl->advance_delta = location.x - totalAdvance;
|
||
gl->advance = maxX - totalAdvance;
|
||
totalAdvance = maxX;
|
||
}
|
||
else
|
||
{
|
||
CGFloat minX;
|
||
|
||
if (nextGlyphIndex == numberOfGlyphs)
|
||
glyphRange = NSMakeRange (prevGlyphIndex,
|
||
numberOfGlyphs - prevGlyphIndex);
|
||
else
|
||
glyphRange = NSMakeRange (prevGlyphIndex,
|
||
glyphIndex + 1 - prevGlyphIndex);
|
||
glyphRects =
|
||
[layoutManager
|
||
rectArrayForGlyphRange:glyphRange
|
||
withinSelectedGlyphRange:(NSMakeRange (NSNotFound, 0))
|
||
inTextContainer:textContainer rectCount:&nrects];
|
||
minX = min (NSMinX (glyphRects[0]), totalAdvance);
|
||
gl->advance = totalAdvance - minX;
|
||
totalAdvance = minX;
|
||
gl->advance_delta = location.x - totalAdvance;
|
||
}
|
||
|
||
prevGlyphIndex = glyphIndex + 1;
|
||
glyphIndex = nextGlyphIndex;
|
||
}
|
||
|
||
if (RIGHT_TO_LEFT_P)
|
||
xfree (permutation);
|
||
|
||
#undef RIGHT_TO_LEFT_P
|
||
|
||
result = used;
|
||
}
|
||
[textStorage release];
|
||
|
||
return result;
|
||
}
|
||
|
||
static CFIndex
|
||
mac_screen_font_shape (ScreenFontRef font, CFStringRef string,
|
||
struct mac_glyph_layout *glyph_layouts,
|
||
CFIndex glyph_len)
|
||
{
|
||
return mac_font_shape_1 ([(NSFont *)font printerFont],
|
||
(NSString *) string,
|
||
glyph_layouts, glyph_len, YES);
|
||
}
|
||
|
||
static CGColorRef
|
||
get_cgcolor(unsigned long idx, struct frame *f)
|
||
{
|
||
NSColor *nsColor = ns_lookup_indexed_color (idx, f);
|
||
[nsColor set];
|
||
CGColorSpaceRef colorSpace = [[nsColor colorSpace] CGColorSpace];
|
||
NSInteger noc = [nsColor numberOfComponents];
|
||
CGFloat *components = xmalloc (sizeof(CGFloat)*(1+noc));
|
||
CGColorRef cgColor;
|
||
|
||
[nsColor getComponents: components];
|
||
cgColor = CGColorCreate (colorSpace, components);
|
||
xfree (components);
|
||
return cgColor;
|
||
}
|
||
|
||
#define CG_SET_FILL_COLOR_WITH_FACE_FOREGROUND(context, face, f) \
|
||
do { \
|
||
CGColorRef refcol_ = get_cgcolor (NS_FACE_FOREGROUND (face), f); \
|
||
CGContextSetFillColorWithColor (context, refcol_) ; \
|
||
CGColorRelease (refcol_); \
|
||
} while (0)
|
||
#define CG_SET_FILL_COLOR_WITH_FACE_BACKGROUND(context, face, f) \
|
||
do { \
|
||
CGColorRef refcol_ = get_cgcolor (NS_FACE_BACKGROUND (face), f); \
|
||
CGContextSetFillColorWithColor (context, refcol_); \
|
||
CGColorRelease (refcol_); \
|
||
} while (0)
|
||
#define CG_SET_STROKE_COLOR_WITH_FACE_FOREGROUND(context, face, f) \
|
||
do { \
|
||
CGColorRef refcol_ = get_cgcolor (NS_FACE_FOREGROUND (face), f); \
|
||
CGContextSetStrokeColorWithColor (context, refcol_); \
|
||
CGColorRelease (refcol_); \
|
||
} while (0)
|
||
|
||
|
||
/* Mac font driver. */
|
||
|
||
static struct
|
||
{
|
||
/* registry name */
|
||
const char *name;
|
||
/* characters to distinguish the charset from the others */
|
||
int uniquifier[6];
|
||
/* additional constraint by language */
|
||
CFStringRef lang;
|
||
/* set on demand */
|
||
CFCharacterSetRef cf_charset;
|
||
CFStringRef cf_charset_string;
|
||
} cf_charset_table[] =
|
||
{ { "iso8859-1", { 0x00A0, 0x00A1, 0x00B4, 0x00BC, 0x00D0 } },
|
||
{ "iso8859-2", { 0x00A0, 0x010E }},
|
||
{ "iso8859-3", { 0x00A0, 0x0108 }},
|
||
{ "iso8859-4", { 0x00A0, 0x00AF, 0x0128, 0x0156, 0x02C7 }},
|
||
{ "iso8859-5", { 0x00A0, 0x0401 }},
|
||
{ "iso8859-6", { 0x00A0, 0x060C }},
|
||
{ "iso8859-7", { 0x00A0, 0x0384 }},
|
||
{ "iso8859-8", { 0x00A0, 0x05D0 }},
|
||
{ "iso8859-9", { 0x00A0, 0x00A1, 0x00BC, 0x011E }},
|
||
{ "iso8859-10", { 0x00A0, 0x00D0, 0x0128, 0x2015 }},
|
||
{ "iso8859-11", { 0x00A0, 0x0E01 }},
|
||
{ "iso8859-13", { 0x00A0, 0x201C }},
|
||
{ "iso8859-14", { 0x00A0, 0x0174 }},
|
||
{ "iso8859-15", { 0x00A0, 0x00A1, 0x00D0, 0x0152 }},
|
||
{ "iso8859-16", { 0x00A0, 0x0218}},
|
||
{ "gb2312.1980-0", { 0x4E13 }, CFSTR ("zh-Hans")},
|
||
{ "big5-0", { /* 0xF6B1 in ftfont.c */ 0x4EDC }, CFSTR ("zh-Hant") },
|
||
{ "jisx0208.1983-0", { 0x4E55 }, CFSTR ("ja")},
|
||
{ "ksc5601.1987-0", { 0xAC00 }, CFSTR ("ko")},
|
||
{ "cns11643.1992-1", { 0xFE32 }, CFSTR ("zh-Hant")},
|
||
{ "cns11643.1992-2", { 0x4E33, 0x7934 }},
|
||
{ "cns11643.1992-3", { 0x201A9 }},
|
||
{ "cns11643.1992-4", { 0x20057 }},
|
||
{ "cns11643.1992-5", { 0x20000 }},
|
||
{ "cns11643.1992-6", { 0x20003 }},
|
||
{ "cns11643.1992-7", { 0x20055 }},
|
||
{ "gbk-0", { 0x4E06 }, CFSTR ("zh-Hans")},
|
||
{ "jisx0212.1990-0", { 0x4E44 }},
|
||
{ "jisx0213.2000-1", { 0xFA10 }, CFSTR ("ja")},
|
||
{ "jisx0213.2000-2", { 0xFA49 }},
|
||
{ "jisx0213.2004-1", { 0x20B9F }},
|
||
{ "viscii1.1-1", { 0x1EA0, 0x1EAE, 0x1ED2 }, CFSTR ("vi")},
|
||
{ "tis620.2529-1", { 0x0E01 }, CFSTR ("th")},
|
||
{ "windows-1251", { 0x0401, 0x0490 }, CFSTR ("ru")},
|
||
{ "koi8-r", { 0x0401, 0x2219 }, CFSTR ("ru")},
|
||
{ "mulelao-1", { 0x0E81 }, CFSTR ("lo")},
|
||
{ "unicode-sip", { 0x20000 }},
|
||
{ NULL }
|
||
};
|
||
|
||
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1080
|
||
static const struct
|
||
{
|
||
CFStringRef language;
|
||
CFStringRef font_names[3];
|
||
} macfont_language_default_font_names[] = {
|
||
{ CFSTR ("ja"), { CFSTR ("HiraKakuProN-W3"), /* 10.5 - 10.9 */
|
||
CFSTR ("HiraKakuPro-W3"), /* 10.4 */
|
||
NULL }},
|
||
{ CFSTR ("ko"), { CFSTR ("AppleSDGothicNeo-Regular"), /* 10.8 - 10.9 */
|
||
CFSTR ("AppleGothic"), /* 10.4 - 10.7 */
|
||
NULL }},
|
||
{ CFSTR ("zh-Hans"), { CFSTR ("STHeitiSC-Light"), /* 10.6 - 10.9 */
|
||
CFSTR ("STXihei"), /* 10.4 - 10.5 */
|
||
NULL }},
|
||
{ CFSTR ("zh-Hant"), { CFSTR ("STHeitiTC-Light"), /* 10.6 - 10.9 */
|
||
CFSTR ("LiHeiPro"), /* 10.4 - 10.5 */
|
||
NULL }},
|
||
{ NULL }
|
||
};
|
||
#endif
|
||
|
||
static CGFloat macfont_antialias_threshold;
|
||
|
||
static void
|
||
macfont_update_antialias_threshold (void)
|
||
{
|
||
int threshold;
|
||
Boolean valid_p;
|
||
|
||
threshold =
|
||
CFPreferencesGetAppIntegerValue (CFSTR ("AppleAntiAliasingThreshold"),
|
||
kCFPreferencesCurrentApplication,
|
||
&valid_p);
|
||
if (valid_p)
|
||
macfont_antialias_threshold = threshold;
|
||
}
|
||
|
||
static inline Lisp_Object
|
||
macfont_intern_prop_cfstring (CFStringRef cfstring)
|
||
{
|
||
Lisp_Object string = cfstring_to_lisp_nodecode (cfstring);
|
||
|
||
return font_intern_prop (SSDATA (string), SBYTES (string), 1);
|
||
}
|
||
|
||
static inline CFIndex
|
||
macfont_store_utf32char_to_unichars (UTF32Char c, UniChar *unichars)
|
||
{
|
||
if (c < 0x10000)
|
||
{
|
||
unichars[0] = c;
|
||
|
||
return 1;
|
||
}
|
||
else
|
||
{
|
||
c -= 0x10000;
|
||
unichars[0] = (c >> 10) + 0xD800;
|
||
unichars[1] = (c & 0x3FF) + 0xDC00;
|
||
|
||
return 2;
|
||
}
|
||
}
|
||
|
||
static Boolean
|
||
cfnumber_get_font_symbolic_traits_value (CFNumberRef number,
|
||
FontSymbolicTraits *sym_traits)
|
||
{
|
||
SInt64 sint64_value;
|
||
|
||
/* Getting symbolic traits with kCFNumberSInt32Type is lossy on Mac
|
||
OS 10.6 when the value is greater than or equal to 1 << 31. */
|
||
if (CFNumberGetValue (number, kCFNumberSInt64Type, &sint64_value))
|
||
{
|
||
*sym_traits = (FontSymbolicTraits) sint64_value;
|
||
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
static void
|
||
macfont_store_descriptor_attributes (FontDescriptorRef desc,
|
||
Lisp_Object spec_or_entity)
|
||
{
|
||
CFStringRef str;
|
||
CFDictionaryRef dict;
|
||
CFNumberRef num;
|
||
CGFloat floatval;
|
||
|
||
str = mac_font_descriptor_copy_attribute (desc,
|
||
MAC_FONT_FAMILY_NAME_ATTRIBUTE);
|
||
if (str)
|
||
{
|
||
ASET (spec_or_entity, FONT_FAMILY_INDEX,
|
||
macfont_intern_prop_cfstring (str));
|
||
CFRelease (str);
|
||
}
|
||
dict = mac_font_descriptor_copy_attribute (desc, MAC_FONT_TRAITS_ATTRIBUTE);
|
||
if (dict)
|
||
{
|
||
struct {
|
||
enum font_property_index index;
|
||
CFStringRef trait;
|
||
CGPoint points[6];
|
||
} numeric_traits[] =
|
||
{{FONT_WEIGHT_INDEX, MAC_FONT_WEIGHT_TRAIT,
|
||
{{-0.4, 50}, /* light */
|
||
{-0.24, 87.5}, /* (semi-light + normal) / 2 */
|
||
{0, 100}, /* normal */
|
||
{0.24, 140}, /* (semi-bold + normal) / 2 */
|
||
{0.4, 200}, /* bold */
|
||
{CGFLOAT_MAX, CGFLOAT_MAX}}},
|
||
{FONT_SLANT_INDEX, MAC_FONT_SLANT_TRAIT,
|
||
{{0, 100}, {0.1, 200}, {CGFLOAT_MAX, CGFLOAT_MAX}}},
|
||
{FONT_WIDTH_INDEX, MAC_FONT_WIDTH_TRAIT,
|
||
{{0, 100}, {1, 200}, {CGFLOAT_MAX, CGFLOAT_MAX}}}};
|
||
int i;
|
||
|
||
for (i = 0; i < sizeof (numeric_traits) / sizeof (numeric_traits[0]); i++)
|
||
{
|
||
num = CFDictionaryGetValue (dict, numeric_traits[i].trait);
|
||
if (num && CFNumberGetValue (num, kCFNumberCGFloatType, &floatval))
|
||
{
|
||
CGPoint *point = numeric_traits[i].points;
|
||
|
||
while (point->x < floatval)
|
||
point++;
|
||
if (point == numeric_traits[i].points)
|
||
point++;
|
||
else if (point->x == CGFLOAT_MAX)
|
||
point--;
|
||
floatval = (point - 1)->y + ((floatval - (point - 1)->x)
|
||
* ((point->y - (point - 1)->y)
|
||
/ (point->x - (point - 1)->x)));
|
||
FONT_SET_STYLE (spec_or_entity, numeric_traits[i].index,
|
||
make_number (lround (floatval)));
|
||
}
|
||
}
|
||
|
||
num = CFDictionaryGetValue (dict, MAC_FONT_SYMBOLIC_TRAIT);
|
||
if (num)
|
||
{
|
||
FontSymbolicTraits sym_traits;
|
||
int spacing;
|
||
|
||
cfnumber_get_font_symbolic_traits_value (num, &sym_traits);
|
||
spacing = (sym_traits & MAC_FONT_TRAIT_MONO_SPACE
|
||
? FONT_SPACING_MONO : FONT_SPACING_PROPORTIONAL);
|
||
ASET (spec_or_entity, FONT_SPACING_INDEX, make_number (spacing));
|
||
}
|
||
|
||
CFRelease (dict);
|
||
}
|
||
num = mac_font_descriptor_copy_attribute (desc, MAC_FONT_SIZE_ATTRIBUTE);
|
||
if (num && CFNumberGetValue (num, kCFNumberCGFloatType, &floatval))
|
||
ASET (spec_or_entity, FONT_SIZE_INDEX, make_number (floatval));
|
||
else
|
||
ASET (spec_or_entity, FONT_SIZE_INDEX, make_number (0));
|
||
if (num)
|
||
CFRelease (num);
|
||
}
|
||
|
||
static Lisp_Object
|
||
macfont_descriptor_entity (FontDescriptorRef desc, Lisp_Object extra,
|
||
FontSymbolicTraits synth_sym_traits)
|
||
{
|
||
Lisp_Object entity;
|
||
CFDictionaryRef dict;
|
||
FontSymbolicTraits sym_traits = 0;
|
||
CFStringRef name;
|
||
|
||
entity = font_make_entity ();
|
||
|
||
ASET (entity, FONT_TYPE_INDEX, macfont_driver.type);
|
||
ASET (entity, FONT_REGISTRY_INDEX, Qiso10646_1);
|
||
|
||
macfont_store_descriptor_attributes (desc, entity);
|
||
|
||
dict = mac_font_descriptor_copy_attribute (desc, MAC_FONT_TRAITS_ATTRIBUTE);
|
||
if (dict)
|
||
{
|
||
CFNumberRef num = CFDictionaryGetValue (dict, MAC_FONT_SYMBOLIC_TRAIT);
|
||
|
||
if (num)
|
||
cfnumber_get_font_symbolic_traits_value (num, &sym_traits);
|
||
CFRelease (dict);
|
||
}
|
||
if (EQ (AREF (entity, FONT_SIZE_INDEX), make_number (0)))
|
||
ASET (entity, FONT_AVGWIDTH_INDEX, make_number (0));
|
||
ASET (entity, FONT_EXTRA_INDEX, Fcopy_sequence (extra));
|
||
name = mac_font_descriptor_copy_attribute (desc, MAC_FONT_NAME_ATTRIBUTE);
|
||
font_put_extra (entity, QCfont_entity,
|
||
make_save_ptr_int ((void *) name, sym_traits));
|
||
if (synth_sym_traits & MAC_FONT_TRAIT_ITALIC)
|
||
FONT_SET_STYLE (entity, FONT_SLANT_INDEX,
|
||
make_number (FONT_SLANT_SYNTHETIC_ITALIC));
|
||
if (synth_sym_traits & MAC_FONT_TRAIT_BOLD)
|
||
FONT_SET_STYLE (entity, FONT_WEIGHT_INDEX,
|
||
make_number (FONT_WEIGHT_SYNTHETIC_BOLD));
|
||
if (synth_sym_traits & MAC_FONT_TRAIT_MONO_SPACE)
|
||
ASET (entity, FONT_SPACING_INDEX,
|
||
make_number (FONT_SPACING_SYNTHETIC_MONO));
|
||
|
||
return entity;
|
||
}
|
||
|
||
static CFStringRef
|
||
macfont_create_family_with_symbol (Lisp_Object symbol)
|
||
{
|
||
static CFArrayRef families = NULL;
|
||
CFStringRef result = NULL, family_name;
|
||
int using_cache_p = 1;
|
||
CFComparatorFunction family_name_comparator;
|
||
|
||
family_name = cfstring_create_with_string_noencode (SYMBOL_NAME (symbol));
|
||
if (family_name == NULL)
|
||
return NULL;
|
||
|
||
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
|
||
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
|
||
if (CTFontManagerCompareFontFamilyNames != NULL)
|
||
#endif
|
||
{
|
||
family_name_comparator = CTFontManagerCompareFontFamilyNames;
|
||
}
|
||
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
|
||
else /* CTFontManagerCompareFontFamilyNames == NULL */
|
||
#endif
|
||
#endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 */
|
||
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
|
||
{
|
||
family_name_comparator = mac_font_family_compare;
|
||
}
|
||
#endif
|
||
|
||
if ((*family_name_comparator) (family_name, CFSTR ("LastResort"), NULL)
|
||
== kCFCompareEqualTo)
|
||
result = CFSTR ("LastResort");
|
||
else
|
||
while (1)
|
||
{
|
||
CFIndex i, count;
|
||
|
||
if (families == NULL)
|
||
{
|
||
families = mac_font_create_available_families ();
|
||
using_cache_p = 0;
|
||
if (families == NULL)
|
||
break;
|
||
}
|
||
|
||
count = CFArrayGetCount (families);
|
||
i = CFArrayBSearchValues (families, CFRangeMake (0, count),
|
||
(const void *) family_name,
|
||
family_name_comparator, NULL);
|
||
if (i < count)
|
||
{
|
||
CFStringRef name = CFArrayGetValueAtIndex (families, i);
|
||
|
||
if ((*family_name_comparator) (name, family_name, NULL)
|
||
== kCFCompareEqualTo)
|
||
result = CFRetain (name);
|
||
}
|
||
|
||
if (result || !using_cache_p)
|
||
break;
|
||
else
|
||
{
|
||
CFRelease (families);
|
||
families = NULL;
|
||
}
|
||
}
|
||
|
||
CFRelease (family_name);
|
||
|
||
return result;
|
||
}
|
||
|
||
#define WIDTH_FRAC_BITS (4)
|
||
#define WIDTH_FRAC_SCALE (2 * ((1 << (WIDTH_FRAC_BITS - 1)) - 1))
|
||
|
||
struct macfont_metrics
|
||
{
|
||
unsigned char lbearing_low, rbearing_low;
|
||
signed lbearing_high : 4, rbearing_high : 4;
|
||
unsigned char ascent_low, descent_low;
|
||
signed ascent_high : 4, descent_high : 4;
|
||
|
||
/* These two members are used for fixed-point representation of
|
||
glyph width. The `width_int' member is an integer that is
|
||
closest to the width. The `width_frac' member is the fractional
|
||
adjustment representing a value in [-.5, .5], multiplied by
|
||
WIDTH_FRAC_SCALE. For synthetic monospace fonts, they represent
|
||
the advance delta for centering instead of the glyph width. */
|
||
signed width_frac : WIDTH_FRAC_BITS, width_int : 16 - WIDTH_FRAC_BITS;
|
||
};
|
||
|
||
#define METRICS_VALUE(metrics, member) \
|
||
(((metrics)->member##_high << 8) | (metrics)->member##_low)
|
||
#define METRICS_SET_VALUE(metrics, member, value) \
|
||
do {short tmp = (value); (metrics)->member##_low = tmp & 0xff; \
|
||
(metrics)->member##_high = tmp >> 8;} while (0)
|
||
|
||
enum metrics_status
|
||
{
|
||
METRICS_INVALID = -1, /* metrics entry is invalid */
|
||
METRICS_WIDTH_VALID = -2 /* width is valid but others are invalid */
|
||
};
|
||
|
||
#define METRICS_STATUS(metrics) \
|
||
(METRICS_VALUE (metrics, ascent) + METRICS_VALUE (metrics, descent))
|
||
#define METRICS_SET_STATUS(metrics, status) \
|
||
do {METRICS_SET_VALUE (metrics, ascent, 0); \
|
||
METRICS_SET_VALUE (metrics, descent, status);} while (0)
|
||
|
||
#define METRICS_NCOLS_PER_ROW (128)
|
||
#define LCD_FONT_SMOOTHING_LEFT_MARGIN (0.396f)
|
||
#define LCD_FONT_SMOOTHING_RIGHT_MARGIN (0.396f)
|
||
|
||
static int
|
||
macfont_glyph_extents (struct font *font, CGGlyph glyph,
|
||
struct font_metrics *metrics, CGFloat *advance_delta,
|
||
int force_integral_p)
|
||
{
|
||
struct macfont_info *macfont_info = (struct macfont_info *) font;
|
||
FontRef macfont = macfont_info->macfont;
|
||
int row, col;
|
||
struct macfont_metrics *cache;
|
||
int width;
|
||
|
||
row = glyph / METRICS_NCOLS_PER_ROW;
|
||
col = glyph % METRICS_NCOLS_PER_ROW;
|
||
if (row >= macfont_info->metrics_nrows)
|
||
{
|
||
macfont_info->metrics =
|
||
xrealloc (macfont_info->metrics,
|
||
sizeof (struct macfont_metrics *) * (row + 1));
|
||
memset (macfont_info->metrics + macfont_info->metrics_nrows, 0,
|
||
(sizeof (struct macfont_metrics *)
|
||
* (row + 1 - macfont_info->metrics_nrows)));
|
||
macfont_info->metrics_nrows = row + 1;
|
||
}
|
||
if (macfont_info->metrics[row] == NULL)
|
||
{
|
||
struct macfont_metrics *new;
|
||
int i;
|
||
|
||
new = xmalloc (sizeof (struct macfont_metrics) * METRICS_NCOLS_PER_ROW);
|
||
for (i = 0; i < METRICS_NCOLS_PER_ROW; i++)
|
||
METRICS_SET_STATUS (new + i, METRICS_INVALID);
|
||
macfont_info->metrics[row] = new;
|
||
}
|
||
cache = macfont_info->metrics[row] + col;
|
||
|
||
if (METRICS_STATUS (cache) == METRICS_INVALID)
|
||
{
|
||
CGFloat fwidth;
|
||
|
||
if (macfont_info->screen_font)
|
||
fwidth = mac_screen_font_get_advance_width_for_glyph (macfont_info->screen_font, glyph);
|
||
else
|
||
fwidth = mac_font_get_advance_width_for_glyph (macfont, glyph);
|
||
|
||
/* For synthetic mono fonts, cache->width_{int,frac} holds the
|
||
advance delta value. */
|
||
if (macfont_info->spacing == MACFONT_SPACING_SYNTHETIC_MONO)
|
||
fwidth = (font->pixel_size - fwidth) / 2;
|
||
cache->width_int = lround (fwidth);
|
||
cache->width_frac = lround ((fwidth - cache->width_int)
|
||
* WIDTH_FRAC_SCALE);
|
||
METRICS_SET_STATUS (cache, METRICS_WIDTH_VALID);
|
||
}
|
||
if (macfont_info->spacing == MACFONT_SPACING_SYNTHETIC_MONO)
|
||
width = font->pixel_size;
|
||
else
|
||
width = cache->width_int;
|
||
|
||
if (metrics)
|
||
{
|
||
if (METRICS_STATUS (cache) == METRICS_WIDTH_VALID)
|
||
{
|
||
CGRect bounds = mac_font_get_bounding_rect_for_glyph (macfont, glyph);
|
||
|
||
if (macfont_info->synthetic_italic_p)
|
||
{
|
||
/* We assume the members a, b, c, and d in
|
||
synthetic_italic_atfm are non-negative. */
|
||
bounds.origin =
|
||
CGPointApplyAffineTransform (bounds.origin,
|
||
synthetic_italic_atfm);
|
||
bounds.size =
|
||
CGSizeApplyAffineTransform (bounds.size, synthetic_italic_atfm);
|
||
}
|
||
if (macfont_info->synthetic_bold_p)
|
||
{
|
||
CGFloat d =
|
||
- synthetic_bold_factor * mac_font_get_size (macfont) / 2;
|
||
|
||
bounds = CGRectInset (bounds, d, d);
|
||
}
|
||
switch (macfont_info->spacing)
|
||
{
|
||
case MACFONT_SPACING_PROPORTIONAL:
|
||
bounds.origin.x += - (cache->width_frac
|
||
/ (CGFloat) (WIDTH_FRAC_SCALE * 2));
|
||
break;
|
||
case MACFONT_SPACING_MONO:
|
||
break;
|
||
case MACFONT_SPACING_SYNTHETIC_MONO:
|
||
bounds.origin.x += (cache->width_int
|
||
+ (cache->width_frac
|
||
/ (CGFloat) WIDTH_FRAC_SCALE));
|
||
break;
|
||
}
|
||
if (bounds.size.width > 0)
|
||
{
|
||
bounds.origin.x -= LCD_FONT_SMOOTHING_LEFT_MARGIN;
|
||
bounds.size.width += (LCD_FONT_SMOOTHING_LEFT_MARGIN
|
||
+ LCD_FONT_SMOOTHING_RIGHT_MARGIN);
|
||
}
|
||
bounds = CGRectIntegral (bounds);
|
||
METRICS_SET_VALUE (cache, lbearing, CGRectGetMinX (bounds));
|
||
METRICS_SET_VALUE (cache, rbearing, CGRectGetMaxX (bounds));
|
||
METRICS_SET_VALUE (cache, ascent, CGRectGetMaxY (bounds));
|
||
METRICS_SET_VALUE (cache, descent, -CGRectGetMinY (bounds));
|
||
}
|
||
metrics->lbearing = METRICS_VALUE (cache, lbearing);
|
||
metrics->rbearing = METRICS_VALUE (cache, rbearing);
|
||
metrics->width = width;
|
||
metrics->ascent = METRICS_VALUE (cache, ascent);
|
||
metrics->descent = METRICS_VALUE (cache, descent);
|
||
}
|
||
|
||
if (advance_delta)
|
||
{
|
||
switch (macfont_info->spacing)
|
||
{
|
||
case MACFONT_SPACING_PROPORTIONAL:
|
||
*advance_delta = (force_integral_p ? 0
|
||
: - (cache->width_frac
|
||
/ (CGFloat) (WIDTH_FRAC_SCALE * 2)));
|
||
break;
|
||
case MACFONT_SPACING_MONO:
|
||
*advance_delta = 0;
|
||
break;
|
||
case MACFONT_SPACING_SYNTHETIC_MONO:
|
||
*advance_delta = (force_integral_p ? cache->width_int
|
||
: (cache->width_int
|
||
+ (cache->width_frac
|
||
/ (CGFloat) WIDTH_FRAC_SCALE)));
|
||
break;
|
||
}
|
||
}
|
||
|
||
return width;
|
||
}
|
||
|
||
static CFMutableDictionaryRef macfont_cache_dictionary;
|
||
|
||
/* Threshold used in row_nkeys_or_perm. This must be less than or
|
||
equal to the number of rows that are invalid as BMP (i.e., from
|
||
U+D800 to U+DFFF). */
|
||
#define ROW_PERM_OFFSET (8)
|
||
|
||
/* The number of glyphs that can be stored in a value for a single
|
||
entry of CFDictionary. */
|
||
#define NGLYPHS_IN_VALUE (sizeof (void *) / sizeof (CGGlyph))
|
||
|
||
struct macfont_cache
|
||
{
|
||
int reference_count;
|
||
CFCharacterSetRef cf_charset;
|
||
struct {
|
||
/* The cached glyph for a BMP character c is stored in
|
||
matrix[row_nkeys_or_perm[c / 256] - ROW_PERM_OFFSET][c % 256]
|
||
if row_nkeys_or_perm[c / 256] >= ROW_PERM_OFFSET. */
|
||
unsigned char row_nkeys_or_perm[256];
|
||
CGGlyph **matrix;
|
||
|
||
/* Number of rows for which the BMP cache is allocated so far.
|
||
I.e., matrix[0] ... matrix[nrows - 1] are non-NULL. */
|
||
int nrows;
|
||
|
||
/* The cached glyph for a character c is stored as the (c %
|
||
NGLYPHS_IN_VALUE)-th CGGlyph block of a value for the key (c /
|
||
NGLYPHS_IN_VALUE). However, the glyph for a BMP character c is
|
||
not stored here if row_nkeys_or_perm[c / 256] >=
|
||
ROW_PERM_OFFSET. */
|
||
CFMutableDictionaryRef dictionary;
|
||
} glyph;
|
||
|
||
struct {
|
||
/* UVS (Unicode Variation Sequence) subtable data, which is of
|
||
type CFDataRef if available. NULL means it is not initialized
|
||
yet. kCFNull means the subtable is not found and there is no
|
||
suitable fallback table for this font. */
|
||
CFTypeRef table;
|
||
|
||
/* Character collection specifying the destination of the mapping
|
||
provided by `table' above. If `table' is obtained from the UVS
|
||
subtable in the font cmap table, then the value of this member
|
||
should be MAC_CHARACTER_COLLECTION_IDENTITY_MAPPING. */
|
||
CharacterCollection collection;
|
||
} uvs;
|
||
};
|
||
|
||
static struct macfont_cache *macfont_lookup_cache (CFStringRef);
|
||
static struct macfont_cache *macfont_retain_cache (struct macfont_cache *);
|
||
static void macfont_release_cache (struct macfont_cache *);
|
||
static CFCharacterSetRef macfont_get_cf_charset (struct font *);
|
||
static CFCharacterSetRef macfont_get_cf_charset_for_name (CFStringRef);
|
||
static CGGlyph macfont_get_glyph_for_character (struct font *, UTF32Char);
|
||
static CGGlyph macfont_get_glyph_for_cid (struct font *font,
|
||
CharacterCollection, CGFontIndex);
|
||
static CFDataRef macfont_get_uvs_table (struct font *, CharacterCollection *);
|
||
|
||
static struct macfont_cache *
|
||
macfont_lookup_cache (CFStringRef key)
|
||
{
|
||
struct macfont_cache *cache;
|
||
|
||
if (macfont_cache_dictionary == NULL)
|
||
{
|
||
macfont_cache_dictionary =
|
||
CFDictionaryCreateMutable (NULL, 0,
|
||
&kCFTypeDictionaryKeyCallBacks, NULL);
|
||
cache = NULL;
|
||
}
|
||
else
|
||
cache = ((struct macfont_cache *)
|
||
CFDictionaryGetValue (macfont_cache_dictionary, key));
|
||
|
||
if (cache == NULL)
|
||
{
|
||
FontRef macfont = mac_font_create_with_name (key, 0);
|
||
|
||
if (macfont)
|
||
{
|
||
cache = xzalloc (sizeof (struct macfont_cache));
|
||
/* Treat the LastResort font as if it contained glyphs for
|
||
all characters. This may look too rough, but neither
|
||
CTFontCopyCharacterSet nor -[NSFont coveredCharacterSet]
|
||
for this font is correct for non-BMP characters on Mac OS
|
||
X 10.5, anyway. */
|
||
if (CFStringCompare (key, CFSTR ("LastResort"), 0)
|
||
== kCFCompareEqualTo)
|
||
{
|
||
CFRange range = CFRangeMake (0, MAX_UNICODE_CHAR + 1);
|
||
|
||
cache->cf_charset =
|
||
CFCharacterSetCreateWithCharactersInRange (NULL, range);
|
||
}
|
||
if (cache->cf_charset == NULL)
|
||
cache->cf_charset = mac_font_copy_character_set (macfont);
|
||
CFDictionaryAddValue (macfont_cache_dictionary, key,
|
||
(const void *) cache);
|
||
CFRelease (macfont);
|
||
}
|
||
}
|
||
|
||
return cache;
|
||
}
|
||
|
||
static struct macfont_cache *
|
||
macfont_retain_cache (struct macfont_cache *cache)
|
||
{
|
||
cache->reference_count++;
|
||
|
||
return cache;
|
||
}
|
||
|
||
static void
|
||
macfont_release_cache (struct macfont_cache *cache)
|
||
{
|
||
if (--cache->reference_count == 0)
|
||
{
|
||
int i;
|
||
|
||
for (i = 0; i < cache->glyph.nrows; i++)
|
||
xfree (cache->glyph.matrix[i]);
|
||
xfree (cache->glyph.matrix);
|
||
if (cache->glyph.dictionary)
|
||
CFRelease (cache->glyph.dictionary);
|
||
memset (&cache->glyph, 0, sizeof (cache->glyph));
|
||
if (cache->uvs.table)
|
||
CFRelease (cache->uvs.table);
|
||
memset (&cache->uvs, 0, sizeof (cache->uvs));
|
||
}
|
||
}
|
||
|
||
static CFCharacterSetRef
|
||
macfont_get_cf_charset (struct font *font)
|
||
{
|
||
struct macfont_info *macfont_info = (struct macfont_info *) font;
|
||
|
||
return macfont_info->cache->cf_charset;
|
||
}
|
||
|
||
static CFCharacterSetRef
|
||
macfont_get_cf_charset_for_name (CFStringRef name)
|
||
{
|
||
struct macfont_cache *cache = macfont_lookup_cache (name);
|
||
|
||
return cache->cf_charset;
|
||
}
|
||
|
||
static CGGlyph
|
||
macfont_get_glyph_for_character (struct font *font, UTF32Char c)
|
||
{
|
||
struct macfont_info *macfont_info = (struct macfont_info *) font;
|
||
FontRef macfont = macfont_info->macfont;
|
||
struct macfont_cache *cache = macfont_info->cache;
|
||
|
||
if (c < 0xD800 || (c > 0xDFFF && c < 0x10000))
|
||
{
|
||
int row = c / 256;
|
||
int nkeys_or_perm = cache->glyph.row_nkeys_or_perm[row];
|
||
|
||
if (nkeys_or_perm < ROW_PERM_OFFSET)
|
||
{
|
||
UniChar unichars[256], ch;
|
||
CGGlyph *glyphs;
|
||
int i, len;
|
||
int nrows;
|
||
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
|
||
dispatch_queue_t queue;
|
||
dispatch_group_t group = NULL;
|
||
#else
|
||
int nkeys;
|
||
#endif
|
||
|
||
if (row != 0)
|
||
{
|
||
CFMutableDictionaryRef dictionary;
|
||
uintptr_t key, value;
|
||
int nshifts;
|
||
CGGlyph glyph;
|
||
|
||
if (cache->glyph.dictionary == NULL)
|
||
cache->glyph.dictionary =
|
||
CFDictionaryCreateMutable (NULL, 0, NULL, NULL);
|
||
dictionary = cache->glyph.dictionary;
|
||
key = c / NGLYPHS_IN_VALUE;
|
||
nshifts = ((c % NGLYPHS_IN_VALUE) * sizeof (CGGlyph) * 8);
|
||
value = ((uintptr_t)
|
||
CFDictionaryGetValue (dictionary, (const void *) key));
|
||
glyph = (value >> nshifts);
|
||
if (glyph)
|
||
return glyph;
|
||
|
||
if (nkeys_or_perm + 1 != ROW_PERM_OFFSET)
|
||
{
|
||
ch = c;
|
||
if (!mac_font_get_glyphs_for_characters (macfont, &ch,
|
||
&glyph, 1)
|
||
|| glyph == 0)
|
||
glyph = kCGFontIndexInvalid;
|
||
|
||
if (value == 0)
|
||
cache->glyph.row_nkeys_or_perm[row] = nkeys_or_perm + 1;
|
||
value |= ((uintptr_t) glyph << nshifts);
|
||
CFDictionarySetValue (dictionary, (const void *) key,
|
||
(const void *) value);
|
||
|
||
return glyph;
|
||
}
|
||
|
||
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
|
||
queue =
|
||
dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
|
||
group = dispatch_group_create ();
|
||
dispatch_group_async (group, queue, ^{
|
||
int nkeys;
|
||
uintptr_t key;
|
||
#endif
|
||
nkeys = nkeys_or_perm;
|
||
for (key = row * (256 / NGLYPHS_IN_VALUE); ; key++)
|
||
if (CFDictionaryContainsKey (dictionary,
|
||
(const void *) key))
|
||
{
|
||
CFDictionaryRemoveValue (dictionary,
|
||
(const void *) key);
|
||
if (--nkeys == 0)
|
||
break;
|
||
}
|
||
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
|
||
});
|
||
#endif
|
||
}
|
||
|
||
len = 0;
|
||
for (i = 0; i < 256; i++)
|
||
{
|
||
ch = row * 256 + i;
|
||
if (CFCharacterSetIsLongCharacterMember (cache->cf_charset, ch))
|
||
unichars[len++] = ch;
|
||
}
|
||
|
||
glyphs = xmalloc (sizeof (CGGlyph) * 256);
|
||
if (len > 0)
|
||
{
|
||
mac_font_get_glyphs_for_characters (macfont, unichars,
|
||
glyphs, len);
|
||
while (i > len)
|
||
{
|
||
int next = unichars[len - 1] % 256;
|
||
|
||
while (--i > next)
|
||
glyphs[i] = kCGFontIndexInvalid;
|
||
|
||
len--;
|
||
glyphs[i] = glyphs[len];
|
||
if (len == 0)
|
||
break;
|
||
}
|
||
}
|
||
if (i > len)
|
||
while (i-- > 0)
|
||
glyphs[i] = kCGFontIndexInvalid;
|
||
|
||
nrows = cache->glyph.nrows;
|
||
nkeys_or_perm = nrows + ROW_PERM_OFFSET;
|
||
cache->glyph.row_nkeys_or_perm[row] = nkeys_or_perm;
|
||
nrows++;
|
||
cache->glyph.matrix = xrealloc (cache->glyph.matrix,
|
||
sizeof (CGGlyph *) * nrows);
|
||
cache->glyph.matrix[nrows - 1] = glyphs;
|
||
cache->glyph.nrows = nrows;
|
||
|
||
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
|
||
if (group)
|
||
{
|
||
dispatch_group_wait (group, DISPATCH_TIME_FOREVER);
|
||
dispatch_release (group);
|
||
}
|
||
#endif
|
||
}
|
||
|
||
return cache->glyph.matrix[nkeys_or_perm - ROW_PERM_OFFSET][c % 256];
|
||
}
|
||
else
|
||
{
|
||
uintptr_t key, value;
|
||
int nshifts;
|
||
CGGlyph glyph;
|
||
|
||
if (cache->glyph.dictionary == NULL)
|
||
cache->glyph.dictionary =
|
||
CFDictionaryCreateMutable (NULL, 0, NULL, NULL);
|
||
key = c / NGLYPHS_IN_VALUE;
|
||
nshifts = ((c % NGLYPHS_IN_VALUE) * sizeof (CGGlyph) * 8);
|
||
value = (uintptr_t) CFDictionaryGetValue (cache->glyph.dictionary,
|
||
(const void *) key);
|
||
glyph = (value >> nshifts);
|
||
if (glyph == 0)
|
||
{
|
||
UniChar unichars[2];
|
||
CGGlyph glyphs[2];
|
||
CFIndex count = macfont_store_utf32char_to_unichars (c, unichars);
|
||
|
||
if (mac_font_get_glyphs_for_characters (macfont, unichars, glyphs,
|
||
count))
|
||
glyph = glyphs[0];
|
||
if (glyph == 0)
|
||
glyph = kCGFontIndexInvalid;
|
||
|
||
value |= ((uintptr_t) glyph << nshifts);
|
||
CFDictionarySetValue (cache->glyph.dictionary,
|
||
(const void *) key, (const void *) value);
|
||
}
|
||
|
||
return glyph;
|
||
}
|
||
}
|
||
|
||
static CGGlyph
|
||
macfont_get_glyph_for_cid (struct font *font, CharacterCollection collection,
|
||
CGFontIndex cid)
|
||
{
|
||
struct macfont_info *macfont_info = (struct macfont_info *) font;
|
||
FontRef macfont = macfont_info->macfont;
|
||
|
||
/* Cache it? */
|
||
return mac_font_get_glyph_for_cid (macfont, collection, cid);
|
||
}
|
||
|
||
static CFDataRef
|
||
macfont_get_uvs_table (struct font *font, CharacterCollection *collection)
|
||
{
|
||
struct macfont_info *macfont_info = (struct macfont_info *) font;
|
||
FontRef macfont = macfont_info->macfont;
|
||
struct macfont_cache *cache = macfont_info->cache;
|
||
CFDataRef result = NULL;
|
||
|
||
if (cache->uvs.table == NULL)
|
||
{
|
||
CFDataRef uvs_table = mac_font_copy_uvs_table (macfont);
|
||
CharacterCollection uvs_collection =
|
||
MAC_CHARACTER_COLLECTION_IDENTITY_MAPPING;
|
||
|
||
if (uvs_table == NULL
|
||
&& mac_font_get_glyph_for_cid (macfont,
|
||
MAC_CHARACTER_COLLECTION_ADOBE_JAPAN1,
|
||
6480) != kCGFontIndexInvalid)
|
||
{
|
||
/* If the glyph for U+4E55 is accessible via its CID 6480,
|
||
then we use the Adobe-Japan1 UVS table, which maps a
|
||
variation sequence to a CID, as a fallback. */
|
||
static CFDataRef mac_uvs_table_adobe_japan1 = NULL;
|
||
|
||
if (mac_uvs_table_adobe_japan1 == NULL)
|
||
mac_uvs_table_adobe_japan1 =
|
||
CFDataCreateWithBytesNoCopy (NULL,
|
||
mac_uvs_table_adobe_japan1_bytes,
|
||
sizeof (mac_uvs_table_adobe_japan1_bytes),
|
||
kCFAllocatorNull);
|
||
if (mac_uvs_table_adobe_japan1)
|
||
{
|
||
uvs_table = CFRetain (mac_uvs_table_adobe_japan1);
|
||
uvs_collection = MAC_CHARACTER_COLLECTION_ADOBE_JAPAN1;
|
||
}
|
||
}
|
||
if (uvs_table == NULL)
|
||
cache->uvs.table = kCFNull;
|
||
else
|
||
cache->uvs.table = uvs_table;
|
||
cache->uvs.collection = uvs_collection;
|
||
}
|
||
|
||
if (cache->uvs.table != kCFNull)
|
||
{
|
||
result = cache->uvs.table;
|
||
*collection = cache->uvs.collection;
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
static Lisp_Object macfont_get_cache (struct frame *);
|
||
static Lisp_Object macfont_list (struct frame *, Lisp_Object);
|
||
static Lisp_Object macfont_match (struct frame *, Lisp_Object);
|
||
static Lisp_Object macfont_list_family (struct frame *);
|
||
static void macfont_free_entity (Lisp_Object);
|
||
static Lisp_Object macfont_open (struct frame *, Lisp_Object, int);
|
||
static void macfont_close (struct font *);
|
||
static int macfont_has_char (Lisp_Object, int);
|
||
static unsigned macfont_encode_char (struct font *, int);
|
||
static int macfont_text_extents (struct font *, unsigned int *, int,
|
||
struct font_metrics *);
|
||
static int macfont_draw (struct glyph_string *, int, int, int, int, bool);
|
||
static Lisp_Object macfont_shape (Lisp_Object);
|
||
static int macfont_variation_glyphs (struct font *, int c,
|
||
unsigned variations[256]);
|
||
static void macfont_filter_properties (Lisp_Object, Lisp_Object);
|
||
|
||
static struct font_driver macfont_driver =
|
||
{
|
||
LISP_INITIALLY_ZERO, /* Qmac_ct */
|
||
0, /* case insensitive */
|
||
macfont_get_cache,
|
||
macfont_list,
|
||
macfont_match,
|
||
macfont_list_family,
|
||
macfont_free_entity,
|
||
macfont_open,
|
||
macfont_close,
|
||
NULL, /* prepare_face */
|
||
NULL, /* done_face */
|
||
macfont_has_char,
|
||
macfont_encode_char,
|
||
macfont_text_extents,
|
||
macfont_draw,
|
||
NULL, /* get_bitmap */
|
||
NULL, /* free_bitmap */
|
||
NULL, /* get_outline */
|
||
NULL, /* free_outline */
|
||
NULL, /* anchor_point */
|
||
NULL, /* otf_capability */
|
||
NULL, /* otf_drive */
|
||
NULL, /* start_for_frame */
|
||
NULL, /* end_for_frame */
|
||
macfont_shape,
|
||
NULL, /* check */
|
||
macfont_variation_glyphs,
|
||
macfont_filter_properties,
|
||
};
|
||
|
||
static Lisp_Object
|
||
macfont_get_cache (struct frame * f)
|
||
{
|
||
Display_Info *dpyinfo = FRAME_DISPLAY_INFO (f);
|
||
|
||
return (dpyinfo->name_list_element);
|
||
}
|
||
|
||
static int
|
||
macfont_get_charset (Lisp_Object registry)
|
||
{
|
||
char *str = SSDATA (SYMBOL_NAME (registry));
|
||
char *re = alloca (SBYTES (SYMBOL_NAME (registry)) * 2 + 1);
|
||
Lisp_Object regexp;
|
||
int i, j;
|
||
|
||
for (i = j = 0; i < SBYTES (SYMBOL_NAME (registry)); i++, j++)
|
||
{
|
||
if (str[i] == '.')
|
||
re[j++] = '\\';
|
||
else if (str[i] == '*')
|
||
re[j++] = '.';
|
||
re[j] = str[i];
|
||
if (re[j] == '?')
|
||
re[j] = '.';
|
||
}
|
||
re[j] = '\0';
|
||
regexp = make_unibyte_string (re, j);
|
||
for (i = 0; cf_charset_table[i].name; i++)
|
||
if (fast_c_string_match_ignore_case
|
||
(regexp, cf_charset_table[i].name,
|
||
strlen (cf_charset_table[i].name)) >= 0)
|
||
break;
|
||
if (! cf_charset_table[i].name)
|
||
return -1;
|
||
if (! cf_charset_table[i].cf_charset)
|
||
{
|
||
int *uniquifier = cf_charset_table[i].uniquifier;
|
||
UniChar *unichars = alloca (sizeof (cf_charset_table[i].uniquifier));
|
||
CFIndex count = 0;
|
||
CFStringRef string;
|
||
CFMutableCharacterSetRef charset = CFCharacterSetCreateMutable (NULL);
|
||
|
||
if (! charset)
|
||
return -1;
|
||
for (j = 0; uniquifier[j]; j++)
|
||
{
|
||
count += macfont_store_utf32char_to_unichars (uniquifier[j],
|
||
unichars + count);
|
||
CFCharacterSetAddCharactersInRange (charset,
|
||
CFRangeMake (uniquifier[j], 1));
|
||
}
|
||
|
||
string = CFStringCreateWithCharacters (NULL, unichars, count);
|
||
if (! string)
|
||
{
|
||
CFRelease (charset);
|
||
return -1;
|
||
}
|
||
cf_charset_table[i].cf_charset = CFCharacterSetCreateCopy (NULL,
|
||
charset);
|
||
CFRelease (charset);
|
||
/* CFCharacterSetCreateWithCharactersInString does not handle
|
||
surrogate pairs properly as of Mac OS X 10.5. */
|
||
cf_charset_table[i].cf_charset_string = string;
|
||
}
|
||
return i;
|
||
}
|
||
|
||
struct OpenTypeSpec
|
||
{
|
||
Lisp_Object script;
|
||
unsigned int script_tag, langsys_tag;
|
||
int nfeatures[2];
|
||
unsigned int *features[2];
|
||
};
|
||
|
||
#define OTF_SYM_TAG(SYM, TAG) \
|
||
do { \
|
||
unsigned char *p = SDATA (SYMBOL_NAME (SYM)); \
|
||
TAG = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; \
|
||
} while (0)
|
||
|
||
#define OTF_TAG_STR(TAG, P) \
|
||
do { \
|
||
(P)[0] = (char) (TAG >> 24); \
|
||
(P)[1] = (char) ((TAG >> 16) & 0xFF); \
|
||
(P)[2] = (char) ((TAG >> 8) & 0xFF); \
|
||
(P)[3] = (char) (TAG & 0xFF); \
|
||
(P)[4] = '\0'; \
|
||
} while (0)
|
||
|
||
static struct OpenTypeSpec *
|
||
macfont_get_open_type_spec (Lisp_Object otf_spec)
|
||
{
|
||
struct OpenTypeSpec *spec = xmalloc (sizeof *spec);
|
||
Lisp_Object val;
|
||
int i, j;
|
||
bool negative;
|
||
|
||
if (! spec)
|
||
return NULL;
|
||
spec->script = XCAR (otf_spec);
|
||
if (! NILP (spec->script))
|
||
{
|
||
OTF_SYM_TAG (spec->script, spec->script_tag);
|
||
val = assq_no_quit (spec->script, Votf_script_alist);
|
||
if (CONSP (val) && SYMBOLP (XCDR (val)))
|
||
spec->script = XCDR (val);
|
||
else
|
||
spec->script = Qnil;
|
||
}
|
||
else
|
||
spec->script_tag = 0x44464C54; /* "DFLT" */
|
||
otf_spec = XCDR (otf_spec);
|
||
spec->langsys_tag = 0;
|
||
if (! NILP (otf_spec))
|
||
{
|
||
val = XCAR (otf_spec);
|
||
if (! NILP (val))
|
||
OTF_SYM_TAG (val, spec->langsys_tag);
|
||
otf_spec = XCDR (otf_spec);
|
||
}
|
||
spec->nfeatures[0] = spec->nfeatures[1] = 0;
|
||
for (i = 0; i < 2 && ! NILP (otf_spec); i++, otf_spec = XCDR (otf_spec))
|
||
{
|
||
Lisp_Object len;
|
||
|
||
val = XCAR (otf_spec);
|
||
if (NILP (val))
|
||
continue;
|
||
len = Flength (val);
|
||
spec->features[i] =
|
||
(min (PTRDIFF_MAX, SIZE_MAX) / sizeof (int) < XINT (len)
|
||
? 0
|
||
: malloc (XINT (len) * sizeof *spec->features[i]));
|
||
if (! spec->features[i])
|
||
{
|
||
if (i > 0 && spec->features[0])
|
||
free (spec->features[0]);
|
||
free (spec);
|
||
return NULL;
|
||
}
|
||
for (j = 0, negative = 0; CONSP (val); val = XCDR (val))
|
||
{
|
||
if (NILP (XCAR (val)))
|
||
negative = 1;
|
||
else
|
||
{
|
||
unsigned int tag;
|
||
|
||
OTF_SYM_TAG (XCAR (val), tag);
|
||
spec->features[i][j++] = negative ? tag & 0x80000000 : tag;
|
||
}
|
||
}
|
||
spec->nfeatures[i] = j;
|
||
}
|
||
return spec;
|
||
}
|
||
|
||
static CFMutableDictionaryRef
|
||
macfont_create_attributes_with_spec (Lisp_Object spec)
|
||
{
|
||
Lisp_Object tmp, extra;
|
||
CFMutableArrayRef langarray = NULL;
|
||
CFCharacterSetRef charset = NULL;
|
||
CFStringRef charset_string = NULL;
|
||
CFMutableDictionaryRef attributes = NULL, traits = NULL;
|
||
Lisp_Object script = Qnil;
|
||
Lisp_Object registry;
|
||
int cf_charset_idx, i;
|
||
struct OpenTypeSpec *otspec = NULL;
|
||
struct {
|
||
enum font_property_index index;
|
||
CFStringRef trait;
|
||
CGPoint points[6];
|
||
} numeric_traits[] =
|
||
{{FONT_WEIGHT_INDEX, MAC_FONT_WEIGHT_TRAIT,
|
||
{{-0.4, 50}, /* light */
|
||
{-0.24, 87.5}, /* (semi-light + normal) / 2 */
|
||
{0, 100}, /* normal */
|
||
{0.24, 140}, /* (semi-bold + normal) / 2 */
|
||
{0.4, 200}, /* bold */
|
||
{CGFLOAT_MAX, CGFLOAT_MAX}}},
|
||
{FONT_SLANT_INDEX, MAC_FONT_SLANT_TRAIT,
|
||
{{0, 100}, {0.1, 200}, {CGFLOAT_MAX, CGFLOAT_MAX}}},
|
||
{FONT_WIDTH_INDEX, MAC_FONT_WIDTH_TRAIT,
|
||
{{0, 100}, {1, 200}, {CGFLOAT_MAX, CGFLOAT_MAX}}}};
|
||
|
||
registry = AREF (spec, FONT_REGISTRY_INDEX);
|
||
if (NILP (registry)
|
||
|| EQ (registry, Qascii_0)
|
||
|| EQ (registry, Qiso10646_1)
|
||
|| EQ (registry, Qunicode_bmp))
|
||
cf_charset_idx = -1;
|
||
else
|
||
{
|
||
CFStringRef lang;
|
||
|
||
cf_charset_idx = macfont_get_charset (registry);
|
||
if (cf_charset_idx < 0)
|
||
goto err;
|
||
charset = cf_charset_table[cf_charset_idx].cf_charset;
|
||
charset_string = cf_charset_table[cf_charset_idx].cf_charset_string;
|
||
lang = cf_charset_table[cf_charset_idx].lang;
|
||
if (lang)
|
||
{
|
||
langarray = CFArrayCreateMutable (NULL, 0, &kCFTypeArrayCallBacks);
|
||
if (! langarray)
|
||
goto err;
|
||
CFArrayAppendValue (langarray, lang);
|
||
}
|
||
}
|
||
|
||
for (extra = AREF (spec, FONT_EXTRA_INDEX);
|
||
CONSP (extra); extra = XCDR (extra))
|
||
{
|
||
Lisp_Object key, val;
|
||
|
||
tmp = XCAR (extra);
|
||
key = XCAR (tmp), val = XCDR (tmp);
|
||
if (EQ (key, QClang))
|
||
{
|
||
if (! langarray)
|
||
langarray = CFArrayCreateMutable (NULL, 0, &kCFTypeArrayCallBacks);
|
||
if (! langarray)
|
||
goto err;
|
||
if (SYMBOLP (val))
|
||
val = list1 (val);
|
||
for (; CONSP (val); val = XCDR (val))
|
||
if (SYMBOLP (XCAR (val)))
|
||
{
|
||
CFStringRef lang =
|
||
cfstring_create_with_string_noencode (SYMBOL_NAME
|
||
(XCAR (val)));
|
||
|
||
if (lang == NULL)
|
||
goto err;
|
||
CFArrayAppendValue (langarray, lang);
|
||
CFRelease (lang);
|
||
}
|
||
}
|
||
else if (EQ (key, QCotf))
|
||
{
|
||
otspec = macfont_get_open_type_spec (val);
|
||
if (! otspec)
|
||
goto err;
|
||
script = otspec->script;
|
||
}
|
||
else if (EQ (key, QCscript))
|
||
script = val;
|
||
}
|
||
|
||
if (! NILP (script) && ! charset)
|
||
{
|
||
Lisp_Object chars = assq_no_quit (script, Vscript_representative_chars);
|
||
|
||
if (CONSP (chars) && CONSP (CDR (chars)))
|
||
{
|
||
CFMutableStringRef string = CFStringCreateMutable (NULL, 0);
|
||
CFMutableCharacterSetRef cs = CFCharacterSetCreateMutable (NULL);
|
||
|
||
if (! string || !cs)
|
||
{
|
||
if (string)
|
||
CFRelease (string);
|
||
else if (cs)
|
||
CFRelease (cs);
|
||
goto err;
|
||
}
|
||
for (chars = XCDR (chars); CONSP (chars); chars = XCDR (chars))
|
||
if (CHARACTERP (XCAR (chars)))
|
||
{
|
||
UniChar unichars[2];
|
||
CFIndex count =
|
||
macfont_store_utf32char_to_unichars (XFASTINT (XCAR (chars)),
|
||
unichars);
|
||
CFRange range = CFRangeMake (XFASTINT (XCAR (chars)), 1);
|
||
|
||
CFStringAppendCharacters (string, unichars, count);
|
||
CFCharacterSetAddCharactersInRange (cs, range);
|
||
}
|
||
charset = cs;
|
||
/* CFCharacterSetCreateWithCharactersInString does not
|
||
handle surrogate pairs properly as of Mac OS X 10.5. */
|
||
charset_string = string;
|
||
}
|
||
}
|
||
|
||
attributes = CFDictionaryCreateMutable (NULL, 0,
|
||
&kCFTypeDictionaryKeyCallBacks,
|
||
&kCFTypeDictionaryValueCallBacks);
|
||
if (! attributes)
|
||
goto err;
|
||
|
||
tmp = AREF (spec, FONT_FAMILY_INDEX);
|
||
if (SYMBOLP (tmp) && ! NILP (tmp))
|
||
{
|
||
CFStringRef family = macfont_create_family_with_symbol (tmp);
|
||
|
||
if (! family)
|
||
goto err;
|
||
CFDictionaryAddValue (attributes, MAC_FONT_FAMILY_NAME_ATTRIBUTE,
|
||
family);
|
||
CFRelease (family);
|
||
}
|
||
|
||
traits = CFDictionaryCreateMutable (NULL, 4,
|
||
&kCFTypeDictionaryKeyCallBacks,
|
||
&kCFTypeDictionaryValueCallBacks);
|
||
if (! traits)
|
||
goto err;
|
||
|
||
for (i = 0; i < sizeof (numeric_traits) / sizeof (numeric_traits[0]); i++)
|
||
{
|
||
tmp = AREF (spec, numeric_traits[i].index);
|
||
if (INTEGERP (tmp))
|
||
{
|
||
CGPoint *point = numeric_traits[i].points;
|
||
CGFloat floatval = (XINT (tmp) >> 8); // XXX
|
||
CFNumberRef num;
|
||
|
||
while (point->y < floatval)
|
||
point++;
|
||
if (point == numeric_traits[i].points)
|
||
point++;
|
||
else if (point->y == CGFLOAT_MAX)
|
||
point--;
|
||
floatval = (point - 1)->x + ((floatval - (point - 1)->y)
|
||
* ((point->x - (point - 1)->x)
|
||
/ (point->y - (point - 1)->y)));
|
||
if (floatval > 1.0)
|
||
floatval = 1.0;
|
||
else if (floatval < -1.0)
|
||
floatval = -1.0;
|
||
num = CFNumberCreate (NULL, kCFNumberCGFloatType, &floatval);
|
||
if (! num)
|
||
goto err;
|
||
CFDictionaryAddValue (traits, numeric_traits[i].trait, num);
|
||
CFRelease (num);
|
||
}
|
||
}
|
||
if (CFDictionaryGetCount (traits))
|
||
CFDictionaryAddValue (attributes, MAC_FONT_TRAITS_ATTRIBUTE, traits);
|
||
|
||
if (charset)
|
||
CFDictionaryAddValue (attributes, MAC_FONT_CHARACTER_SET_ATTRIBUTE,
|
||
charset);
|
||
if (charset_string)
|
||
CFDictionaryAddValue (attributes, MAC_FONT_CHARACTER_SET_STRING_ATTRIBUTE,
|
||
charset_string);
|
||
if (langarray)
|
||
CFDictionaryAddValue (attributes, MAC_FONT_LANGUAGES_ATTRIBUTE, langarray);
|
||
|
||
goto finish;
|
||
|
||
err:
|
||
if (attributes)
|
||
{
|
||
CFRelease (attributes);
|
||
attributes = NULL;
|
||
}
|
||
|
||
finish:
|
||
if (langarray) CFRelease (langarray);
|
||
if (charset && cf_charset_idx < 0) CFRelease (charset);
|
||
if (charset_string && cf_charset_idx < 0) CFRelease (charset_string);
|
||
if (traits) CFRelease (traits);
|
||
if (otspec)
|
||
{
|
||
if (otspec->nfeatures[0] > 0)
|
||
free (otspec->features[0]);
|
||
if (otspec->nfeatures[1] > 0)
|
||
free (otspec->features[1]);
|
||
free (otspec);
|
||
}
|
||
|
||
return attributes;
|
||
}
|
||
|
||
static Boolean
|
||
macfont_supports_charset_and_languages_p (FontDescriptorRef desc,
|
||
CFCharacterSetRef charset,
|
||
Lisp_Object chars,
|
||
CFArrayRef languages)
|
||
{
|
||
Boolean result = true;
|
||
|
||
if (charset || VECTORP (chars))
|
||
{
|
||
CFCharacterSetRef desc_charset =
|
||
mac_font_descriptor_copy_attribute (desc,
|
||
MAC_FONT_CHARACTER_SET_ATTRIBUTE);
|
||
|
||
if (desc_charset == NULL)
|
||
result = false;
|
||
else
|
||
{
|
||
if (charset)
|
||
result = CFCharacterSetIsSupersetOfSet (desc_charset, charset);
|
||
else /* VECTORP (chars) */
|
||
{
|
||
ptrdiff_t j;
|
||
|
||
for (j = 0; j < ASIZE (chars); j++)
|
||
if (TYPE_RANGED_INTEGERP (UTF32Char, AREF (chars, j))
|
||
&& CFCharacterSetIsLongCharacterMember (desc_charset,
|
||
XFASTINT (AREF (chars, j))))
|
||
break;
|
||
if (j == ASIZE (chars))
|
||
result = false;
|
||
}
|
||
CFRelease (desc_charset);
|
||
}
|
||
}
|
||
if (result && languages)
|
||
result = mac_font_descriptor_supports_languages (desc, languages);
|
||
|
||
return result;
|
||
}
|
||
|
||
static CFIndex
|
||
macfont_closest_traits_index (CFArrayRef traits_array,
|
||
FontSymbolicTraits target)
|
||
{
|
||
CFIndex i, result = -1, count = CFArrayGetCount (traits_array);
|
||
int min_distance = (1 << 3);
|
||
|
||
for (i = 0; i < count; i++)
|
||
{
|
||
FontSymbolicTraits traits, diff;
|
||
int distance = 0;
|
||
|
||
traits = ((FontSymbolicTraits) (uintptr_t)
|
||
CFArrayGetValueAtIndex (traits_array, i));
|
||
diff = (target ^ traits);
|
||
/* We prefer synthetic bold of italic to synthetic italic of
|
||
bold when both bold and italic are available but bold-italic
|
||
is not available. */
|
||
if (diff & MAC_FONT_TRAIT_BOLD)
|
||
distance |= (1 << 0);
|
||
if (diff & MAC_FONT_TRAIT_ITALIC)
|
||
distance |= (1 << 1);
|
||
if (diff & MAC_FONT_TRAIT_MONO_SPACE)
|
||
distance |= (1 << 2);
|
||
if (distance < min_distance)
|
||
{
|
||
min_distance = distance;
|
||
result = i;
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
static Lisp_Object
|
||
macfont_list (struct frame *f, Lisp_Object spec)
|
||
{
|
||
Lisp_Object val = Qnil, family, extra;
|
||
int i, n;
|
||
CFStringRef family_name = NULL;
|
||
CFMutableDictionaryRef attributes = NULL, traits;
|
||
Lisp_Object chars = Qnil;
|
||
int spacing = -1;
|
||
FontSymbolicTraits synth_sym_traits = 0;
|
||
CFArrayRef families;
|
||
CFIndex families_count;
|
||
CFCharacterSetRef charset = NULL;
|
||
CFArrayRef languages = NULL;
|
||
|
||
block_input ();
|
||
|
||
family = AREF (spec, FONT_FAMILY_INDEX);
|
||
if (! NILP (family))
|
||
{
|
||
family_name = macfont_create_family_with_symbol (family);
|
||
if (family_name == NULL)
|
||
goto finish;
|
||
}
|
||
|
||
attributes = macfont_create_attributes_with_spec (spec);
|
||
if (! attributes)
|
||
goto finish;
|
||
|
||
languages = CFDictionaryGetValue (attributes, MAC_FONT_LANGUAGES_ATTRIBUTE);
|
||
|
||
if (INTEGERP (AREF (spec, FONT_SPACING_INDEX)))
|
||
spacing = XINT (AREF (spec, FONT_SPACING_INDEX));
|
||
|
||
traits = ((CFMutableDictionaryRef)
|
||
CFDictionaryGetValue (attributes, MAC_FONT_TRAITS_ATTRIBUTE));
|
||
|
||
n = FONT_SLANT_NUMERIC (spec);
|
||
if (n < 0 || n == FONT_SLANT_SYNTHETIC_ITALIC)
|
||
{
|
||
synth_sym_traits |= MAC_FONT_TRAIT_ITALIC;
|
||
if (traits)
|
||
CFDictionaryRemoveValue (traits, MAC_FONT_SLANT_TRAIT);
|
||
}
|
||
|
||
n = FONT_WEIGHT_NUMERIC (spec);
|
||
if (n < 0 || n == FONT_WEIGHT_SYNTHETIC_BOLD)
|
||
{
|
||
synth_sym_traits |= MAC_FONT_TRAIT_BOLD;
|
||
if (traits)
|
||
CFDictionaryRemoveValue (traits, MAC_FONT_WEIGHT_TRAIT);
|
||
}
|
||
|
||
if (languages
|
||
&& (spacing < 0 || spacing == FONT_SPACING_SYNTHETIC_MONO))
|
||
{
|
||
CFStringRef language = CFArrayGetValueAtIndex (languages, 0);
|
||
|
||
if (CFStringHasPrefix (language, CFSTR ("ja"))
|
||
|| CFStringHasPrefix (language, CFSTR ("ko"))
|
||
|| CFStringHasPrefix (language, CFSTR ("zh")))
|
||
synth_sym_traits |= MAC_FONT_TRAIT_MONO_SPACE;
|
||
}
|
||
|
||
/* Create array of families. */
|
||
if (family_name)
|
||
families = CFArrayCreate (NULL, (const void **) &family_name,
|
||
1, &kCFTypeArrayCallBacks);
|
||
else
|
||
{
|
||
CFStringRef pref_family;
|
||
CFIndex families_count, pref_family_index = -1;
|
||
|
||
families = mac_font_create_available_families ();
|
||
if (families == NULL)
|
||
goto err;
|
||
|
||
families_count = CFArrayGetCount (families);
|
||
|
||
/* Move preferred family to the front if exists. */
|
||
pref_family =
|
||
mac_font_create_preferred_family_for_attributes (attributes);
|
||
if (pref_family)
|
||
{
|
||
pref_family_index =
|
||
CFArrayGetFirstIndexOfValue (families,
|
||
CFRangeMake (0, families_count),
|
||
pref_family);
|
||
CFRelease (pref_family);
|
||
}
|
||
if (pref_family_index > 0)
|
||
{
|
||
CFMutableArrayRef mutable_families =
|
||
CFArrayCreateMutable (NULL, families_count, &kCFTypeArrayCallBacks);
|
||
|
||
if (mutable_families)
|
||
{
|
||
CFArrayAppendValue (mutable_families,
|
||
CFArrayGetValueAtIndex (families,
|
||
pref_family_index));
|
||
CFArrayAppendArray (mutable_families, families,
|
||
CFRangeMake (0, pref_family_index));
|
||
if (pref_family_index + 1 < families_count)
|
||
CFArrayAppendArray (mutable_families, families,
|
||
CFRangeMake (pref_family_index + 1,
|
||
families_count
|
||
- (pref_family_index + 1)));
|
||
CFRelease (families);
|
||
families = mutable_families;
|
||
}
|
||
}
|
||
}
|
||
|
||
charset = CFDictionaryGetValue (attributes,
|
||
MAC_FONT_CHARACTER_SET_ATTRIBUTE);
|
||
if (charset)
|
||
{
|
||
CFRetain (charset);
|
||
CFDictionaryRemoveValue (attributes, MAC_FONT_CHARACTER_SET_ATTRIBUTE);
|
||
}
|
||
else
|
||
{
|
||
val = assq_no_quit (QCscript, AREF (spec, FONT_EXTRA_INDEX));
|
||
if (! NILP (val))
|
||
{
|
||
val = assq_no_quit (XCDR (val), Vscript_representative_chars);
|
||
if (CONSP (val) && VECTORP (XCDR (val)))
|
||
chars = XCDR (val);
|
||
}
|
||
val = Qnil;
|
||
}
|
||
|
||
if (languages)
|
||
{
|
||
CFRetain (languages);
|
||
CFDictionaryRemoveValue (attributes, MAC_FONT_LANGUAGES_ATTRIBUTE);
|
||
}
|
||
|
||
val = Qnil;
|
||
extra = AREF (spec, FONT_EXTRA_INDEX);
|
||
families_count = CFArrayGetCount (families);
|
||
for (i = 0; i < families_count; i++)
|
||
{
|
||
CFStringRef family_name = CFArrayGetValueAtIndex (families, i);
|
||
FontDescriptorRef pat_desc;
|
||
CFArrayRef descs;
|
||
CFIndex descs_count;
|
||
CFMutableArrayRef filtered_descs, traits_array;
|
||
Lisp_Object entity;
|
||
int j;
|
||
|
||
CFDictionarySetValue (attributes, MAC_FONT_FAMILY_NAME_ATTRIBUTE,
|
||
family_name);
|
||
pat_desc = mac_font_descriptor_create_with_attributes (attributes);
|
||
if (! pat_desc)
|
||
goto err;
|
||
|
||
/* CTFontDescriptorCreateMatchingFontDescriptors on Mac OS X
|
||
10.7 returns NULL if pat_desc represents the LastResort font.
|
||
So we use CTFontDescriptorCreateMatchingFontDescriptor (no
|
||
trailing "s") for such a font. */
|
||
if (CFStringCompare (family_name, CFSTR ("LastResort"), 0)
|
||
!= kCFCompareEqualTo)
|
||
descs = mac_font_descriptor_create_matching_font_descriptors (pat_desc,
|
||
NULL);
|
||
else
|
||
{
|
||
FontDescriptorRef lr_desc =
|
||
mac_font_descriptor_create_matching_font_descriptor (pat_desc,
|
||
NULL);
|
||
if (lr_desc)
|
||
{
|
||
descs = CFArrayCreate (NULL, (const void **) &lr_desc, 1,
|
||
&kCFTypeArrayCallBacks);
|
||
CFRelease (lr_desc);
|
||
}
|
||
else
|
||
descs = NULL;
|
||
}
|
||
CFRelease (pat_desc);
|
||
if (! descs)
|
||
goto err;
|
||
|
||
descs_count = CFArrayGetCount (descs);
|
||
if (descs_count == 0
|
||
|| !macfont_supports_charset_and_languages_p (CFArrayGetValueAtIndex (descs, 0),
|
||
charset, chars,
|
||
languages))
|
||
{
|
||
CFRelease (descs);
|
||
continue;
|
||
}
|
||
|
||
filtered_descs =
|
||
CFArrayCreateMutable (NULL, descs_count, &kCFTypeArrayCallBacks);
|
||
traits_array = CFArrayCreateMutable (NULL, descs_count, NULL);
|
||
for (j = 0; j < descs_count; j++)
|
||
{
|
||
FontDescriptorRef desc = CFArrayGetValueAtIndex (descs, j);
|
||
CFDictionaryRef dict;
|
||
CFNumberRef num;
|
||
FontSymbolicTraits sym_traits;
|
||
|
||
dict = mac_font_descriptor_copy_attribute (desc,
|
||
MAC_FONT_TRAITS_ATTRIBUTE);
|
||
if (dict == NULL)
|
||
continue;
|
||
|
||
num = CFDictionaryGetValue (dict, MAC_FONT_SYMBOLIC_TRAIT);
|
||
CFRelease (dict);
|
||
if (num == NULL
|
||
|| !cfnumber_get_font_symbolic_traits_value (num, &sym_traits))
|
||
continue;
|
||
|
||
if (spacing >= 0
|
||
&& !(synth_sym_traits & MAC_FONT_TRAIT_MONO_SPACE)
|
||
&& (((sym_traits & MAC_FONT_TRAIT_MONO_SPACE) != 0)
|
||
!= (spacing >= FONT_SPACING_MONO)))
|
||
continue;
|
||
|
||
/* Don't use a color bitmap font unless its family is
|
||
explicitly specified. */
|
||
if ((sym_traits & MAC_FONT_TRAIT_COLOR_GLYPHS) && NILP (family))
|
||
continue;
|
||
|
||
if (j > 0
|
||
&& !macfont_supports_charset_and_languages_p (desc, charset,
|
||
chars, languages))
|
||
continue;
|
||
|
||
CFArrayAppendValue (filtered_descs, desc);
|
||
CFArrayAppendValue (traits_array,
|
||
(const void *) (uintptr_t) sym_traits);
|
||
}
|
||
|
||
CFRelease (descs);
|
||
descs = filtered_descs;
|
||
descs_count = CFArrayGetCount (descs);
|
||
|
||
for (j = 0; j < descs_count; j++)
|
||
{
|
||
FontDescriptorRef desc = CFArrayGetValueAtIndex (descs, j);
|
||
FontSymbolicTraits sym_traits =
|
||
((FontSymbolicTraits) (uintptr_t)
|
||
CFArrayGetValueAtIndex (traits_array, j));
|
||
FontSymbolicTraits mask_min, mask_max, imask, bmask, mmask;
|
||
|
||
mask_min = ((synth_sym_traits ^ sym_traits)
|
||
& (MAC_FONT_TRAIT_ITALIC | MAC_FONT_TRAIT_BOLD));
|
||
if (FONT_SLANT_NUMERIC (spec) < 0)
|
||
mask_min &= ~MAC_FONT_TRAIT_ITALIC;
|
||
if (FONT_WEIGHT_NUMERIC (spec) < 0)
|
||
mask_min &= ~MAC_FONT_TRAIT_BOLD;
|
||
|
||
mask_max = (synth_sym_traits & ~sym_traits);
|
||
/* Synthetic bold does not work for bitmap-only fonts on Mac
|
||
OS X 10.6. */
|
||
if ((mask_min ^ mask_max) & MAC_FONT_TRAIT_BOLD)
|
||
{
|
||
CFNumberRef format =
|
||
mac_font_descriptor_copy_attribute (desc,
|
||
MAC_FONT_FORMAT_ATTRIBUTE);
|
||
|
||
if (format)
|
||
{
|
||
uint32_t format_val;
|
||
|
||
if (CFNumberGetValue (format, kCFNumberSInt32Type,
|
||
&format_val)
|
||
&& format_val == MAC_FONT_FORMAT_BITMAP)
|
||
mask_max &= ~MAC_FONT_TRAIT_BOLD;
|
||
}
|
||
}
|
||
if (spacing >= 0)
|
||
mask_min |= (mask_max & MAC_FONT_TRAIT_MONO_SPACE);
|
||
|
||
for (mmask = (mask_min & MAC_FONT_TRAIT_MONO_SPACE);
|
||
mmask <= (mask_max & MAC_FONT_TRAIT_MONO_SPACE);
|
||
mmask += MAC_FONT_TRAIT_MONO_SPACE)
|
||
for (bmask = (mask_min & MAC_FONT_TRAIT_BOLD);
|
||
bmask <= (mask_max & MAC_FONT_TRAIT_BOLD);
|
||
bmask += MAC_FONT_TRAIT_BOLD)
|
||
for (imask = (mask_min & MAC_FONT_TRAIT_ITALIC);
|
||
imask <= (mask_max & MAC_FONT_TRAIT_ITALIC);
|
||
imask += MAC_FONT_TRAIT_ITALIC)
|
||
{
|
||
FontSymbolicTraits synth = (imask | bmask | mmask);
|
||
|
||
if (synth == 0
|
||
|| j == macfont_closest_traits_index (traits_array,
|
||
(sym_traits | synth)))
|
||
{
|
||
entity = macfont_descriptor_entity (desc, extra, synth);
|
||
if (! NILP (entity))
|
||
val = Fcons (entity, val);
|
||
}
|
||
}
|
||
}
|
||
|
||
CFRelease (traits_array);
|
||
CFRelease (descs);
|
||
}
|
||
|
||
CFRelease (families);
|
||
val = Fnreverse (val);
|
||
goto finish;
|
||
err:
|
||
val = Qnil;
|
||
|
||
finish:
|
||
FONT_ADD_LOG ("macfont-list", spec, val);
|
||
if (charset) CFRelease (charset);
|
||
if (languages) CFRelease (languages);
|
||
if (attributes) CFRelease (attributes);
|
||
if (family_name) CFRelease (family_name);
|
||
|
||
unblock_input ();
|
||
|
||
return val;
|
||
}
|
||
|
||
static Lisp_Object
|
||
macfont_match (struct frame * frame, Lisp_Object spec)
|
||
{
|
||
Lisp_Object entity = Qnil;
|
||
CFMutableDictionaryRef attributes;
|
||
FontDescriptorRef pat_desc = NULL, desc = NULL;
|
||
|
||
block_input ();
|
||
|
||
attributes = macfont_create_attributes_with_spec (spec);
|
||
if (attributes)
|
||
{
|
||
pat_desc = mac_font_descriptor_create_with_attributes (attributes);
|
||
CFRelease (attributes);
|
||
}
|
||
if (pat_desc)
|
||
{
|
||
desc = mac_font_descriptor_create_matching_font_descriptor (pat_desc,
|
||
NULL);
|
||
CFRelease (pat_desc);
|
||
}
|
||
if (desc)
|
||
{
|
||
entity = macfont_descriptor_entity (desc, AREF (spec, FONT_EXTRA_INDEX),
|
||
0);
|
||
CFRelease (desc);
|
||
}
|
||
unblock_input ();
|
||
|
||
FONT_ADD_LOG ("macfont-match", spec, entity);
|
||
return entity;
|
||
}
|
||
|
||
static Lisp_Object
|
||
macfont_list_family (struct frame *frame)
|
||
{
|
||
Lisp_Object list = Qnil;
|
||
CFArrayRef families;
|
||
|
||
block_input ();
|
||
|
||
families = mac_font_create_available_families ();
|
||
if (families)
|
||
{
|
||
CFIndex i, count = CFArrayGetCount (families);
|
||
|
||
for (i = 0; i < count; i++)
|
||
list = Fcons (macfont_intern_prop_cfstring (CFArrayGetValueAtIndex (families, i)), list);
|
||
CFRelease (families);
|
||
}
|
||
|
||
unblock_input ();
|
||
|
||
return list;
|
||
}
|
||
|
||
static void
|
||
macfont_free_entity (Lisp_Object entity)
|
||
{
|
||
Lisp_Object val = assq_no_quit (QCfont_entity,
|
||
AREF (entity, FONT_EXTRA_INDEX));
|
||
CFStringRef name = XSAVE_POINTER (XCDR (val), 0);
|
||
|
||
block_input ();
|
||
CFRelease (name);
|
||
unblock_input ();
|
||
}
|
||
|
||
static Lisp_Object
|
||
macfont_open (struct frame * f, Lisp_Object entity, int pixel_size)
|
||
{
|
||
Lisp_Object val, font_object;
|
||
CFStringRef font_name;
|
||
struct macfont_info *macfont_info = NULL;
|
||
struct font *font;
|
||
int size;
|
||
FontRef macfont;
|
||
FontSymbolicTraits sym_traits;
|
||
char name[256];
|
||
int len, i, total_width;
|
||
CGGlyph glyph;
|
||
CGFloat ascent, descent, leading;
|
||
|
||
val = assq_no_quit (QCfont_entity, AREF (entity, FONT_EXTRA_INDEX));
|
||
if (! CONSP (val)
|
||
|| XTYPE (XCDR (val)) != Lisp_Misc
|
||
|| XMISCTYPE (XCDR (val)) != Lisp_Misc_Save_Value)
|
||
return Qnil;
|
||
font_name = XSAVE_POINTER (XCDR (val), 0);
|
||
sym_traits = XSAVE_INTEGER (XCDR (val), 1);
|
||
|
||
size = XINT (AREF (entity, FONT_SIZE_INDEX));
|
||
if (size == 0)
|
||
size = pixel_size;
|
||
|
||
block_input ();
|
||
macfont = mac_font_create_with_name (font_name, size);
|
||
if (macfont)
|
||
{
|
||
int fontsize = (int) [((NSFont *) macfont) pointSize];
|
||
if (fontsize != size) size = fontsize;
|
||
}
|
||
unblock_input ();
|
||
if (! macfont)
|
||
return Qnil;
|
||
|
||
font_object = font_make_object (VECSIZE (struct macfont_info), entity, size);
|
||
ASET (font_object, FONT_TYPE_INDEX, macfont_driver.type);
|
||
len = font_unparse_xlfd (entity, size, name, 256);
|
||
if (len > 0)
|
||
ASET (font_object, FONT_NAME_INDEX, make_string (name, len));
|
||
len = font_unparse_fcname (entity, size, name, 256);
|
||
if (len > 0)
|
||
ASET (font_object, FONT_FULLNAME_INDEX, make_string (name, len));
|
||
else
|
||
ASET (font_object, FONT_FULLNAME_INDEX,
|
||
AREF (font_object, FONT_NAME_INDEX));
|
||
font = XFONT_OBJECT (font_object);
|
||
font->pixel_size = size;
|
||
font->driver = &macfont_driver;
|
||
font->encoding_charset = font->repertory_charset = -1;
|
||
|
||
block_input ();
|
||
|
||
macfont_info = (struct macfont_info *) font;
|
||
macfont_info->macfont = macfont;
|
||
macfont_info->cgfont = mac_font_copy_graphics_font (macfont);
|
||
|
||
val = assq_no_quit (QCdestination, AREF (entity, FONT_EXTRA_INDEX));
|
||
if (CONSP (val) && EQ (XCDR (val), make_number (1)))
|
||
macfont_info->screen_font = mac_screen_font_create_with_name (font_name,
|
||
size);
|
||
else
|
||
macfont_info->screen_font = NULL;
|
||
macfont_info->cache = macfont_lookup_cache (font_name);
|
||
macfont_retain_cache (macfont_info->cache);
|
||
macfont_info->metrics = NULL;
|
||
macfont_info->metrics_nrows = 0;
|
||
macfont_info->synthetic_italic_p = 0;
|
||
macfont_info->synthetic_bold_p = 0;
|
||
macfont_info->spacing = MACFONT_SPACING_PROPORTIONAL;
|
||
macfont_info->antialias = MACFONT_ANTIALIAS_DEFAULT;
|
||
if (!(sym_traits & MAC_FONT_TRAIT_ITALIC)
|
||
&& FONT_SLANT_NUMERIC (entity) == FONT_SLANT_SYNTHETIC_ITALIC)
|
||
macfont_info->synthetic_italic_p = 1;
|
||
if (!(sym_traits & MAC_FONT_TRAIT_BOLD)
|
||
&& FONT_WEIGHT_NUMERIC (entity) == FONT_WEIGHT_SYNTHETIC_BOLD)
|
||
macfont_info->synthetic_bold_p = 1;
|
||
if (sym_traits & MAC_FONT_TRAIT_MONO_SPACE)
|
||
macfont_info->spacing = MACFONT_SPACING_MONO;
|
||
else if (INTEGERP (AREF (entity, FONT_SPACING_INDEX))
|
||
&& (XINT (AREF (entity, FONT_SPACING_INDEX))
|
||
== FONT_SPACING_SYNTHETIC_MONO))
|
||
macfont_info->spacing = MACFONT_SPACING_SYNTHETIC_MONO;
|
||
if (macfont_info->synthetic_italic_p || macfont_info->synthetic_bold_p)
|
||
macfont_info->antialias = MACFONT_ANTIALIAS_ON;
|
||
else
|
||
{
|
||
val = assq_no_quit (QCantialias, AREF (entity, FONT_EXTRA_INDEX));
|
||
if (CONSP (val))
|
||
macfont_info->antialias =
|
||
NILP (XCDR (val)) ? MACFONT_ANTIALIAS_OFF : MACFONT_ANTIALIAS_ON;
|
||
}
|
||
macfont_info->color_bitmap_p = 0;
|
||
if (sym_traits & MAC_FONT_TRAIT_COLOR_GLYPHS)
|
||
macfont_info->color_bitmap_p = 1;
|
||
|
||
glyph = macfont_get_glyph_for_character (font, ' ');
|
||
if (glyph != kCGFontIndexInvalid)
|
||
font->space_width = macfont_glyph_extents (font, glyph, NULL, NULL, 0);
|
||
else
|
||
/* dirty workaround */
|
||
font->space_width = pixel_size;
|
||
|
||
total_width = font->space_width;
|
||
for (i = 1; i < 95; i++)
|
||
{
|
||
glyph = macfont_get_glyph_for_character (font, ' ' + i);
|
||
if (glyph == kCGFontIndexInvalid)
|
||
break;
|
||
total_width += macfont_glyph_extents (font, glyph, NULL, NULL, 0);
|
||
}
|
||
if (i == 95)
|
||
font->average_width = total_width / 95;
|
||
else
|
||
font->average_width = font->space_width; /* XXX */
|
||
|
||
if (!(macfont_info->screen_font
|
||
&& mac_screen_font_get_metrics (macfont_info->screen_font,
|
||
&ascent, &descent, &leading)))
|
||
{
|
||
CFStringRef family_name;
|
||
|
||
ascent = mac_font_get_ascent (macfont);
|
||
descent = mac_font_get_descent (macfont);
|
||
leading = mac_font_get_leading (macfont);
|
||
/* AppKit and WebKit do some adjustment to the heights of
|
||
Courier, Helvetica, and Times. */
|
||
family_name = mac_font_copy_family_name (macfont);
|
||
if (family_name)
|
||
{
|
||
if ((CFStringCompare (family_name, CFSTR ("Courier"), 0)
|
||
== kCFCompareEqualTo)
|
||
|| (CFStringCompare (family_name, CFSTR ("Helvetica"), 0)
|
||
== kCFCompareEqualTo)
|
||
|| (CFStringCompare (family_name, CFSTR ("Times"), 0)
|
||
== kCFCompareEqualTo))
|
||
ascent += (ascent + descent) * .15f;
|
||
else if (CFStringHasPrefix (family_name, CFSTR ("Hiragino")))
|
||
{
|
||
leading *= .25f;
|
||
ascent += leading;
|
||
}
|
||
CFRelease (family_name);
|
||
}
|
||
}
|
||
font->ascent = ascent + 0.5f;
|
||
val = assq_no_quit (QCminspace, AREF (entity, FONT_EXTRA_INDEX));
|
||
if (CONSP (val) && !NILP (XCDR (val)))
|
||
font->descent = descent + 0.5f;
|
||
else
|
||
font->descent = descent + leading + 0.5f;
|
||
font->height = font->ascent + font->descent;
|
||
|
||
font->underline_position = - mac_font_get_underline_position (macfont) + 0.5f;
|
||
font->underline_thickness = mac_font_get_underline_thickness (macfont) + 0.5f;
|
||
|
||
unblock_input ();
|
||
|
||
/* Unfortunately Xft doesn't provide a way to get minimum char
|
||
width. So, we use space_width instead. */
|
||
font->min_width = font->max_width = font->space_width; /* XXX */
|
||
|
||
font->baseline_offset = 0;
|
||
font->relative_compose = 0;
|
||
font->default_ascent = 0;
|
||
font->vertical_centering = 0;
|
||
|
||
return font_object;
|
||
}
|
||
|
||
static void
|
||
macfont_close (struct font *font)
|
||
{
|
||
struct macfont_info *macfont_info = (struct macfont_info *) font;
|
||
int i;
|
||
|
||
block_input ();
|
||
CFRelease (macfont_info->macfont);
|
||
CGFontRelease (macfont_info->cgfont);
|
||
if (macfont_info->screen_font)
|
||
CFRelease (macfont_info->screen_font);
|
||
macfont_release_cache (macfont_info->cache);
|
||
for (i = 0; i < macfont_info->metrics_nrows; i++)
|
||
if (macfont_info->metrics[i])
|
||
xfree (macfont_info->metrics[i]);
|
||
if (macfont_info->metrics)
|
||
xfree (macfont_info->metrics);
|
||
unblock_input ();
|
||
}
|
||
|
||
static int
|
||
macfont_has_char (Lisp_Object font, int c)
|
||
{
|
||
int result;
|
||
CFCharacterSetRef charset;
|
||
|
||
block_input ();
|
||
if (FONT_ENTITY_P (font))
|
||
{
|
||
Lisp_Object val;
|
||
CFStringRef name;
|
||
|
||
val = assq_no_quit (QCfont_entity, AREF (font, FONT_EXTRA_INDEX));
|
||
val = XCDR (val);
|
||
name = XSAVE_POINTER (val, 0);
|
||
charset = macfont_get_cf_charset_for_name (name);
|
||
}
|
||
else
|
||
charset = macfont_get_cf_charset (XFONT_OBJECT (font));
|
||
|
||
result = CFCharacterSetIsLongCharacterMember (charset, c);
|
||
unblock_input ();
|
||
|
||
return result;
|
||
}
|
||
|
||
static unsigned
|
||
macfont_encode_char (struct font *font, int c)
|
||
{
|
||
struct macfont_info *macfont_info = (struct macfont_info *) font;
|
||
CGGlyph glyph;
|
||
|
||
block_input ();
|
||
glyph = macfont_get_glyph_for_character (font, c);
|
||
unblock_input ();
|
||
|
||
return glyph != kCGFontIndexInvalid ? glyph : FONT_INVALID_CODE;
|
||
}
|
||
|
||
static int
|
||
macfont_text_extents (struct font *font, unsigned int *code, int nglyphs,
|
||
struct font_metrics *metrics)
|
||
{
|
||
int width, i;
|
||
|
||
block_input ();
|
||
width = macfont_glyph_extents (font, code[0], metrics, NULL, 0);
|
||
for (i = 1; i < nglyphs; i++)
|
||
{
|
||
struct font_metrics m;
|
||
int w = macfont_glyph_extents (font, code[i], metrics ? &m : NULL,
|
||
NULL, 0);
|
||
|
||
if (metrics)
|
||
{
|
||
if (width + m.lbearing < metrics->lbearing)
|
||
metrics->lbearing = width + m.lbearing;
|
||
if (width + m.rbearing > metrics->rbearing)
|
||
metrics->rbearing = width + m.rbearing;
|
||
if (m.ascent > metrics->ascent)
|
||
metrics->ascent = m.ascent;
|
||
if (m.descent > metrics->descent)
|
||
metrics->descent = m.descent;
|
||
}
|
||
width += w;
|
||
}
|
||
unblock_input ();
|
||
|
||
if (metrics)
|
||
metrics->width = width;
|
||
|
||
return width;
|
||
}
|
||
|
||
static int
|
||
macfont_draw (struct glyph_string *s, int from, int to, int x, int y,
|
||
bool with_background)
|
||
{
|
||
struct frame * f = s->f;
|
||
struct macfont_info *macfont_info = (struct macfont_info *) s->font;
|
||
FontRef macfont = macfont_info->macfont;
|
||
CGContextRef context;
|
||
BOOL isComposite = s->first_glyph->type == COMPOSITE_GLYPH;
|
||
int end = isComposite ? s->cmp_to : s->nchars;
|
||
int len = end - s->cmp_from;
|
||
struct face *face = s->face;
|
||
int i;
|
||
|
||
block_input ();
|
||
|
||
context = [[NSGraphicsContext currentContext] graphicsPort];
|
||
CGContextSaveGState (context);
|
||
|
||
#if 0
|
||
if (s->num_clips > 0)
|
||
{
|
||
CGRect clips[2];
|
||
|
||
for (i = 0; i < s->num_clips; i++)
|
||
clips[i] = mac_rect_make (f, s->clip[i].left, s->clip[i].top,
|
||
s->clip[i].right - s->clip[i].left,
|
||
s->clip[i].bottom - s->clip[i].top);
|
||
CGContextClipToRects (context, clips, s->num_clips);
|
||
}
|
||
#endif
|
||
|
||
if (with_background)
|
||
{
|
||
if (s->hl == DRAW_MOUSE_FACE)
|
||
{
|
||
face = FACE_FROM_ID (s->f, MOUSE_HL_INFO (s->f)->mouse_face_face_id);
|
||
if (!face)
|
||
face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
|
||
}
|
||
|
||
CG_SET_FILL_COLOR_WITH_FACE_BACKGROUND (context, face, f);
|
||
CGContextFillRect (context,
|
||
CGRectMake (x, y,
|
||
s->width, FONT_HEIGHT (s->font)));
|
||
}
|
||
|
||
if (macfont_info->cgfont)
|
||
{
|
||
CGGlyph *glyphs = alloca (sizeof (CGGlyph) * len);
|
||
CGPoint *positions = alloca (sizeof (CGPoint) * len);
|
||
CGFloat total_width = 0;
|
||
CGFloat font_size = mac_font_get_size (macfont);
|
||
CGAffineTransform atfm;
|
||
CGFloat advance_delta = 0;
|
||
int y_draw = -s->ybase;
|
||
int no_antialias_p =
|
||
(macfont_info->antialias == MACFONT_ANTIALIAS_OFF
|
||
|| (macfont_info->antialias == MACFONT_ANTIALIAS_DEFAULT
|
||
&& font_size <= macfont_antialias_threshold));
|
||
|
||
for (i = 0; i < len; i++)
|
||
{
|
||
int width;
|
||
|
||
glyphs[i] = *(s->char2b + s->cmp_from + i);
|
||
width = (s->padding_p ? 1
|
||
: macfont_glyph_extents (s->font, glyphs[i],
|
||
NULL, &advance_delta,
|
||
no_antialias_p));
|
||
positions[i].x = total_width + advance_delta;
|
||
positions[i].y = 0;
|
||
total_width += width;
|
||
}
|
||
|
||
CGContextScaleCTM (context, 1, -1);
|
||
CG_SET_FILL_COLOR_WITH_FACE_FOREGROUND (context, face, s->f);
|
||
if (macfont_info->synthetic_italic_p)
|
||
atfm = synthetic_italic_atfm;
|
||
else
|
||
atfm = CGAffineTransformIdentity;
|
||
if (macfont_info->synthetic_bold_p)
|
||
{
|
||
CGContextSetTextDrawingMode (context, kCGTextFillStroke);
|
||
CGContextSetLineWidth (context, synthetic_bold_factor * font_size);
|
||
CG_SET_STROKE_COLOR_WITH_FACE_FOREGROUND (context, face, f);
|
||
}
|
||
if (no_antialias_p)
|
||
CGContextSetShouldAntialias (context, false);
|
||
|
||
CGContextSetTextMatrix (context, atfm);
|
||
CGContextSetTextPosition (context, x, y_draw);
|
||
|
||
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
|
||
if (macfont_info->color_bitmap_p
|
||
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
|
||
&& CTFontDrawGlyphs != NULL
|
||
#endif
|
||
)
|
||
{
|
||
if (len > 0)
|
||
{
|
||
CTFontDrawGlyphs (macfont, glyphs, positions, len, context);
|
||
}
|
||
}
|
||
else
|
||
#endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */
|
||
{
|
||
CGContextSetFont (context, macfont_info->cgfont);
|
||
CGContextSetFontSize (context, font_size);
|
||
CGContextShowGlyphsAtPositions (context, glyphs, positions, len);
|
||
}
|
||
}
|
||
|
||
CGContextRestoreGState (context);
|
||
|
||
unblock_input ();
|
||
|
||
return len;
|
||
}
|
||
|
||
static Lisp_Object
|
||
macfont_shape (Lisp_Object lgstring)
|
||
{
|
||
struct font *font;
|
||
struct macfont_info *macfont_info;
|
||
FontRef macfont;
|
||
ptrdiff_t glyph_len, len, i, j;
|
||
CFIndex nonbmp_len;
|
||
UniChar *unichars;
|
||
CFIndex *nonbmp_indices;
|
||
CFStringRef string;
|
||
CFIndex used = 0;
|
||
struct mac_glyph_layout *glyph_layouts;
|
||
|
||
CHECK_FONT_GET_OBJECT (LGSTRING_FONT (lgstring), font);
|
||
macfont_info = (struct macfont_info *) font;
|
||
macfont = macfont_info->macfont;
|
||
|
||
glyph_len = LGSTRING_GLYPH_LEN (lgstring);
|
||
nonbmp_len = 0;
|
||
for (i = 0; i < glyph_len; i++)
|
||
{
|
||
Lisp_Object lglyph = LGSTRING_GLYPH (lgstring, i);
|
||
|
||
if (NILP (lglyph))
|
||
break;
|
||
if (LGLYPH_CHAR (lglyph) >= 0x10000)
|
||
nonbmp_len++;
|
||
}
|
||
|
||
len = i;
|
||
|
||
if (INT_MAX / 2 < len)
|
||
memory_full (SIZE_MAX);
|
||
|
||
unichars = alloca (sizeof (UniChar) * (len + nonbmp_len));
|
||
nonbmp_indices = alloca (sizeof (CFIndex) * (nonbmp_len + 1));
|
||
for (i = j = 0; i < len; i++)
|
||
{
|
||
UTF32Char c = LGLYPH_CHAR (LGSTRING_GLYPH (lgstring, i));
|
||
|
||
if (macfont_store_utf32char_to_unichars (c, unichars + i + j) > 1)
|
||
{
|
||
nonbmp_indices[j] = i + j;
|
||
j++;
|
||
}
|
||
}
|
||
nonbmp_indices[j] = len + j; /* sentinel */
|
||
|
||
block_input ();
|
||
|
||
string = CFStringCreateWithCharactersNoCopy (NULL, unichars, len + nonbmp_len,
|
||
kCFAllocatorNull);
|
||
if (string)
|
||
{
|
||
glyph_layouts = alloca (sizeof (struct mac_glyph_layout) * glyph_len);
|
||
if (macfont_info->screen_font)
|
||
used = mac_screen_font_shape (macfont_info->screen_font, string,
|
||
glyph_layouts, glyph_len);
|
||
else
|
||
used = mac_font_shape (macfont, string, glyph_layouts, glyph_len);
|
||
CFRelease (string);
|
||
}
|
||
|
||
unblock_input ();
|
||
|
||
if (used == 0)
|
||
return Qnil;
|
||
|
||
block_input ();
|
||
|
||
for (i = 0; i < used; i++)
|
||
{
|
||
Lisp_Object lglyph = LGSTRING_GLYPH (lgstring, i);
|
||
struct mac_glyph_layout *gl = glyph_layouts + i;
|
||
EMACS_INT from, to;
|
||
struct font_metrics metrics;
|
||
int xoff, yoff, wadjust;
|
||
|
||
if (NILP (lglyph))
|
||
{
|
||
lglyph = Fmake_vector (make_number (LGLYPH_SIZE), Qnil);
|
||
LGSTRING_SET_GLYPH (lgstring, i, lglyph);
|
||
}
|
||
|
||
from = gl->comp_range.location;
|
||
/* Convert UTF-16 index to UTF-32. */
|
||
j = 0;
|
||
while (nonbmp_indices[j] < from)
|
||
j++;
|
||
from -= j;
|
||
LGLYPH_SET_FROM (lglyph, from);
|
||
|
||
to = gl->comp_range.location + gl->comp_range.length;
|
||
/* Convert UTF-16 index to UTF-32. */
|
||
while (nonbmp_indices[j] < to)
|
||
j++;
|
||
to -= j;
|
||
LGLYPH_SET_TO (lglyph, to - 1);
|
||
|
||
/* LGLYPH_CHAR is used in `describe-char' for checking whether
|
||
the composition is trivial. */
|
||
{
|
||
UTF32Char c;
|
||
|
||
if (unichars[gl->string_index] >= 0xD800
|
||
&& unichars[gl->string_index] < 0xDC00)
|
||
c = (((unichars[gl->string_index] - 0xD800) << 10)
|
||
+ (unichars[gl->string_index + 1] - 0xDC00) + 0x10000);
|
||
else
|
||
c = unichars[gl->string_index];
|
||
if (macfont_get_glyph_for_character (font, c) != gl->glyph_id)
|
||
c = 0;
|
||
LGLYPH_SET_CHAR (lglyph, c);
|
||
}
|
||
|
||
{
|
||
unsigned long cc = gl->glyph_id;
|
||
LGLYPH_SET_CODE (lglyph, cc);
|
||
}
|
||
|
||
macfont_glyph_extents (font, gl->glyph_id, &metrics, NULL, 0);
|
||
LGLYPH_SET_WIDTH (lglyph, metrics.width);
|
||
LGLYPH_SET_LBEARING (lglyph, metrics.lbearing);
|
||
LGLYPH_SET_RBEARING (lglyph, metrics.rbearing);
|
||
LGLYPH_SET_ASCENT (lglyph, metrics.ascent);
|
||
LGLYPH_SET_DESCENT (lglyph, metrics.descent);
|
||
|
||
xoff = lround (gl->advance_delta);
|
||
yoff = lround (- gl->baseline_delta);
|
||
wadjust = lround (gl->advance);
|
||
if (xoff != 0 || yoff != 0 || wadjust != metrics.width)
|
||
{
|
||
Lisp_Object vec;
|
||
|
||
vec = Fmake_vector (make_number (3), Qnil);
|
||
ASET (vec, 0, make_number (xoff));
|
||
ASET (vec, 1, make_number (yoff));
|
||
ASET (vec, 2, make_number (wadjust));
|
||
LGLYPH_SET_ADJUSTMENT (lglyph, vec);
|
||
}
|
||
}
|
||
|
||
unblock_input ();
|
||
|
||
return make_number (used);
|
||
}
|
||
|
||
/* Structures for the UVS subtable (format 14) in the cmap table. */
|
||
typedef UInt8 UINT24[3];
|
||
|
||
#pragma pack(push, 1)
|
||
struct variation_selector_record
|
||
{
|
||
UINT24 var_selector;
|
||
UInt32 default_uvs_offset, non_default_uvs_offset;
|
||
};
|
||
struct uvs_table
|
||
{
|
||
UInt16 format;
|
||
UInt32 length, num_var_selector_records;
|
||
struct variation_selector_record variation_selector_records[1];
|
||
};
|
||
#define SIZEOF_UVS_TABLE_HEADER \
|
||
(sizeof (struct uvs_table) - sizeof (struct variation_selector_record))
|
||
|
||
struct unicode_value_range
|
||
{
|
||
UINT24 start_unicode_value;
|
||
UInt8 additional_count;
|
||
};
|
||
struct default_uvs_table {
|
||
UInt32 num_unicode_value_ranges;
|
||
struct unicode_value_range unicode_value_ranges[1];
|
||
};
|
||
#define SIZEOF_DEFAULT_UVS_TABLE_HEADER \
|
||
(sizeof (struct default_uvs_table) - sizeof (struct unicode_value_range))
|
||
|
||
struct uvs_mapping
|
||
{
|
||
UINT24 unicode_value;
|
||
UInt16 glyph_id;
|
||
};
|
||
struct non_default_uvs_table
|
||
{
|
||
UInt32 num_uvs_mappings;
|
||
struct uvs_mapping uvs_mappings[1];
|
||
};
|
||
#define SIZEOF_NON_DEFAULT_UVS_TABLE_HEADER \
|
||
(sizeof (struct non_default_uvs_table) - sizeof (struct uvs_mapping))
|
||
#pragma pack(pop)
|
||
|
||
/* Read big endian values. The argument LVAL must be an lvalue. */
|
||
/* I suppose OSReadBigInt* takes care of unaligned data. At least, we
|
||
can find "... = OSReadBigInt32(cdb, 2);" followed by "... =
|
||
OSReadBigInt16(cdb, 7);" in a sample code by Apple. */
|
||
#define BUINT8_VALUE(lval) (*((UInt8 *) &(lval)))
|
||
#define BUINT16_VALUE(lval) OSReadBigInt16 (&(lval), 0)
|
||
/* Succeeding one byte should also be accessible. */
|
||
#define BUINT24_VALUE(lval) (OSReadBigInt32 (&(lval), 0) >> 8)
|
||
#define BUINT32_VALUE(lval) OSReadBigInt32 (&(lval), 0)
|
||
|
||
/* Return UVS subtable for the specified FONT. If the subtable is not
|
||
found or ill-formatted, then return NULL. */
|
||
|
||
static CFDataRef
|
||
mac_font_copy_uvs_table (FontRef font)
|
||
{
|
||
CFDataRef cmap_table, uvs_table = NULL;
|
||
|
||
cmap_table = mac_font_copy_non_synthetic_table (font, cmapFontTableTag);
|
||
if (cmap_table)
|
||
{
|
||
sfntCMapHeader *cmap = (sfntCMapHeader *) CFDataGetBytePtr (cmap_table);
|
||
struct uvs_table *uvs;
|
||
struct variation_selector_record *records;
|
||
UInt32 cmap_len, ntables, i, uvs_offset, uvs_len, nrecords;
|
||
|
||
#if __LP64__
|
||
if (CFDataGetLength (cmap_table) > UINT32_MAX)
|
||
goto finish;
|
||
#endif
|
||
|
||
cmap_len = CFDataGetLength (cmap_table);
|
||
if (sizeof_sfntCMapHeader > cmap_len)
|
||
goto finish;
|
||
|
||
ntables = BUINT16_VALUE (cmap->numTables);
|
||
if (ntables > ((cmap_len - sizeof_sfntCMapHeader)
|
||
/ sizeof_sfntCMapEncoding))
|
||
goto finish;
|
||
|
||
for (i = 0; i < ntables; i++)
|
||
if ((BUINT16_VALUE (cmap->encoding[i].platformID)
|
||
== kFontUnicodePlatform)
|
||
&& (BUINT16_VALUE (cmap->encoding[i].scriptID)
|
||
== 5)) /* kFontUnicodeV4_0VariationSequenceSemantics */
|
||
{
|
||
uvs_offset = BUINT32_VALUE (cmap->encoding[i].offset);
|
||
break;
|
||
}
|
||
if (i == ntables
|
||
|| uvs_offset > cmap_len
|
||
|| SIZEOF_UVS_TABLE_HEADER > cmap_len - uvs_offset)
|
||
goto finish;
|
||
|
||
uvs = (struct uvs_table *) ((UInt8 *) cmap + uvs_offset);
|
||
uvs_len = BUINT32_VALUE (uvs->length);
|
||
if (uvs_len > cmap_len - uvs_offset
|
||
|| SIZEOF_UVS_TABLE_HEADER > uvs_len)
|
||
goto finish;
|
||
|
||
if (BUINT16_VALUE (uvs->format) != 14)
|
||
goto finish;
|
||
|
||
nrecords = BUINT32_VALUE (uvs->num_var_selector_records);
|
||
if (nrecords > ((uvs_len - SIZEOF_UVS_TABLE_HEADER)
|
||
/ sizeof (struct variation_selector_record)))
|
||
goto finish;
|
||
|
||
records = uvs->variation_selector_records;
|
||
for (i = 0; i < nrecords; i++)
|
||
{
|
||
UInt32 default_uvs_offset, non_default_uvs_offset;
|
||
|
||
default_uvs_offset = BUINT32_VALUE (records[i].default_uvs_offset);
|
||
if (default_uvs_offset)
|
||
{
|
||
struct default_uvs_table *default_uvs;
|
||
UInt32 nranges;
|
||
|
||
if (default_uvs_offset > uvs_len
|
||
|| (SIZEOF_DEFAULT_UVS_TABLE_HEADER
|
||
> uvs_len - default_uvs_offset))
|
||
goto finish;
|
||
|
||
default_uvs = ((struct default_uvs_table *)
|
||
((UInt8 *) uvs + default_uvs_offset));
|
||
nranges = BUINT32_VALUE (default_uvs->num_unicode_value_ranges);
|
||
if (nranges > ((uvs_len - default_uvs_offset
|
||
- SIZEOF_DEFAULT_UVS_TABLE_HEADER)
|
||
/ sizeof (struct unicode_value_range)))
|
||
goto finish;
|
||
/* Now 2 * nranges can't overflow, so we can safely use
|
||
`(lo + hi) / 2' instead of `lo + (hi - lo) / 2' in
|
||
mac_font_get_glyphs_for_variants. */
|
||
}
|
||
|
||
non_default_uvs_offset =
|
||
BUINT32_VALUE (records[i].non_default_uvs_offset);
|
||
if (non_default_uvs_offset)
|
||
{
|
||
struct non_default_uvs_table *non_default_uvs;
|
||
UInt32 nmappings;
|
||
|
||
if (non_default_uvs_offset > uvs_len
|
||
|| (SIZEOF_NON_DEFAULT_UVS_TABLE_HEADER
|
||
> uvs_len - non_default_uvs_offset))
|
||
goto finish;
|
||
|
||
non_default_uvs = ((struct non_default_uvs_table *)
|
||
((UInt8 *) uvs + non_default_uvs_offset));
|
||
nmappings = BUINT32_VALUE (non_default_uvs->num_uvs_mappings);
|
||
if (nmappings > ((uvs_len - non_default_uvs_offset
|
||
- SIZEOF_NON_DEFAULT_UVS_TABLE_HEADER)
|
||
/ sizeof (struct uvs_mapping)))
|
||
goto finish;
|
||
/* Now 2 * nmappings can't overflow, so we can safely
|
||
use `(lo + hi) / 2' instead of `lo + (hi - lo) / 2'
|
||
in mac_font_get_glyphs_for_variants. */
|
||
}
|
||
}
|
||
|
||
uvs_table = CFDataCreate (NULL, (UInt8 *) uvs, uvs_len);
|
||
|
||
finish:
|
||
CFRelease (cmap_table);
|
||
}
|
||
|
||
return uvs_table;
|
||
}
|
||
|
||
/* Find an entry in the given UVS subtable UVS_TABLE for a variation
|
||
sequence consisting of the given base character C and each
|
||
variation selector SELECTORS[i] for 0 <= i < COUNT, and store the
|
||
result (explained below) into the corresponding GLYPHS[i]. If the
|
||
entry is found in the Default UVS Table, then the result is 0. If
|
||
the entry is found in the Non-Default UVS Table, then the result is
|
||
the associated glyph ID. Otherwise, kCGFontIndexInvalid. The
|
||
elements in SELECTORS must be sorted in strictly increasing
|
||
order. */
|
||
|
||
static void
|
||
mac_font_get_glyphs_for_variants (CFDataRef uvs_table, UTF32Char c,
|
||
const UTF32Char selectors[], CGGlyph glyphs[],
|
||
CFIndex count)
|
||
{
|
||
struct uvs_table *uvs = (struct uvs_table *) CFDataGetBytePtr (uvs_table);
|
||
struct variation_selector_record *records = uvs->variation_selector_records;
|
||
CFIndex i;
|
||
UInt32 ir, nrecords;
|
||
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
|
||
dispatch_queue_t queue =
|
||
dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
|
||
dispatch_group_t group = dispatch_group_create ();
|
||
#endif
|
||
|
||
nrecords = BUINT32_VALUE (uvs->num_var_selector_records);
|
||
i = 0;
|
||
ir = 0;
|
||
while (i < count && ir < nrecords)
|
||
{
|
||
UInt32 default_uvs_offset, non_default_uvs_offset;
|
||
|
||
if (selectors[i] < BUINT24_VALUE (records[ir].var_selector))
|
||
{
|
||
glyphs[i++] = kCGFontIndexInvalid;
|
||
continue;
|
||
}
|
||
else if (selectors[i] > BUINT24_VALUE (records[ir].var_selector))
|
||
{
|
||
ir++;
|
||
continue;
|
||
}
|
||
|
||
/* selectors[i] == BUINT24_VALUE (records[ir].var_selector) */
|
||
default_uvs_offset = BUINT32_VALUE (records[ir].default_uvs_offset);
|
||
non_default_uvs_offset =
|
||
BUINT32_VALUE (records[ir].non_default_uvs_offset);
|
||
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
|
||
dispatch_group_async (group, queue, ^{
|
||
#endif
|
||
glyphs[i] = kCGFontIndexInvalid;
|
||
|
||
if (default_uvs_offset)
|
||
{
|
||
struct default_uvs_table *default_uvs =
|
||
(struct default_uvs_table *) ((UInt8 *) uvs
|
||
+ default_uvs_offset);
|
||
struct unicode_value_range *ranges =
|
||
default_uvs->unicode_value_ranges;
|
||
UInt32 lo, hi;
|
||
|
||
lo = 0;
|
||
hi = BUINT32_VALUE (default_uvs->num_unicode_value_ranges);
|
||
while (lo < hi)
|
||
{
|
||
UInt32 mid = (lo + hi) / 2;
|
||
|
||
if (c < BUINT24_VALUE (ranges[mid].start_unicode_value))
|
||
hi = mid;
|
||
else
|
||
lo = mid + 1;
|
||
}
|
||
if (hi > 0
|
||
&& (c <= (BUINT24_VALUE (ranges[hi - 1].start_unicode_value)
|
||
+ BUINT8_VALUE (ranges[hi - 1].additional_count))))
|
||
glyphs[i] = 0;
|
||
}
|
||
|
||
if (glyphs[i] == kCGFontIndexInvalid && non_default_uvs_offset)
|
||
{
|
||
struct non_default_uvs_table *non_default_uvs =
|
||
(struct non_default_uvs_table *) ((UInt8 *) uvs
|
||
+ non_default_uvs_offset);
|
||
struct uvs_mapping *mappings = non_default_uvs->uvs_mappings;
|
||
UInt32 lo, hi;
|
||
|
||
lo = 0;
|
||
hi = BUINT32_VALUE (non_default_uvs->num_uvs_mappings);
|
||
while (lo < hi)
|
||
{
|
||
UInt32 mid = (lo + hi) / 2;
|
||
|
||
if (c < BUINT24_VALUE (mappings[mid].unicode_value))
|
||
hi = mid;
|
||
else
|
||
lo = mid + 1;
|
||
}
|
||
if (hi > 0 &&
|
||
BUINT24_VALUE (mappings[hi - 1].unicode_value) == c)
|
||
glyphs[i] = BUINT16_VALUE (mappings[hi - 1].glyph_id);
|
||
}
|
||
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
|
||
});
|
||
#endif
|
||
i++;
|
||
ir++;
|
||
}
|
||
while (i < count)
|
||
glyphs[i++] = kCGFontIndexInvalid;
|
||
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
|
||
dispatch_group_wait (group, DISPATCH_TIME_FOREVER);
|
||
dispatch_release (group);
|
||
#endif
|
||
}
|
||
|
||
static int
|
||
macfont_variation_glyphs (struct font *font, int c, unsigned variations[256])
|
||
{
|
||
CFDataRef uvs_table;
|
||
CharacterCollection uvs_collection;
|
||
int i, n = 0;
|
||
|
||
block_input ();
|
||
uvs_table = macfont_get_uvs_table (font, &uvs_collection);
|
||
|
||
if (uvs_table)
|
||
{
|
||
UTF32Char selectors[256];
|
||
CGGlyph glyphs[256];
|
||
|
||
for (i = 0; i < 16; i++)
|
||
selectors[i] = 0xFE00 + i;
|
||
for (; i < 256; i++)
|
||
selectors[i] = 0xE0100 + (i - 16);
|
||
mac_font_get_glyphs_for_variants (uvs_table, c, selectors, glyphs, 256);
|
||
for (i = 0; i < 256; i++)
|
||
{
|
||
CGGlyph glyph = glyphs[i];
|
||
|
||
if (uvs_collection != MAC_CHARACTER_COLLECTION_IDENTITY_MAPPING
|
||
&& glyph != kCGFontIndexInvalid)
|
||
glyph = macfont_get_glyph_for_cid (font, uvs_collection, glyph);
|
||
if (glyph == kCGFontIndexInvalid)
|
||
variations[i] = 0;
|
||
else
|
||
{
|
||
variations[i] = (glyph ? glyph
|
||
: macfont_get_glyph_for_character (font, c));
|
||
n++;
|
||
}
|
||
}
|
||
}
|
||
unblock_input ();
|
||
|
||
return n;
|
||
}
|
||
|
||
static const char *const macfont_booleans[] = {
|
||
":antialias",
|
||
":minspace",
|
||
NULL,
|
||
};
|
||
|
||
static const char *const macfont_non_booleans[] = {
|
||
":lang",
|
||
":script",
|
||
":destination",
|
||
NULL,
|
||
};
|
||
|
||
static void
|
||
macfont_filter_properties (Lisp_Object font, Lisp_Object alist)
|
||
{
|
||
font_filter_properties (font, alist, macfont_booleans, macfont_non_booleans);
|
||
}
|
||
|
||
static Boolean
|
||
mac_ctfont_descriptor_supports_languages (CTFontDescriptorRef descriptor,
|
||
CFArrayRef languages)
|
||
{
|
||
Boolean result = true;
|
||
CFArrayRef desc_languages =
|
||
CTFontDescriptorCopyAttribute (descriptor, kCTFontLanguagesAttribute);
|
||
|
||
if (desc_languages == NULL)
|
||
result = false;
|
||
else
|
||
{
|
||
CFIndex desc_languages_count, i, languages_count;
|
||
|
||
desc_languages_count = CFArrayGetCount (desc_languages);
|
||
languages_count = CFArrayGetCount (languages);
|
||
for (i = 0; i < languages_count; i++)
|
||
if (!CFArrayContainsValue (desc_languages,
|
||
CFRangeMake (0, desc_languages_count),
|
||
CFArrayGetValueAtIndex (languages, i)))
|
||
{
|
||
result = false;
|
||
break;
|
||
}
|
||
CFRelease (desc_languages);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
static CFStringRef
|
||
mac_ctfont_create_preferred_family_for_attributes (CFDictionaryRef attributes)
|
||
{
|
||
CFStringRef result = NULL;
|
||
CFStringRef charset_string =
|
||
CFDictionaryGetValue (attributes, MAC_FONT_CHARACTER_SET_STRING_ATTRIBUTE);
|
||
|
||
if (charset_string && CFStringGetLength (charset_string) > 0)
|
||
{
|
||
CFStringRef keys[] = {
|
||
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1090
|
||
kCTLanguageAttributeName
|
||
#else
|
||
CFSTR ("NSLanguage")
|
||
#endif
|
||
};
|
||
CFTypeRef values[] = {NULL};
|
||
CFIndex num_values = 0;
|
||
CFArrayRef languages
|
||
= CFDictionaryGetValue (attributes, MAC_FONT_LANGUAGES_ATTRIBUTE);
|
||
|
||
if (languages && CFArrayGetCount (languages) > 0)
|
||
{
|
||
if (CTGetCoreTextVersion () >= kCTVersionNumber10_9)
|
||
values[num_values++] = CFArrayGetValueAtIndex (languages, 0);
|
||
else
|
||
{
|
||
CFCharacterSetRef charset =
|
||
CFDictionaryGetValue (attributes,
|
||
MAC_FONT_CHARACTER_SET_ATTRIBUTE);
|
||
|
||
result = mac_font_copy_default_name_for_charset_and_languages (charset, languages);
|
||
}
|
||
}
|
||
if (result == NULL)
|
||
{
|
||
CFAttributedStringRef attr_string = NULL;
|
||
CTLineRef ctline = NULL;
|
||
CFDictionaryRef attrs
|
||
= CFDictionaryCreate (NULL, (const void **) keys,
|
||
(const void **) values, num_values,
|
||
&kCFTypeDictionaryKeyCallBacks,
|
||
&kCFTypeDictionaryValueCallBacks);
|
||
|
||
if (attrs)
|
||
{
|
||
attr_string = CFAttributedStringCreate (NULL, charset_string,
|
||
attrs);
|
||
CFRelease (attrs);
|
||
}
|
||
if (attr_string)
|
||
{
|
||
ctline = CTLineCreateWithAttributedString (attr_string);
|
||
CFRelease (attr_string);
|
||
}
|
||
if (ctline)
|
||
{
|
||
CFArrayRef runs = CTLineGetGlyphRuns (ctline);
|
||
CFIndex i, nruns = CFArrayGetCount (runs);
|
||
CTFontRef font;
|
||
|
||
for (i = 0; i < nruns; i++)
|
||
{
|
||
CTRunRef run = CFArrayGetValueAtIndex (runs, i);
|
||
CFDictionaryRef attributes = CTRunGetAttributes (run);
|
||
CTFontRef font_in_run;
|
||
|
||
if (attributes == NULL)
|
||
break;
|
||
font_in_run =
|
||
CFDictionaryGetValue (attributes, kCTFontAttributeName);
|
||
if (font_in_run == NULL)
|
||
break;
|
||
if (i == 0)
|
||
font = font_in_run;
|
||
else if (!mac_ctfont_equal_in_postscript_name (font,
|
||
font_in_run))
|
||
break;
|
||
}
|
||
if (nruns > 0 && i == nruns)
|
||
result = CTFontCopyAttribute (font, kCTFontFamilyNameAttribute);
|
||
CFRelease (ctline);
|
||
}
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
static inline double
|
||
mac_ctfont_get_advance_width_for_glyph (CTFontRef font, CGGlyph glyph)
|
||
{
|
||
return CTFontGetAdvancesForGlyphs (font, kCTFontDefaultOrientation,
|
||
&glyph, NULL, 1);
|
||
}
|
||
|
||
static inline CGRect
|
||
mac_ctfont_get_bounding_rect_for_glyph (CTFontRef font, CGGlyph glyph)
|
||
{
|
||
return CTFontGetBoundingRectsForGlyphs (font, kCTFontDefaultOrientation,
|
||
&glyph, NULL, 1);
|
||
}
|
||
|
||
static CFArrayRef
|
||
mac_ctfont_create_available_families (void)
|
||
{
|
||
CFMutableArrayRef families = NULL;
|
||
|
||
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
|
||
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
|
||
if (CTFontManagerCopyAvailableFontFamilyNames != NULL)
|
||
#endif
|
||
{
|
||
CFArrayRef orig_families = CTFontManagerCopyAvailableFontFamilyNames ();
|
||
|
||
if (orig_families)
|
||
{
|
||
CFIndex i, count = CFArrayGetCount (orig_families);
|
||
|
||
families = CFArrayCreateMutable (NULL, count, &kCFTypeArrayCallBacks);
|
||
if (families)
|
||
for (i = 0; i < count; i++)
|
||
{
|
||
CFStringRef family = CFArrayGetValueAtIndex (orig_families, i);
|
||
|
||
if (!CFStringHasPrefix (family, CFSTR ("."))
|
||
&& (CTFontManagerCompareFontFamilyNames (family,
|
||
CFSTR ("LastResort"),
|
||
NULL)
|
||
!= kCFCompareEqualTo))
|
||
CFArrayAppendValue (families, family);
|
||
}
|
||
CFRelease (orig_families);
|
||
}
|
||
}
|
||
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
|
||
else /* CTFontManagerCopyAvailableFontFamilyNames == NULL */
|
||
#endif
|
||
#endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 */
|
||
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
|
||
{
|
||
CTFontCollectionRef collection;
|
||
CFArrayRef descs = NULL;
|
||
|
||
collection = CTFontCollectionCreateFromAvailableFonts (NULL);
|
||
if (collection)
|
||
{
|
||
descs = CTFontCollectionCreateMatchingFontDescriptors (collection);
|
||
CFRelease (collection);
|
||
}
|
||
if (descs)
|
||
{
|
||
CFIndex i, count = CFArrayGetCount (descs);
|
||
|
||
families = CFArrayCreateMutable (NULL, count, &kCFTypeArrayCallBacks);
|
||
if (families)
|
||
for (i = 0; i < count; i++)
|
||
{
|
||
FontDescriptorRef desc = CFArrayGetValueAtIndex (descs, i);
|
||
CFStringRef name =
|
||
mac_font_descriptor_copy_attribute (desc,
|
||
MAC_FONT_FAMILY_NAME_ATTRIBUTE);
|
||
|
||
if (name)
|
||
{
|
||
CFIndex p, limit = CFArrayGetCount (families);
|
||
|
||
p = CFArrayBSearchValues (families, CFRangeMake (0, limit),
|
||
(const void *) name,
|
||
mac_font_family_compare, NULL);
|
||
if (p >= limit)
|
||
CFArrayAppendValue (families, name);
|
||
else if (mac_font_family_compare
|
||
(CFArrayGetValueAtIndex (families, p),
|
||
name, NULL) != kCFCompareEqualTo)
|
||
CFArrayInsertValueAtIndex (families, p, name);
|
||
CFRelease (name);
|
||
}
|
||
}
|
||
CFRelease (descs);
|
||
}
|
||
}
|
||
#endif
|
||
|
||
return families;
|
||
}
|
||
|
||
static Boolean
|
||
mac_ctfont_equal_in_postscript_name (CTFontRef font1, CTFontRef font2)
|
||
{
|
||
Boolean result;
|
||
CFStringRef name1, name2;
|
||
|
||
if (font1 == font2)
|
||
return true;
|
||
|
||
result = false;
|
||
name1 = CTFontCopyPostScriptName (font1);
|
||
if (name1)
|
||
{
|
||
name2 = CTFontCopyPostScriptName (font2);
|
||
if (name2)
|
||
{
|
||
result = (CFStringCompare (name1, name2, 0) == kCFCompareEqualTo);
|
||
CFRelease (name2);
|
||
}
|
||
CFRelease (name1);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
static CTLineRef
|
||
mac_ctfont_create_line_with_string_and_font (CFStringRef string,
|
||
CTFontRef macfont)
|
||
{
|
||
CFStringRef keys[] = {kCTFontAttributeName, kCTKernAttributeName};
|
||
CFTypeRef values[] = {NULL, NULL};
|
||
CFDictionaryRef attributes = NULL;
|
||
CFAttributedStringRef attr_string = NULL;
|
||
CTLineRef ctline = NULL;
|
||
float float_zero = 0.0f;
|
||
|
||
values[0] = macfont;
|
||
values[1] = CFNumberCreate (NULL, kCFNumberFloatType, &float_zero);
|
||
if (values[1])
|
||
{
|
||
attributes = CFDictionaryCreate (NULL, (const void **) keys,
|
||
(const void **) values,
|
||
sizeof (keys) / sizeof (keys[0]),
|
||
&kCFTypeDictionaryKeyCallBacks,
|
||
&kCFTypeDictionaryValueCallBacks);
|
||
CFRelease (values[1]);
|
||
}
|
||
if (attributes)
|
||
{
|
||
attr_string = CFAttributedStringCreate (NULL, string, attributes);
|
||
CFRelease (attributes);
|
||
}
|
||
if (attr_string)
|
||
{
|
||
ctline = CTLineCreateWithAttributedString (attr_string);
|
||
CFRelease (attr_string);
|
||
}
|
||
if (ctline)
|
||
{
|
||
/* Abandon if ctline contains some fonts other than the
|
||
specified one. */
|
||
CFArrayRef runs = CTLineGetGlyphRuns (ctline);
|
||
CFIndex i, nruns = CFArrayGetCount (runs);
|
||
|
||
for (i = 0; i < nruns; i++)
|
||
{
|
||
CTRunRef run = CFArrayGetValueAtIndex (runs, i);
|
||
CFDictionaryRef attributes = CTRunGetAttributes (run);
|
||
CTFontRef font_in_run;
|
||
|
||
if (attributes == NULL)
|
||
break;
|
||
font_in_run =
|
||
CFDictionaryGetValue (attributes, kCTFontAttributeName);
|
||
if (font_in_run == NULL)
|
||
break;
|
||
if (!mac_ctfont_equal_in_postscript_name (macfont, font_in_run))
|
||
break;
|
||
}
|
||
if (i < nruns)
|
||
{
|
||
CFRelease (ctline);
|
||
ctline = NULL;
|
||
}
|
||
}
|
||
|
||
return ctline;
|
||
}
|
||
|
||
static CFIndex
|
||
mac_ctfont_shape (CTFontRef font, CFStringRef string,
|
||
struct mac_glyph_layout *glyph_layouts, CFIndex glyph_len)
|
||
{
|
||
CFIndex used, result = 0;
|
||
CTLineRef ctline = mac_ctfont_create_line_with_string_and_font (string, font);
|
||
|
||
if (ctline == NULL)
|
||
return 0;
|
||
|
||
used = CTLineGetGlyphCount (ctline);
|
||
if (used <= glyph_len)
|
||
{
|
||
CFArrayRef ctruns = CTLineGetGlyphRuns (ctline);
|
||
CFIndex k, ctrun_count = CFArrayGetCount (ctruns);
|
||
CGFloat total_advance = 0;
|
||
CFIndex total_glyph_count = 0;
|
||
|
||
for (k = 0; k < ctrun_count; k++)
|
||
{
|
||
CTRunRef ctrun = CFArrayGetValueAtIndex (ctruns, k);
|
||
CFIndex i, min_location, glyph_count = CTRunGetGlyphCount (ctrun);
|
||
struct mac_glyph_layout *glbuf = glyph_layouts + total_glyph_count;
|
||
CFRange string_range, comp_range, range;
|
||
CFIndex *permutation;
|
||
|
||
if (CTRunGetStatus (ctrun) & kCTRunStatusRightToLeft)
|
||
permutation = xmalloc (sizeof (CFIndex) * glyph_count);
|
||
else
|
||
permutation = NULL;
|
||
|
||
#define RIGHT_TO_LEFT_P permutation
|
||
|
||
/* Now the `comp_range' member of struct mac_glyph_layout is
|
||
temporarily used as a work area such that:
|
||
glbuf[i].comp_range.location =
|
||
min {compRange[i + 1].location, ...,
|
||
compRange[glyph_count - 1].location,
|
||
maxRange (stringRangeForCTRun)}
|
||
glbuf[i].comp_range.length = maxRange (compRange[i])
|
||
where compRange[i] is the range of composed characters
|
||
containing i-th glyph. */
|
||
string_range = CTRunGetStringRange (ctrun);
|
||
min_location = string_range.location + string_range.length;
|
||
for (i = 0; i < glyph_count; i++)
|
||
{
|
||
struct mac_glyph_layout *gl = glbuf + glyph_count - i - 1;
|
||
CFIndex glyph_index;
|
||
CFRange rng;
|
||
|
||
if (!RIGHT_TO_LEFT_P)
|
||
glyph_index = glyph_count - i - 1;
|
||
else
|
||
glyph_index = i;
|
||
CTRunGetStringIndices (ctrun, CFRangeMake (glyph_index, 1),
|
||
&gl->string_index);
|
||
rng =
|
||
CFStringGetRangeOfComposedCharactersAtIndex (string,
|
||
gl->string_index);
|
||
gl->comp_range.location = min_location;
|
||
gl->comp_range.length = rng.location + rng.length;
|
||
if (rng.location < min_location)
|
||
min_location = rng.location;
|
||
}
|
||
|
||
/* Fill the `comp_range' member of struct mac_glyph_layout,
|
||
and setup a permutation for right-to-left text. */
|
||
comp_range = CFRangeMake (string_range.location, 0);
|
||
range = CFRangeMake (0, 0);
|
||
while (1)
|
||
{
|
||
struct mac_glyph_layout *gl =
|
||
glbuf + range.location + range.length;
|
||
|
||
if (gl->comp_range.length
|
||
> comp_range.location + comp_range.length)
|
||
comp_range.length = gl->comp_range.length - comp_range.location;
|
||
min_location = gl->comp_range.location;
|
||
range.length++;
|
||
|
||
if (min_location >= comp_range.location + comp_range.length)
|
||
{
|
||
comp_range.length = min_location - comp_range.location;
|
||
for (i = 0; i < range.length; i++)
|
||
{
|
||
glbuf[range.location + i].comp_range = comp_range;
|
||
if (RIGHT_TO_LEFT_P)
|
||
permutation[range.location + i] =
|
||
range.location + range.length - i - 1;
|
||
}
|
||
|
||
comp_range = CFRangeMake (min_location, 0);
|
||
range.location += range.length;
|
||
range.length = 0;
|
||
if (range.location == glyph_count)
|
||
break;
|
||
}
|
||
}
|
||
|
||
/* Then fill the remaining members. */
|
||
for (range = CFRangeMake (0, 1); range.location < glyph_count;
|
||
range.location++)
|
||
{
|
||
struct mac_glyph_layout *gl;
|
||
CGPoint position;
|
||
|
||
if (!RIGHT_TO_LEFT_P)
|
||
gl = glbuf + range.location;
|
||
else
|
||
{
|
||
CFIndex src, dest;
|
||
|
||
src = glyph_count - 1 - range.location;
|
||
dest = permutation[src];
|
||
gl = glbuf + dest;
|
||
if (src < dest)
|
||
{
|
||
CFIndex tmp = gl->string_index;
|
||
|
||
gl->string_index = glbuf[src].string_index;
|
||
glbuf[src].string_index = tmp;
|
||
}
|
||
}
|
||
CTRunGetGlyphs (ctrun, range, &gl->glyph_id);
|
||
|
||
CTRunGetPositions (ctrun, range, &position);
|
||
gl->advance_delta = position.x - total_advance;
|
||
gl->baseline_delta = position.y;
|
||
gl->advance = (gl->advance_delta
|
||
+ CTRunGetTypographicBounds (ctrun, range,
|
||
NULL, NULL, NULL));
|
||
total_advance += gl->advance;
|
||
}
|
||
|
||
if (RIGHT_TO_LEFT_P)
|
||
xfree (permutation);
|
||
|
||
#undef RIGHT_TO_LEFT_P
|
||
|
||
total_glyph_count += glyph_count;
|
||
}
|
||
|
||
result = used;
|
||
}
|
||
CFRelease (ctline);
|
||
|
||
return result;
|
||
}
|
||
|
||
/* The function below seems to cause a memory leak for the CFString
|
||
created by CFStringCreateWithCharacters as of Mac OS X 10.5.8 and
|
||
10.6.3. For now, we use the NSGlyphInfo version instead. */
|
||
#if USE_CT_GLYPH_INFO
|
||
static CGGlyph
|
||
mac_ctfont_get_glyph_for_cid (CTFontRef font, CTCharacterCollection collection,
|
||
CGFontIndex cid)
|
||
{
|
||
CGGlyph result = kCGFontIndexInvalid;
|
||
UniChar characters[] = {0xfffd};
|
||
CFStringRef string;
|
||
CFAttributedStringRef attr_string = NULL;
|
||
CTLineRef ctline = NULL;
|
||
|
||
string = CFStringCreateWithCharacters (NULL, characters,
|
||
sizeof (characters)
|
||
/ sizeof (characters[0]));
|
||
if (string)
|
||
{
|
||
CTGlyphInfoRef glyph_info =
|
||
CTGlyphInfoCreateWithCharacterIdentifier (cid, collection, string);
|
||
CFDictionaryRef attributes = NULL;
|
||
|
||
if (glyph_info)
|
||
{
|
||
CFStringRef keys[] = {kCTFontAttributeName,
|
||
kCTGlyphInfoAttributeName};
|
||
CFTypeRef values[] = {font, glyph_info};
|
||
|
||
attributes = CFDictionaryCreate (NULL, (const void **) keys,
|
||
(const void **) values,
|
||
sizeof (keys) / sizeof (keys[0]),
|
||
&kCFTypeDictionaryKeyCallBacks,
|
||
&kCFTypeDictionaryValueCallBacks);
|
||
CFRelease (glyph_info);
|
||
}
|
||
if (attributes)
|
||
{
|
||
attr_string = CFAttributedStringCreate (NULL, string, attributes);
|
||
CFRelease (attributes);
|
||
}
|
||
CFRelease (string);
|
||
}
|
||
if (attr_string)
|
||
{
|
||
ctline = CTLineCreateWithAttributedString (attr_string);
|
||
CFRelease (attr_string);
|
||
}
|
||
if (ctline)
|
||
{
|
||
CFArrayRef runs = CTLineGetGlyphRuns (ctline);
|
||
|
||
if (CFArrayGetCount (runs) > 0)
|
||
{
|
||
CTRunRef run = CFArrayGetValueAtIndex (runs, 0);
|
||
CFDictionaryRef attributes = CTRunGetAttributes (run);
|
||
|
||
if (attributes)
|
||
{
|
||
CTFontRef font_in_run =
|
||
CFDictionaryGetValue (attributes, kCTFontAttributeName);
|
||
|
||
if (font_in_run
|
||
&& mac_ctfont_equal_in_postscript_name (font_in_run, font))
|
||
{
|
||
CTRunGetGlyphs (run, CFRangeMake (0, 1), &result);
|
||
if (result >= CTFontGetGlyphCount (font))
|
||
result = kCGFontIndexInvalid;
|
||
}
|
||
}
|
||
}
|
||
CFRelease (ctline);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
#endif
|
||
|
||
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
|
||
static inline int
|
||
mac_font_family_group (CFStringRef family)
|
||
{
|
||
if (CFStringHasPrefix (family, CFSTR ("#")))
|
||
return 2;
|
||
else
|
||
{
|
||
CFRange range;
|
||
|
||
range = CFStringFind (family, CFSTR ("Apple"),
|
||
kCFCompareCaseInsensitive | kCFCompareAnchored);
|
||
if (range.location != kCFNotFound)
|
||
return 1;
|
||
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
static CFComparisonResult
|
||
mac_font_family_compare (const void *val1, const void *val2, void *context)
|
||
{
|
||
CFStringRef family1 = (CFStringRef) val1, family2 = (CFStringRef) val2;
|
||
int group1, group2;
|
||
|
||
group1 = mac_font_family_group (family1);
|
||
group2 = mac_font_family_group (family2);
|
||
if (group1 < group2)
|
||
return kCFCompareLessThan;
|
||
if (group1 > group2)
|
||
return kCFCompareGreaterThan;
|
||
return CFStringCompare (family1, family2, kCFCompareCaseInsensitive);
|
||
}
|
||
#endif /* MAC_OS_X_VERSION_MIN_REQUIRED < 1060 */
|
||
|
||
static CFArrayRef
|
||
mac_font_copy_default_descriptors_for_language (CFStringRef language)
|
||
{
|
||
CFArrayRef result = NULL;
|
||
|
||
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1080
|
||
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1080
|
||
if (CTFontCopyDefaultCascadeListForLanguages != NULL)
|
||
#endif
|
||
{
|
||
CTFontRef user_font =
|
||
CTFontCreateUIFontForLanguage (kCTFontUserFontType, 0, language);
|
||
|
||
if (user_font)
|
||
{
|
||
CFArrayRef languages =
|
||
CFArrayCreate (NULL, (const void **) &language, 1,
|
||
&kCFTypeArrayCallBacks);
|
||
|
||
if (languages)
|
||
{
|
||
result = CTFontCopyDefaultCascadeListForLanguages (user_font,
|
||
languages);
|
||
CFRelease (languages);
|
||
}
|
||
CFRelease (user_font);
|
||
}
|
||
}
|
||
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1080
|
||
else /* CTFontCopyDefaultCascadeListForLanguages == NULL */
|
||
#endif
|
||
#endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1080 */
|
||
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1080
|
||
{
|
||
CFIndex i;
|
||
|
||
for (i = 0; macfont_language_default_font_names[i].language; i++)
|
||
{
|
||
if (CFStringCompare (macfont_language_default_font_names[i].language,
|
||
language, 0) == kCFCompareEqualTo)
|
||
{
|
||
CFMutableArrayRef descriptors =
|
||
CFArrayCreateMutable (NULL, 0, &kCFTypeArrayCallBacks);
|
||
|
||
if (descriptors)
|
||
{
|
||
CFIndex j;
|
||
|
||
for (j = 0;
|
||
macfont_language_default_font_names[i].font_names[j];
|
||
j++)
|
||
{
|
||
CFDictionaryRef attributes =
|
||
CFDictionaryCreate (NULL,
|
||
((const void **)
|
||
&MAC_FONT_NAME_ATTRIBUTE),
|
||
((const void **)
|
||
&macfont_language_default_font_names[i].font_names[j]),
|
||
1, &kCFTypeDictionaryKeyCallBacks,
|
||
&kCFTypeDictionaryValueCallBacks);
|
||
|
||
if (attributes)
|
||
{
|
||
FontDescriptorRef pat_desc =
|
||
mac_font_descriptor_create_with_attributes (attributes);
|
||
|
||
if (pat_desc)
|
||
{
|
||
FontDescriptorRef descriptor =
|
||
mac_font_descriptor_create_matching_font_descriptor (pat_desc, NULL);
|
||
|
||
if (descriptor)
|
||
{
|
||
CFArrayAppendValue (descriptors, descriptor);
|
||
CFRelease (descriptor);
|
||
}
|
||
CFRelease (pat_desc);
|
||
}
|
||
CFRelease (attributes);
|
||
}
|
||
}
|
||
result = descriptors;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
|
||
return result;
|
||
}
|
||
|
||
static CFStringRef
|
||
mac_font_copy_default_name_for_charset_and_languages (CFCharacterSetRef charset,
|
||
CFArrayRef languages)
|
||
{
|
||
CFStringRef result = NULL;
|
||
CFStringRef language = CFArrayGetValueAtIndex (languages, 0);
|
||
CFArrayRef descriptors =
|
||
mac_font_copy_default_descriptors_for_language (language);
|
||
|
||
if (descriptors)
|
||
{
|
||
CFIndex i, count = CFArrayGetCount (descriptors);
|
||
|
||
for (i = 0; i < count; i++)
|
||
{
|
||
FontDescriptorRef descriptor =
|
||
CFArrayGetValueAtIndex (descriptors, i);
|
||
|
||
if (macfont_supports_charset_and_languages_p (descriptor, charset,
|
||
Qnil, languages))
|
||
{
|
||
CFStringRef family =
|
||
mac_font_descriptor_copy_attribute (descriptor,
|
||
MAC_FONT_FAMILY_NAME_ATTRIBUTE);
|
||
if (family)
|
||
{
|
||
if (!CFStringHasPrefix (family, CFSTR ("."))
|
||
&& (CFStringCompare (family, CFSTR ("LastResort"), 0)
|
||
!= kCFCompareEqualTo))
|
||
{
|
||
result = family;
|
||
break;
|
||
}
|
||
else
|
||
CFRelease (family);
|
||
}
|
||
}
|
||
}
|
||
CFRelease (descriptors);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
void *
|
||
macfont_get_nsctfont (struct font *font)
|
||
{
|
||
struct macfont_info *macfont_info = (struct macfont_info *) font;
|
||
FontRef macfont = macfont_info->macfont;
|
||
|
||
return (void *) macfont;
|
||
}
|
||
|
||
void
|
||
mac_register_font_driver (struct frame *f)
|
||
{
|
||
register_font_driver (&macfont_driver, f);
|
||
}
|
||
|
||
#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= 1050
|
||
|
||
|
||
void
|
||
syms_of_macfont (void)
|
||
{
|
||
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050
|
||
static struct font_driver mac_font_driver;
|
||
|
||
DEFSYM (Qmac_ct, "mac-ct");
|
||
macfont_driver.type = Qmac_ct;
|
||
register_font_driver (&macfont_driver, NULL);
|
||
|
||
DEFSYM (QCdestination, ":destination");
|
||
DEFSYM (QCminspace, ":minspace");
|
||
#endif
|
||
}
|