/* Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. Author: Alexander Malmberg 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 "FTFontEnumerator.h" #include "FTFaceInfo.h" #if 0 /* This is a list of "standard" face names. It is here so make_strings can pick it up and generate .strings files with them. */ NSLocalizedStringFromTable(@"Book", @"nfontFaceNames", @"") NSLocalizedStringFromTable(@"Regular", @"nfontFaceNames", @"") NSLocalizedStringFromTable(@"Roman", @"nfontFaceNames", @"") NSLocalizedStringFromTable(@"Medium", @"nfontFaceNames", @"") NSLocalizedStringFromTable(@"Demi", @"nfontFaceNames", @"") NSLocalizedStringFromTable(@"Demibold", @"nfontFaceNames", @"") NSLocalizedStringFromTable(@"Bold", @"nfontFaceNames", @"") NSLocalizedStringFromTable(@"Heavy", @"nfontFaceNames", @"") NSLocalizedStringFromTable(@"Italic", @"nfontFaceNames", @"") NSLocalizedStringFromTable(@"Oblique", @"nfontFaceNames", @"") NSLocalizedStringFromTable(@"Bold Italic", @"nfontFaceNames", @"") NSLocalizedStringFromTable(@"Bold Oblique", @"nfontFaceNames", @"") #endif static NSMutableArray *fcfg_allFontNames; static NSMutableDictionary *fcfg_allFontFamilies; static NSMutableDictionary *fcfg_all_fonts; static NSMutableSet *families_seen, *families_pending; static BOOL anti_alias_by_default; static int traits_from_string(NSString *s, unsigned int *traits, unsigned int *weight) { static struct { NSString *str; unsigned int trait; int weight; } suffix[] = { /* TODO */ {@"Normal" ,0 ,-1}, {@"Ultralight" ,0 , 1}, {@"Thin" ,0 , 2}, {@"Light" ,0 , 3}, {@"Extralight" ,0 , 3}, {@"Book" ,0 , 4}, {@"Regular" ,0 , 5}, {@"Plain" ,0 , 5}, {@"Display" ,0 , 5}, {@"Roman" ,0 , 5}, {@"Semilight" ,0 , 5}, {@"Medium" ,0 , 6}, {@"Demi" ,0 , 7}, {@"Demibold" ,0 , 7}, {@"Semi" ,0 , 8}, {@"Semibold" ,0 , 8}, {@"Bold" ,NSBoldFontMask , 9}, {@"Extra" ,NSBoldFontMask ,10}, {@"Extrabold" ,NSBoldFontMask ,10}, {@"Heavy" ,NSBoldFontMask ,11}, {@"Heavyface" ,NSBoldFontMask ,11}, {@"Ultrabold" ,NSBoldFontMask ,12}, {@"Black" ,NSBoldFontMask ,12}, {@"Ultra" ,NSBoldFontMask ,13}, {@"Ultrablack" ,NSBoldFontMask ,13}, {@"Fat" ,NSBoldFontMask ,13}, {@"Extrablack" ,NSBoldFontMask ,14}, {@"Obese" ,NSBoldFontMask ,14}, {@"Nord" ,NSBoldFontMask ,14}, {@"Italic" ,NSItalicFontMask ,-1}, {@"Oblique" ,NSItalicFontMask ,-1}, {@"Cond" ,NSCondensedFontMask ,-1}, {@"Condensed" ,NSCondensedFontMask ,-1}, {nil,0,-1} }; int i; *traits = 0; // printf("do '%@'\n", s); while ([s length] > 0) { // printf(" got '%@'\n", s); if ([s hasSuffix: @"-"] || [s hasSuffix: @" "]) { // printf(" do -\n"); s = [s substringToIndex: [s length] - 1]; continue; } for (i = 0; suffix[i].str; i++) { if (![s hasSuffix: suffix[i].str]) continue; // printf(" found '%@'\n", suffix[i].str); if (suffix[i].weight != -1) *weight = suffix[i].weight; (*traits) |= suffix[i].trait; s = [s substringToIndex: [s length] - [suffix[i].str length]]; break; } if (!suffix[i].str) break; } // printf("end up with '%@'\n", s); return [s length]; } static NSArray *fix_path(NSString *path, NSArray *files) { int i, c = [files count]; NSMutableArray *nfiles; if (!files) return nil; nfiles = [[NSMutableArray alloc] init]; for (i = 0; i < c; i++) { if ([[files objectAtIndex: i] isAbsolutePath]) [nfiles addObject: [files objectAtIndex: i]]; else [nfiles addObject: [path stringByAppendingPathComponent: [files objectAtIndex: i]]]; } return nfiles; } /* TODO: handling of .font packages needs to be reworked */ static void add_face(NSString *family, int family_weight, unsigned int family_traits, NSDictionary *d, NSString *path, BOOL from_nfont) { FTFaceInfo *fi; unsigned int weight; unsigned int traits; NSString *fontName; NSString *faceName, *rawFaceName; fontName = [d objectForKey: @"PostScriptName"]; if (!fontName) { NSLog(@"Warning: Face in %@ has no PostScriptName!",path); return; } if ([fcfg_allFontNames containsObject: fontName]) return; fi = [[FTFaceInfo alloc] init]; fi->familyName = [family copy]; if ([d objectForKey: @"LocalizedNames"]) { NSDictionary *l; NSArray *lang; int i; l = [d objectForKey: @"LocalizedNames"]; lang = [[NSUserDefaults standardUserDefaults] stringArrayForKey: @"NSLanguages"]; faceName = nil; rawFaceName = [l objectForKey: @"English"]; for (i = 0; i < [lang count] && !faceName; i++) { faceName = [l objectForKey: [lang objectAtIndex: i]]; } if (!faceName) faceName = rawFaceName; if (!faceName) { faceName = @""; NSLog(@"Warning: couldn't find localized face name or fallback for %@", fontName); } } else if ((faceName = [d objectForKey: @"Name"])) { rawFaceName = faceName; /* TODO: Smarter localization? Parse space separated parts and translate individually? */ /* TODO: Need to define the strings somewhere, and make sure the strings files get created. */ faceName = [NSLocalizedStringFromTableInBundle(faceName,@"nfontFaceNames", [NSBundle bundleForClass: [fi class]],nil) copy]; fi->faceName = faceName; } else if (!from_nfont) { /* try to guess something for .font packages */ unsigned int dummy; int split = traits_from_string(family,&dummy,&dummy); rawFaceName = faceName = [family substringFromIndex: split]; family = [family substringToIndex: split]; faceName = [NSLocalizedStringFromTableInBundle(faceName,@"nfontFaceNames", [NSBundle bundleForClass: [fi class]],nil) copy]; fi->faceName = faceName; } else { NSLog(@"Warning: Can't find name for face %@ in %@!",fontName,path); return; } fi->displayName = [[family stringByAppendingString: @" "] stringByAppendingString: faceName]; weight = family_weight; if (rawFaceName) traits_from_string(rawFaceName, &traits, &weight); { NSDictionary *sizes; NSEnumerator *e; NSString *size; int i; sizes = [d objectForKey: @"ScreenFonts"]; fi->num_sizes = [sizes count]; if (fi->num_sizes) { fi->sizes = malloc(sizeof(fi->sizes[0])*[sizes count]); e = [sizes keyEnumerator]; i = 0; while ((size = [e nextObject])) { fi->sizes[i].pixel_size = [size intValue]; fi->sizes[i].files = fix_path(path,[sizes objectForKey: size]); NSDebugLLog(@"ftfont",@"%@ size %i files |%@|", fontName,fi->sizes[i].pixel_size,fi->sizes[i].files); i++; } } } fi->files = fix_path(path,[d objectForKey: @"Files"]); if ([d objectForKey: @"Weight"]) weight = [[d objectForKey: @"Weight"] intValue]; fi->weight = weight; if ([d objectForKey: @"Traits"]) traits = [[d objectForKey: @"Traits"] intValue]; traits |= family_traits; fi->traits = traits; if ([d objectForKey: @"RenderHints_hack"]) fi->render_hints_hack = strtol([[d objectForKey: @"RenderHints_hack"] cString], NULL, 0); else { if (anti_alias_by_default) fi->render_hints_hack = 0x10202; else fi->render_hints_hack = 0x00202; } NSDebugLLog(@"ftfont", @"adding '%@' '%@'", fontName, fi); [fcfg_all_fonts setObject: fi forKey: fontName]; [fcfg_allFontNames addObject: fontName]; { NSArray *a; NSMutableArray *ma; a = [NSArray arrayWithObjects: fontName, faceName, [NSNumber numberWithInt: weight], [NSNumber numberWithUnsignedInt: traits], nil]; ma = [fcfg_allFontFamilies objectForKey: family]; if (!ma) { ma = [[NSMutableArray alloc] init]; [fcfg_allFontFamilies setObject: ma forKey: family]; [ma release]; } [ma addObject: a]; } DESTROY(fi); } static void load_font_configuration(void) { int i, j, k, c; NSArray *paths; NSString *path, *font_path; NSFileManager *fm = [NSFileManager defaultManager]; NSArray *files; NSDictionary *d; NSArray *faces; NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; if ([ud objectForKey: @"GSFontAntiAlias"]) anti_alias_by_default = [ud boolForKey: @"GSFontAntiAlias"]; else anti_alias_by_default = YES; fcfg_all_fonts = [[NSMutableDictionary alloc] init]; fcfg_allFontFamilies = [[NSMutableDictionary alloc] init]; fcfg_allFontNames = [[NSMutableArray alloc] init]; families_seen = [[NSMutableSet alloc] init]; families_pending = [[NSMutableSet alloc] init]; paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSAllDomainsMask, YES); for (i = 0; i < [paths count]; i++) { path = [paths objectAtIndex: i]; path = [path stringByAppendingPathComponent: @"Fonts"]; files = [fm directoryContentsAtPath: path]; c = [files count]; for (j = 0; j < c; j++) { NSString *family; NSDictionary *face_info; NSString *font_info_path; int weight; unsigned int traits; font_path = [files objectAtIndex: j]; if (![[font_path pathExtension] isEqual: @"nfont"]) continue; family = [font_path stringByDeletingPathExtension]; if ([families_seen member: family]) { NSDebugLLog(@"ftfont", @"'%@' already seen, skipping", family); continue; } [families_seen addObject: family]; font_path = [path stringByAppendingPathComponent: font_path]; NSDebugLLog(@"ftfont",@"loading %@",font_path); font_info_path = [font_path stringByAppendingPathComponent: @"FontInfo.plist"]; if (![fm fileExistsAtPath: font_info_path]) continue; d = [NSDictionary dictionaryWithContentsOfFile: font_info_path]; if (!d) continue; if ([d objectForKey: @"Family"]) family = [d objectForKey: @"Family"]; if ([d objectForKey: @"Weight"]) weight = [[d objectForKey: @"Weight"] intValue]; else weight = 5; if ([d objectForKey: @"Traits"]) traits = [[d objectForKey: @"Traits"] intValue]; else traits = 0; faces = [d objectForKey: @"Faces"]; if (![faces isKindOfClass: [NSArray class]]) { NSLog(@"Warning: %@ isn't a valid .nfont package, ignoring.", font_path); if ([faces isKindOfClass: [NSDictionary class]]) NSLog(@"(it looks like an old-style .nfont package)"); continue; } for (k = 0; k < [faces count]; k++) { face_info = [faces objectAtIndex: k]; add_face(family, weight, traits, face_info, font_path, YES); } } for (j = 0; j < c; j++) { NSString *family; font_path = [files objectAtIndex: j]; if (![[font_path pathExtension] isEqual: @"font"]) continue; family = [font_path stringByDeletingPathExtension]; font_path = [path stringByAppendingPathComponent: font_path]; d = [NSDictionary dictionaryWithObjectsAndKeys: [NSArray arrayWithObjects: family, [family stringByAppendingPathExtension: @"afm"], nil], @"Files", family,@"PostScriptName", nil]; add_face(family, 5, 0, d, font_path, NO); } [families_seen unionSet: families_pending]; [families_pending removeAllObjects]; } NSDebugLLog(@"ftfont", @"got %i fonts in %i families", [fcfg_allFontNames count], [fcfg_allFontFamilies count]); if (![fcfg_allFontNames count]) { NSLog(@"No fonts found!"); exit(1); } DESTROY(families_seen); DESTROY(families_pending); } @implementation FTFontEnumerator + (FTFaceInfo *) fontWithName: (NSString *)name { FTFaceInfo *face; face = [fcfg_all_fonts objectForKey: name]; if (!face) { NSLog (@"Font not found %@", name); } return face; } - (void) enumerateFontsAndFamilies { load_font_configuration(); ASSIGN(allFontNames, fcfg_allFontNames); ASSIGN(allFontFamilies, fcfg_allFontFamilies); } - (NSString *) defaultSystemFontName { if ([fcfg_allFontNames containsObject: @"BitstreamVeraSans-Roman"]) return @"BitstreamVeraSans-Roman"; if ([fcfg_allFontNames containsObject: @"FreeSans"]) return @"FreeSans"; return @"Helvetica"; } - (NSString *) defaultBoldSystemFontName { if ([fcfg_allFontNames containsObject: @"BitstreamVeraSans-Bold"]) return @"BitstreamVeraSans-Bold"; if ([fcfg_allFontNames containsObject: @"FreeSansBold"]) return @"FreeSansBold"; return @"Helvetica-Bold"; } - (NSString *) defaultFixedPitchFontName { if ([fcfg_allFontNames containsObject: @"BitstreamVeraSansMono-Roman"]) return @"BitstreamVeraSansMono-Roman"; if ([fcfg_allFontNames containsObject: @"FreeMono"]) return @"FreeMono"; return @"Courier"; } @end