/* FCFontEnumerator.m Copyright (C) 2003 Free Software Foundation, Inc. August 31, 2003 Written by Banlu Kemiyatorn Base on original code of Alex Malmberg Rewrite: Fred Kiefer Date: Jan 2006 This file is part of GNUstep. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; see the file COPYING.LIB. If not, see or write to the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gsc/GSGState.h" #include "fontconfig/FCFontEnumerator.h" #include "fontconfig/FCFontInfo.h" // Old versions of fontconfig don't have FC_WEIGHT_ULTRABLACK defined. // Use the maximal value instead. #ifndef FC_WEIGHT_ULTRABLACK #define FC_WEIGHT_ULTRABLACK FC_WEIGHT_BLACK #endif @implementation FCFontEnumerator NSMutableDictionary * __allFonts; + (FCFaceInfo *) fontWithName: (NSString *) name { FCFaceInfo *face; face = [__allFonts objectForKey: name]; if (!face) { NSDebugLLog(@"NSFont", @"Font not found %@", name); } return face; } + (Class) faceInfoClass { [self subclassResponsibility: _cmd]; return nil; } // Make a GNUstep style font descriptor from a FcPattern static NSArray *faFromFc(FcPattern *pat) { int weight, slant, spacing, nsweight; unsigned int nstraits = 0; char *family; NSMutableString *name, *style; if (FcPatternGetInteger(pat, FC_WEIGHT, 0, &weight) != FcResultMatch || FcPatternGetInteger(pat, FC_SLANT, 0, &slant) != FcResultMatch || FcPatternGetString(pat, FC_FAMILY, 0, (FcChar8 **)&family) != FcResultMatch) return nil; if (FcPatternGetInteger(pat, FC_SPACING, 0, &spacing) == FcResultMatch) if (spacing==FC_MONO || spacing==FC_CHARCELL) nstraits |= NSFixedPitchFontMask; name = [NSMutableString stringWithCapacity: 100]; style = [NSMutableString stringWithCapacity: 100]; [name appendString: [NSString stringWithUTF8String: family]]; switch (weight) { case FC_WEIGHT_LIGHT: [style appendString: @"Light"]; nsweight = 3; break; case FC_WEIGHT_MEDIUM: nsweight = 6; break; case FC_WEIGHT_DEMIBOLD: [style appendString: @"Demibold"]; nsweight = 7; break; case FC_WEIGHT_BOLD: [style appendString: @"Bold"]; nsweight = 9; nstraits |= NSBoldFontMask; break; case FC_WEIGHT_BLACK: [style appendString: @"Black"]; nsweight = 12; nstraits |= NSBoldFontMask; break; default: nsweight = 6; } switch (slant) { case FC_SLANT_ROMAN: break; case FC_SLANT_ITALIC: [style appendString: @"Italic"]; nstraits |= NSItalicFontMask; break; case FC_SLANT_OBLIQUE: [style appendString: @"Oblique"]; nstraits |= NSItalicFontMask; break; } if ([style length] > 0) { [name appendString: @"-"]; [name appendString: style]; } else { [style appendString: @"Roman"]; } return [NSArray arrayWithObjects: name, style, [NSNumber numberWithInt: nsweight], [NSNumber numberWithUnsignedInt: nstraits], nil]; } - (void) enumerateFontsAndFamilies { int i; NSMutableDictionary *fcxft_allFontFamilies = [NSMutableDictionary new]; NSMutableDictionary *fcxft_allFonts = [NSMutableDictionary new]; NSMutableArray *fcxft_allFontNames = [NSMutableArray new]; Class faceInfoClass = [[self class] faceInfoClass]; FcPattern *pat = FcPatternCreate(); FcObjectSet *os = FcObjectSetBuild(FC_FAMILY, FC_SLANT, FC_WEIGHT, FC_SPACING, NULL); FcFontSet *fs = FcFontList(NULL, pat, os); FcPatternDestroy(pat); FcObjectSetDestroy(os); for (i = 0; i < fs->nfont; i++) { char *family; if (FcPatternGetString(fs->fonts[i], FC_FAMILY, 0, (FcChar8 **)&family) == FcResultMatch) { NSArray *fontArray; if ((fontArray = faFromFc(fs->fonts[i]))) { NSString *familyString; NSMutableArray *familyArray; FCFaceInfo *aFont; NSString *name = [fontArray objectAtIndex: 0]; familyString = [NSString stringWithUTF8String: family]; familyArray = [fcxft_allFontFamilies objectForKey: familyString]; if (familyArray == nil) { NSDebugLLog(@"NSFont", @"Found font family %@", familyString); familyArray = [[NSMutableArray alloc] init]; [fcxft_allFontFamilies setObject: familyArray forKey: familyString]; RELEASE(familyArray); } NSDebugLLog(@"NSFont", @"fc enumerator: adding font: %@", name); [familyArray addObject: fontArray]; [fcxft_allFontNames addObject: name]; aFont = [[faceInfoClass alloc] initWithfamilyName: familyString weight: [[fontArray objectAtIndex: 2] intValue] traits: [[fontArray objectAtIndex: 3] unsignedIntValue] pattern: fs->fonts[i]]; [fcxft_allFonts setObject: aFont forKey: name]; RELEASE(aFont); } } } FcFontSetDestroy (fs); allFontNames = fcxft_allFontNames; allFontFamilies = fcxft_allFontFamilies; __allFonts = fcxft_allFonts; } - (NSString *) defaultSystemFontName { if ([allFontNames containsObject: @"Bitstream Vera Sans"]) { return @"Bitstream Vera Sans"; } if ([allFontNames containsObject: @"FreeSans"]) { return @"FreeSans"; } if ([allFontNames containsObject: @"DejaVu Sans"]) { return @"DejaVu Sans"; } if ([allFontNames containsObject: @"Tahoma"]) { return @"Tahoma"; } if ([allFontNames containsObject: @"Arial"]) { return @"Arial"; } return @"Helvetica"; } - (NSString *) defaultBoldSystemFontName { if ([allFontNames containsObject: @"Bitstream Vera Sans-Bold"]) { return @"Bitstream Vera Sans-Bold"; } if ([allFontNames containsObject: @"FreeSans-Bold"]) { return @"FreeSans-Bold"; } if ([allFontNames containsObject: @"DejaVu Sans-Bold"]) { return @"DejaVu Sans-Bold"; } if ([allFontNames containsObject: @"Tahoma-Bold"]) { return @"Tahoma-Bold"; } if ([allFontNames containsObject: @"Arial-Bold"]) { return @"Arial-Bold"; } return @"Helvetica-Bold"; } - (NSString *) defaultFixedPitchFontName { if ([allFontNames containsObject: @"Bitstream Vera Sans Mono"]) { return @"Bitstream Vera Sans Mono"; } if ([allFontNames containsObject: @"FreeMono"]) { return @"FreeMono"; } if ([allFontNames containsObject: @"DejaVu Sans Mono"]) { return @"DejaVu Sans Mono"; } if ([allFontNames containsObject: @"Courier New"]) { return @"Courier New"; } return @"Courier"; } /** * Overrides the implementation in GSFontInfo, and delegates the * matching to Fontconfig. */ - (NSArray *) matchingFontDescriptorsFor: (NSDictionary *)attributes { NSMutableArray *descriptors; FcResult result; FcPattern *matchedpat, *pat; FontconfigPatternGenerator *generator; descriptors = [NSMutableArray array]; generator = [[FontconfigPatternGenerator alloc] init]; pat = [generator createPatternWithAttributes: attributes]; DESTROY(generator); FcConfigSubstitute(NULL, pat, FcMatchPattern); FcDefaultSubstitute(pat); result = FcResultMatch; matchedpat = FcFontMatch(NULL, pat, &result); if (result != FcResultMatch) { NSLog(@"Warning, FcFontMatch failed with code: %d", result); } else { FcFontSet *fontSet; result = FcResultMatch; fontSet = FcFontSort(NULL, matchedpat, FcFalse, NULL, &result); if (result == FcResultMatch) { int i; for (i=0; infont; i++) { FontconfigPatternParser *parser = [[FontconfigPatternParser alloc] init]; // FIXME: do we need to match this pattern? FcPattern *matchingpat = fontSet->fonts[i]; NSDictionary *attribs = [parser attributesFromPattern: matchingpat]; [parser release]; [descriptors addObject: [NSFontDescriptor fontDescriptorWithFontAttributes: attribs]]; } } else { NSLog(@"ERROR! FcFontSort failed"); } FcFontSetDestroy(fontSet); FcPatternDestroy(matchedpat); } FcPatternDestroy(pat); return descriptors; } @end @implementation FontconfigPatternGenerator - (void)addName: (NSString*)name { // FIXME: Fontconfig ignores PostScript names of fonts; we need // https://bugs.freedesktop.org/show_bug.cgi?id=18095 fixed. // This is a heuristic to try to 'parse' a PostScript font name, // however, since they are just unique identifiers for fonts and // don't need to follow any naming convention, this may fail NSRange dash = [name rangeOfString: @"-"]; if (dash.location == NSNotFound) { FcPatternAddString(_pat, FC_FAMILY, (const FcChar8 *)[name UTF8String]); } else { NSString *weightAndSlant = [name substringFromIndex: dash.location + 1]; NSString *family = [name substringToIndex: dash.location]; FcPatternAddString(_pat, FC_FAMILY, (const FcChar8 *)[family UTF8String]); if (NSNotFound != [weightAndSlant rangeOfString: @"Light"].location) { FcPatternAddInteger(_pat, FC_WEIGHT, FC_WEIGHT_LIGHT); } else if (NSNotFound != [weightAndSlant rangeOfString: @"Medium"].location) { FcPatternAddInteger(_pat, FC_WEIGHT, FC_WEIGHT_MEDIUM); } else if (NSNotFound != [weightAndSlant rangeOfString: @"Demibold"].location) { FcPatternAddInteger(_pat, FC_WEIGHT, FC_WEIGHT_DEMIBOLD); } else if (NSNotFound != [weightAndSlant rangeOfString: @"Bold"].location) { FcPatternAddInteger(_pat, FC_WEIGHT, FC_WEIGHT_BOLD); } else if (NSNotFound != [weightAndSlant rangeOfString: @"Black"].location) { FcPatternAddInteger(_pat, FC_WEIGHT, FC_WEIGHT_BLACK); } if (NSNotFound != [weightAndSlant rangeOfString: @"Italic"].location) { FcPatternAddInteger(_pat, FC_SLANT, FC_SLANT_ITALIC); } else if (NSNotFound != [weightAndSlant rangeOfString: @"Oblique"].location) { FcPatternAddInteger(_pat, FC_SLANT, FC_SLANT_OBLIQUE); } if (NSNotFound != [weightAndSlant rangeOfString: @"Condensed"].location) { FcPatternAddInteger(_pat, FC_WIDTH, FC_WIDTH_CONDENSED); } else if (NSNotFound != [weightAndSlant rangeOfString: @"Expanded"].location) { FcPatternAddInteger(_pat, FC_WIDTH, FC_WIDTH_EXPANDED); } } } - (void)addVisibleName: (NSString*)name { FcPatternAddString(_pat, FC_FULLNAME, (const FcChar8 *)[name UTF8String]); } - (void)addFamilyName: (NSString*)name { FcPatternAddString(_pat, FC_FAMILY, (const FcChar8 *)[name UTF8String]); } - (void)addStyleName: (NSString*)style { FcPatternAddString(_pat, FC_STYLE, (const FcChar8 *)[style UTF8String]); } - (void)addTraits: (NSDictionary*)traits { if ([traits objectForKey: NSFontSymbolicTrait]) { NSFontSymbolicTraits symTraits = [[traits objectForKey: NSFontSymbolicTrait] intValue]; if (symTraits & NSFontItalicTrait) { // NOTE: May be overridden by NSFontSlantTrait FcPatternAddInteger(_pat, FC_SLANT, FC_SLANT_ITALIC); } if (symTraits & NSFontBoldTrait) { // NOTE: May be overridden by NSFontWeightTrait FcPatternAddInteger(_pat, FC_WEIGHT, FC_WEIGHT_BOLD); } if (symTraits & NSFontExpandedTrait) { // NOTE: May be overridden by NSFontWidthTrait FcPatternAddInteger(_pat, FC_WIDTH, FC_WIDTH_EXPANDED); } if (symTraits & NSFontCondensedTrait) { // NOTE: May be overridden by NSFontWidthTrait FcPatternAddInteger(_pat, FC_WIDTH, FC_WIDTH_CONDENSED); } if (symTraits & NSFontMonoSpaceTrait) { FcValue value; // If you run "fc-match :spacing=100", you get "DejaVu Sans" even though you would // expect to get "DejaVu Sans Mono". So, we also add "monospace" as a weak family // name to fix the problem. FcPatternAddInteger(_pat, FC_SPACING, FC_MONO); value.type = FcTypeString; value.u.s = (FcChar8*)"monospace"; FcPatternAddWeak(_pat, FC_FAMILY, value, FcTrue); } if (symTraits & NSFontVerticalTrait) { // FIXME: What is this supposed to mean? } if (symTraits & NSFontUIOptimizedTrait) { // NOTE: Fontconfig can't express this } { NSFontFamilyClass class = symTraits & NSFontFamilyClassMask; char *addWeakFamilyName = NULL; switch (class) { default: case NSFontUnknownClass: case NSFontOrnamentalsClass: case NSFontScriptsClass: case NSFontSymbolicClass: // FIXME: Is there some way to convey these to Fontconfig? break; case NSFontOldStyleSerifsClass: case NSFontTransitionalSerifsClass: case NSFontModernSerifsClass: case NSFontClarendonSerifsClass: case NSFontSlabSerifsClass: case NSFontFreeformSerifsClass: addWeakFamilyName = "serif"; break; case NSFontSansSerifClass: addWeakFamilyName = "sans"; break; } if (addWeakFamilyName) { FcValue value; value.type = FcTypeString; value.u.s = (const FcChar8 *)addWeakFamilyName; FcPatternAddWeak(_pat, FC_FAMILY, value, FcTrue); } } } if ([traits objectForKey: NSFontWeightTrait]) { /** * Scale: -1 is thinnest, 0 is normal, 1 is heaviest */ double weight = [[traits objectForKey: NSFontWeightTrait] doubleValue]; int fcWeight; weight = MAX(-1, MIN(1, weight)); if (weight <= 0) { fcWeight = FC_WEIGHT_THIN + ((weight + 1.0) * (FC_WEIGHT_NORMAL - FC_WEIGHT_THIN)); } else { fcWeight = FC_WEIGHT_NORMAL + (weight * (FC_WEIGHT_ULTRABLACK - FC_WEIGHT_NORMAL)); } FcPatternAddInteger(_pat, FC_WEIGHT, fcWeight); } if ([traits objectForKey: NSFontWidthTrait]) { /** * Scale: -1 is most condensed, 0 is normal, 1 is most spread apart */ double width = [[traits objectForKey: NSFontWidthTrait] doubleValue]; int fcWidth; width = MAX(-1, MIN(1, width)); if (width <= 0) { fcWidth = FC_WIDTH_ULTRACONDENSED + ((width + 1.0) * (FC_WIDTH_NORMAL - FC_WIDTH_ULTRACONDENSED)); } else { fcWidth = FC_WIDTH_NORMAL + (width * (FC_WIDTH_ULTRAEXPANDED - FC_WIDTH_NORMAL)); } FcPatternAddInteger(_pat, FC_WIDTH, fcWidth); } if ([traits objectForKey: NSFontSlantTrait]) { /** * Scale: -1 is 30 degree counterclockwise slant, 0 is no slant, 1 * is 30 degree clockwise slant */ double slant = [[traits objectForKey: NSFontSlantTrait] doubleValue]; // NOTE: Fontconfig can't express this as a scale if (slant > 0) { FcPatternAddInteger(_pat, FC_SLANT, FC_SLANT_ITALIC); } else { FcPatternAddInteger(_pat, FC_SLANT, FC_SLANT_ROMAN); } } } - (void)addSize: (NSNumber*)size { FcPatternAddDouble(_pat, FC_SIZE, [size doubleValue]); } - (void)addCharacterSet: (NSCharacterSet*)characterSet { if ([characterSet isKindOfClass: [FontconfigCharacterSet class]]) { // Fast case FcPatternAddCharSet(_pat, FC_CHARSET, [(FontconfigCharacterSet*)characterSet fontconfigCharSet]); } else { // Slow case FcCharSet *fcSet = FcCharSetCreate(); uint32_t plane; for (plane=0; plane<=16; plane++) { if ([characterSet hasMemberInPlane: plane]) { uint32_t codePoint; for (codePoint = plane<<16; codePoint <= 0xffff + (plane<<16); codePoint++) { if ([characterSet longCharacterIsMember: codePoint]) { FcCharSetAddChar(fcSet, codePoint); } } } } FcPatternAddCharSet(_pat, FC_CHARSET, fcSet); FcCharSetDestroy(fcSet); } } #define ADD_TO_PATTERN(key, handlerMethod, valueClass) \ do { \ id value = [_attributes objectForKey: key]; \ if (value) \ { \ if ([value isKindOfClass: valueClass]) \ { \ [self handlerMethod value]; \ } \ else \ { \ NSLog(@"NSFontDescriptor: Ignoring invalid value %@ for attribute %@", value, key); \ } \ } \ } while (0); - (void)addAttributes { ADD_TO_PATTERN(NSFontNameAttribute, addName:, [NSString class]); ADD_TO_PATTERN(NSFontVisibleNameAttribute, addVisibleName:, [NSString class]); ADD_TO_PATTERN(NSFontFamilyAttribute, addFamilyName:, [NSString class]); ADD_TO_PATTERN(NSFontFaceAttribute, addStyleName:, [NSString class]); ADD_TO_PATTERN(NSFontTraitsAttribute, addTraits:, [NSDictionary class]); ADD_TO_PATTERN(NSFontSizeAttribute, addSize:, [NSNumber class]); ADD_TO_PATTERN(NSFontCharacterSetAttribute, addCharacterSet:, [NSCharacterSet class]); } - (FcPattern *)createPatternWithAttributes: (NSDictionary *)attributes { _attributes = attributes; _pat = FcPatternCreate(); [self addAttributes]; return _pat; } @end @implementation FontconfigPatternParser - (NSString*)readFontconfigString: (const char *)key fromPattern: (FcPattern*)pat { unsigned char *string = NULL; if (FcResultMatch == FcPatternGetString(pat, key, 0, &string)) { if (string) { return [NSString stringWithUTF8String: (const char *)string]; } } return nil; } - (NSNumber*)readFontconfigInteger: (const char *)key fromPattern: (FcPattern*)pat { int value; if (FcResultMatch == FcPatternGetInteger(pat, key, 0, &value)) { return [NSNumber numberWithInt: value]; } return nil; } - (NSNumber*)readFontconfigDouble: (const char *)key fromPattern: (FcPattern*)pat { double value; if (FcResultMatch == FcPatternGetDouble(pat, key, 0, &value)) { return [NSNumber numberWithDouble: value]; } return nil; } - (NSString*)readNameFromPattern: (FcPattern*)pat { // FIXME: Hack which generates a PostScript-style name from the // family name and style NSString *family = [self readFontconfigString: FC_FAMILY fromPattern: pat]; NSString *style = [self readFontconfigString: FC_STYLE fromPattern: pat]; if (style) { return [NSString stringWithFormat: @"%@-%@", family, style]; } else { return family; } } - (NSString*)readVisibleNameFromPattern: (FcPattern*)pat { // FIXME: try to get the localized one return [self readFontconfigString: FC_FULLNAME fromPattern: pat]; } - (NSString*)readFamilyNameFromPattern: (FcPattern*)pat { // FIXME: try to get the localized one return [self readFontconfigString: FC_FAMILY fromPattern: pat]; } - (NSString*)readStyleNameFromPattern: (FcPattern*)pat { // FIXME: try to get the localized one return [self readFontconfigString: FC_STYLE fromPattern: pat]; } - (NSDictionary*)readTraitsFromPattern: (FcPattern*)pat { NSMutableDictionary *traits = [NSMutableDictionary dictionary]; NSFontSymbolicTraits symTraits = 0; int value; if (FcResultMatch == FcPatternGetInteger(pat, FC_SLANT, 0, &value)) { if (value == FC_SLANT_ITALIC) { symTraits |= NSFontItalicTrait; } } if (FcResultMatch == FcPatternGetInteger(pat, FC_WEIGHT, 0, &value)) { double weight; if (value >= FC_WEIGHT_BOLD) { symTraits |= NSFontBoldTrait; } if (value <= FC_WEIGHT_NORMAL) { weight = ((value - FC_WEIGHT_THIN) / (double)(FC_WEIGHT_NORMAL - FC_WEIGHT_THIN)) - 1.0; } else { weight = (value - FC_WEIGHT_NORMAL) / (double)(FC_WEIGHT_ULTRABLACK - FC_WEIGHT_NORMAL); } [traits setObject: [NSNumber numberWithDouble: weight] forKey: NSFontWeightTrait]; } if (FcResultMatch == FcPatternGetInteger(pat, FC_WIDTH, 0, &value)) { double width; if (value >= FC_WIDTH_EXPANDED) { symTraits |= NSFontExpandedTrait; } if (value <= FC_WIDTH_CONDENSED) { symTraits |= NSFontCondensedTrait; } if (value <= FC_WIDTH_NORMAL) { width = ((value - FC_WIDTH_ULTRACONDENSED) / (double)(FC_WIDTH_NORMAL - FC_WIDTH_ULTRACONDENSED)) - 1.0; } else { width = (value - FC_WIDTH_NORMAL) / (double)(FC_WIDTH_ULTRAEXPANDED - FC_WIDTH_NORMAL); } [traits setObject: [NSNumber numberWithDouble: width] forKey: NSFontWidthTrait]; } if (FcResultMatch == FcPatternGetInteger(pat, FC_SPACING, 0, &value)) { if (value == FC_MONO || value == FC_CHARCELL) { symTraits |= NSFontMonoSpaceTrait; } } if (symTraits != 0) { [traits setObject: [NSNumber numberWithUnsignedInt: symTraits] forKey: NSFontSymbolicTrait]; } return traits; } - (NSNumber*)readSizeFromPattern: (FcPattern*)pat { return [self readFontconfigDouble: FC_SIZE fromPattern: pat]; } - (NSCharacterSet*)readCharacterSetFromPattern: (FcPattern*)pat { FcCharSet *value; if (FcResultMatch == FcPatternGetCharSet(pat, FC_CHARSET, 0, &value)) { return [[[FontconfigCharacterSet alloc] initWithFontconfigCharSet: value] autorelease]; } return nil; } #define READ_FROM_PATTERN(key, readMethod) \ do { \ id result = [self readMethod _pat]; \ if (result != nil) \ { \ [_attributes setObject: result \ forKey: key]; \ } \ } while (0); - (void)parseAttributes { READ_FROM_PATTERN(NSFontNameAttribute, readNameFromPattern:); READ_FROM_PATTERN(NSFontVisibleNameAttribute, readVisibleNameFromPattern:); READ_FROM_PATTERN(NSFontFamilyAttribute, readFamilyNameFromPattern:); READ_FROM_PATTERN(NSFontFaceAttribute, readStyleNameFromPattern:); READ_FROM_PATTERN(NSFontTraitsAttribute, readTraitsFromPattern:); READ_FROM_PATTERN(NSFontSizeAttribute, readSizeFromPattern:); READ_FROM_PATTERN(NSFontCharacterSetAttribute, readCharacterSetFromPattern:); } - (NSDictionary*)attributesFromPattern: (FcPattern *)pat { _attributes = [NSMutableDictionary dictionary]; _pat = pat; [self parseAttributes]; return _attributes; } @end @implementation FontconfigCharacterSet - (id)initWithFontconfigCharSet: (FcCharSet*)charset { if ((self = [super init])) { _charset = FcCharSetCopy(charset); } return self; } - (id)mutableCopyWithZone: (NSZone*)aZone { return [[NSMutableCharacterSet characterSetWithBitmapRepresentation: [self bitmapRepresentation]] retain]; } - (void)dealloc { FcCharSetDestroy(_charset); [super dealloc]; } - (FcCharSet*)fontconfigCharSet { return _charset; } - (BOOL)characterIsMember: (unichar)c { return FcCharSetHasChar(_charset, c); } - (BOOL)longCharacterIsMember: (UTF32Char)c { return FcCharSetHasChar(_charset, c); } // FIXME: Implement for better performance //- (NSData *)bitmapRepresentation //{ //} @end