~ubuntu-branches/ubuntu/utopic/gnustep-back/utopic

« back to all changes in this revision

Viewing changes to Source/fontconfig/FCFontEnumerator.m

  • Committer: Package Import Robot
  • Author(s): Yavor Doganov
  • Date: 2014-07-07 16:00:20 UTC
  • mfrom: (4.1.7 sid)
  • Revision ID: package-import@ubuntu.com-20140707160020-0ukvke5ml6ibbezv
Tags: 0.24.0-2
* Upload to unstable; should finally fix the piuparts issue once
  mknfonts.tool is binNMUed (Closes: #663388).
* debian/compat: Bump to 9 to get .build-id debugging symbols.
* debian/control.m4 (Build-Depends): Require debhlper >= 9.
  (Description): Mention that the art backend is deprecated.
  (Suggests): Remove fonts-freefont-ttf as -common depends on it.
* debian/control: Regenerate.
* debian/rules (confflags): Define conditionally and pass --host to
  configure only if cross-compiling.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
   FCFontEnumerator.m
 
3
 
 
4
   Copyright (C) 2003 Free Software Foundation, Inc.
 
5
 
 
6
   August 31, 2003
 
7
   Written by Banlu Kemiyatorn <object at gmail dot com>
 
8
   Base on original code of Alex Malmberg
 
9
   Rewrite: Fred Kiefer <fredkiefer@gmx.de>
 
10
   Date: Jan 2006
 
11
 
 
12
   This file is part of GNUstep.
 
13
 
 
14
   This library is free software; you can redistribute it and/or
 
15
   modify it under the terms of the GNU Lesser General Public
 
16
   License as published by the Free Software Foundation; either
 
17
   version 2 of the License, or (at your option) any later version.
 
18
 
 
19
   This library is distributed in the hope that it will be useful,
 
20
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
21
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
22
   Lesser General Public License for more details.
 
23
 
 
24
   You should have received a copy of the GNU Lesser General Public
 
25
   License along with this library; see the file COPYING.LIB.
 
26
   If not, see <http://www.gnu.org/licenses/> or write to the 
 
27
   Free Software Foundation, 51 Franklin Street, Fifth Floor, 
 
28
   Boston, MA 02110-1301, USA.
 
29
*/
 
30
 
 
31
#include <Foundation/NSObject.h>
 
32
#include <Foundation/NSArray.h>
 
33
#include <Foundation/NSSet.h>
 
34
#include <Foundation/NSDictionary.h>
 
35
#include <Foundation/NSValue.h>
 
36
#include <Foundation/NSPathUtilities.h>
 
37
#include <Foundation/NSFileManager.h>
 
38
#include <Foundation/NSUserDefaults.h>
 
39
#include <Foundation/NSBundle.h>
 
40
#include <Foundation/NSDebug.h>
 
41
#include <GNUstepGUI/GSFontInfo.h>
 
42
#include <AppKit/NSAffineTransform.h>
 
43
#include <AppKit/NSBezierPath.h>
 
44
#include <AppKit/NSFontDescriptor.h>
 
45
 
 
46
#include "gsc/GSGState.h"
 
47
#include "fontconfig/FCFontEnumerator.h"
 
48
#include "fontconfig/FCFontInfo.h"
 
49
 
 
50
// Old versions of fontconfig don't have FC_WEIGHT_ULTRABLACK defined.
 
51
// Use the maximal value instead.
 
52
#ifndef FC_WEIGHT_ULTRABLACK
 
53
#define FC_WEIGHT_ULTRABLACK FC_WEIGHT_BLACK
 
54
#endif
 
55
 
 
56
@implementation FCFontEnumerator 
 
57
 
 
58
NSMutableDictionary * __allFonts;
 
59
 
 
60
+ (FCFaceInfo *) fontWithName: (NSString *) name
 
61
{
 
62
  FCFaceInfo *face;
 
63
 
 
64
  face = [__allFonts objectForKey: name];
 
65
  if (!face)
 
66
    {
 
67
      NSDebugLLog(@"NSFont", @"Font not found %@", name);
 
68
    }
 
69
  return face;
 
70
}
 
71
 
 
72
+ (Class) faceInfoClass
 
73
{
 
74
  [self subclassResponsibility: _cmd];
 
75
  return nil;
 
76
}
 
77
 
 
78
// Make a GNUstep style font descriptor from a FcPattern
 
79
static NSArray *faFromFc(FcPattern *pat)
 
80
{
 
81
  int weight, slant, spacing, nsweight;
 
82
  unsigned int nstraits = 0;
 
83
  char *family;
 
84
  NSMutableString *name, *style;
 
85
 
 
86
  if (FcPatternGetInteger(pat, FC_WEIGHT, 0, &weight) != FcResultMatch
 
87
    || FcPatternGetInteger(pat, FC_SLANT,  0, &slant) != FcResultMatch
 
88
    || FcPatternGetString(pat, FC_FAMILY, 0, (FcChar8 **)&family)
 
89
      != FcResultMatch)
 
90
    return nil;
 
91
 
 
92
  if (FcPatternGetInteger(pat, FC_SPACING, 0, &spacing) == FcResultMatch)
 
93
    if (spacing==FC_MONO || spacing==FC_CHARCELL)
 
94
      nstraits |= NSFixedPitchFontMask;
 
95
  
 
96
  name = [NSMutableString stringWithCapacity: 100];
 
97
  style = [NSMutableString stringWithCapacity: 100];
 
98
  [name appendString: [NSString stringWithUTF8String: family]];
 
99
 
 
100
  switch (weight) 
 
101
    {
 
102
      case FC_WEIGHT_LIGHT:
 
103
        [style appendString: @"Light"];
 
104
        nsweight = 3;
 
105
        break;
 
106
      case FC_WEIGHT_MEDIUM:
 
107
        nsweight = 6;
 
108
        break;
 
109
      case FC_WEIGHT_DEMIBOLD:
 
110
        [style appendString: @"Demibold"];
 
111
        nsweight = 7;
 
112
        break;
 
113
      case FC_WEIGHT_BOLD:
 
114
        [style appendString: @"Bold"];
 
115
        nsweight = 9;
 
116
        nstraits |= NSBoldFontMask;
 
117
        break;
 
118
      case FC_WEIGHT_BLACK:
 
119
        [style appendString: @"Black"];
 
120
        nsweight = 12;
 
121
        nstraits |= NSBoldFontMask;
 
122
        break;
 
123
      default:
 
124
        nsweight = 6;
 
125
    }
 
126
 
 
127
  switch (slant) 
 
128
    {
 
129
      case FC_SLANT_ROMAN:
 
130
        break;
 
131
      case FC_SLANT_ITALIC:
 
132
        [style appendString: @"Italic"];
 
133
        nstraits |= NSItalicFontMask;
 
134
        break;
 
135
      case FC_SLANT_OBLIQUE:
 
136
        [style appendString: @"Oblique"];
 
137
        nstraits |= NSItalicFontMask;
 
138
        break;
 
139
    }
 
140
 
 
141
  if ([style length] > 0)
 
142
    {
 
143
      [name appendString: @"-"];
 
144
      [name appendString: style];
 
145
    }
 
146
  else
 
147
    {
 
148
      [style appendString: @"Roman"];
 
149
    }
 
150
 
 
151
  return [NSArray arrayWithObjects: name, 
 
152
                  style, 
 
153
                  [NSNumber numberWithInt: nsweight], 
 
154
                  [NSNumber numberWithUnsignedInt: nstraits],
 
155
                  nil];
 
156
}
 
157
 
 
158
- (void) enumerateFontsAndFamilies
 
159
{
 
160
  int i;
 
161
  NSMutableDictionary *fcxft_allFontFamilies = [NSMutableDictionary new];
 
162
  NSMutableDictionary *fcxft_allFonts = [NSMutableDictionary new];
 
163
  NSMutableArray *fcxft_allFontNames = [NSMutableArray new];
 
164
  Class faceInfoClass = [[self class] faceInfoClass];
 
165
 
 
166
  FcPattern *pat = FcPatternCreate();
 
167
  FcObjectSet *os = FcObjectSetBuild(FC_FAMILY, FC_SLANT, FC_WEIGHT, 
 
168
                                     FC_SPACING, NULL);
 
169
  FcFontSet *fs = FcFontList(NULL, pat, os);
 
170
 
 
171
  FcPatternDestroy(pat);
 
172
  FcObjectSetDestroy(os);
 
173
 
 
174
  for (i = 0; i < fs->nfont; i++)
 
175
    {
 
176
      char *family;
 
177
 
 
178
      if (FcPatternGetString(fs->fonts[i], FC_FAMILY, 0, (FcChar8 **)&family)
 
179
          == FcResultMatch)
 
180
        {
 
181
          NSArray *fontArray;
 
182
 
 
183
          if ((fontArray = faFromFc(fs->fonts[i])))
 
184
            {
 
185
              NSString *familyString;
 
186
              NSMutableArray *familyArray;
 
187
              FCFaceInfo *aFont;
 
188
              NSString *name = [fontArray objectAtIndex: 0];
 
189
 
 
190
              familyString = [NSString stringWithUTF8String: family];
 
191
              familyArray = [fcxft_allFontFamilies objectForKey: familyString];
 
192
              if (familyArray == nil)
 
193
                {
 
194
                  NSDebugLLog(@"NSFont", @"Found font family %@", familyString);
 
195
                  familyArray = [[NSMutableArray alloc] init];
 
196
                  [fcxft_allFontFamilies setObject: familyArray
 
197
                                         forKey: familyString];
 
198
                  RELEASE(familyArray);
 
199
                }
 
200
              NSDebugLLog(@"NSFont", @"fc enumerator: adding font: %@", name);
 
201
              [familyArray addObject: fontArray];
 
202
              [fcxft_allFontNames addObject: name];      
 
203
              aFont = [[faceInfoClass alloc] initWithfamilyName: familyString
 
204
                                             weight: [[fontArray objectAtIndex: 2] intValue]
 
205
                                             traits: [[fontArray objectAtIndex: 3] unsignedIntValue]
 
206
                                             pattern: fs->fonts[i]];
 
207
              [fcxft_allFonts setObject: aFont forKey: name];
 
208
              RELEASE(aFont);
 
209
            }
 
210
        }
 
211
    }
 
212
  FcFontSetDestroy (fs); 
 
213
 
 
214
  allFontNames = fcxft_allFontNames;
 
215
  allFontFamilies = fcxft_allFontFamilies;
 
216
  __allFonts = fcxft_allFonts;
 
217
}
 
218
 
 
219
- (NSString *) defaultSystemFontName
 
220
{
 
221
  if ([allFontNames containsObject: @"Bitstream Vera Sans"])
 
222
    {
 
223
      return @"Bitstream Vera Sans";
 
224
    }
 
225
  if ([allFontNames containsObject: @"FreeSans"])
 
226
    {
 
227
      return @"FreeSans";
 
228
    }
 
229
  if ([allFontNames containsObject: @"DejaVu Sans"])
 
230
    {
 
231
      return @"DejaVu Sans";
 
232
    }
 
233
  if ([allFontNames containsObject: @"Tahoma"])
 
234
    {
 
235
      return @"Tahoma";
 
236
    }
 
237
  if ([allFontNames containsObject: @"Arial"])
 
238
    {
 
239
      return @"Arial";
 
240
    }
 
241
  return @"Helvetica";
 
242
}
 
243
 
 
244
- (NSString *) defaultBoldSystemFontName
 
245
{
 
246
  if ([allFontNames containsObject: @"Bitstream Vera Sans-Bold"])
 
247
    {
 
248
      return @"Bitstream Vera Sans-Bold";
 
249
    }
 
250
  if ([allFontNames containsObject: @"FreeSans-Bold"])
 
251
    {
 
252
      return @"FreeSans-Bold";
 
253
    }
 
254
  if ([allFontNames containsObject: @"DejaVu Sans-Bold"])
 
255
    {
 
256
      return @"DejaVu Sans-Bold";
 
257
    }
 
258
  if ([allFontNames containsObject: @"Tahoma-Bold"])
 
259
    {
 
260
      return @"Tahoma-Bold";
 
261
    }
 
262
  if ([allFontNames containsObject: @"Arial-Bold"])
 
263
    {
 
264
      return @"Arial-Bold";
 
265
    }
 
266
  return @"Helvetica-Bold";
 
267
}
 
268
 
 
269
- (NSString *) defaultFixedPitchFontName
 
270
{
 
271
  if ([allFontNames containsObject: @"Bitstream Vera Sans Mono"])
 
272
    {
 
273
      return @"Bitstream Vera Sans Mono";
 
274
    }
 
275
  if ([allFontNames containsObject: @"FreeMono"])
 
276
    {
 
277
      return @"FreeMono";
 
278
    }
 
279
  if ([allFontNames containsObject: @"DejaVu Sans Mono"])
 
280
    {
 
281
      return @"DejaVu Sans Mono";
 
282
    }
 
283
  if ([allFontNames containsObject: @"Courier New"])
 
284
    {
 
285
      return @"Courier New";
 
286
    }
 
287
  return @"Courier";
 
288
}
 
289
 
 
290
/**
 
291
 * Overrides the implementation in GSFontInfo, and delegates the
 
292
 * matching to Fontconfig.
 
293
 */
 
294
- (NSArray *) matchingFontDescriptorsFor: (NSDictionary *)attributes
 
295
{
 
296
  NSMutableArray *descriptors;
 
297
  FcResult result;
 
298
  FcPattern *matchedpat, *pat;
 
299
  FontconfigPatternGenerator *generator;
 
300
 
 
301
  descriptors = [NSMutableArray array];
 
302
 
 
303
  generator = [[FontconfigPatternGenerator alloc] init];
 
304
  pat = [generator createPatternWithAttributes: attributes];
 
305
  DESTROY(generator);
 
306
 
 
307
  FcConfigSubstitute(NULL, pat, FcMatchPattern);
 
308
  FcDefaultSubstitute(pat);
 
309
  result = FcResultMatch;
 
310
  matchedpat = FcFontMatch(NULL, pat, &result);
 
311
  if (result != FcResultMatch)
 
312
    {
 
313
      NSLog(@"Warning, FcFontMatch failed with code: %d", result);
 
314
    }
 
315
  else
 
316
    {
 
317
      FcFontSet *fontSet;
 
318
      result = FcResultMatch;
 
319
      fontSet = FcFontSort(NULL, matchedpat, FcFalse, NULL, &result);
 
320
      if (result == FcResultMatch)
 
321
        {
 
322
          int i;
 
323
          for (i=0; i<fontSet->nfont; i++)
 
324
            {
 
325
              FontconfigPatternParser *parser = [[FontconfigPatternParser alloc] init];
 
326
              // FIXME: do we need to match this pattern?
 
327
              FcPattern *matchingpat = fontSet->fonts[i];
 
328
              NSDictionary *attribs = [parser attributesFromPattern: matchingpat];
 
329
              [parser release];
 
330
 
 
331
              [descriptors addObject: [NSFontDescriptor fontDescriptorWithFontAttributes: attribs]];
 
332
            }
 
333
        }
 
334
      else
 
335
        {
 
336
          NSLog(@"ERROR! FcFontSort failed");
 
337
        }
 
338
 
 
339
      FcFontSetDestroy(fontSet);
 
340
      FcPatternDestroy(matchedpat);
 
341
    }
 
342
  
 
343
  FcPatternDestroy(pat);
 
344
  return descriptors;
 
345
}
 
346
 
 
347
@end
 
348
 
 
349
 
 
350
 
 
351
@implementation FontconfigPatternGenerator
 
352
 
 
353
- (void)addName: (NSString*)name
 
354
{
 
355
  // FIXME: Fontconfig ignores PostScript names of fonts; we need
 
356
  // https://bugs.freedesktop.org/show_bug.cgi?id=18095 fixed.
 
357
  
 
358
  // This is a heuristic to try to 'parse' a PostScript font name,
 
359
  // however, since they are just unique identifiers for fonts and
 
360
  // don't need to follow any naming convention, this may fail
 
361
 
 
362
  NSRange dash = [name rangeOfString: @"-"];
 
363
  if (dash.location == NSNotFound)
 
364
    {
 
365
      FcPatternAddString(_pat, FC_FAMILY, (const FcChar8 *)[name UTF8String]);
 
366
    }
 
367
  else
 
368
    {
 
369
      NSString *weightAndSlant = [name substringFromIndex: dash.location + 1];
 
370
      NSString *family = [name substringToIndex: dash.location];
 
371
 
 
372
      FcPatternAddString(_pat, FC_FAMILY, (const FcChar8 *)[family UTF8String]);
 
373
 
 
374
      if (NSNotFound != [weightAndSlant rangeOfString: @"Light"].location)
 
375
        {
 
376
          FcPatternAddInteger(_pat, FC_WEIGHT, FC_WEIGHT_LIGHT);
 
377
        }
 
378
      else if (NSNotFound != [weightAndSlant rangeOfString: @"Medium"].location)
 
379
        {
 
380
          FcPatternAddInteger(_pat, FC_WEIGHT, FC_WEIGHT_MEDIUM);
 
381
        }
 
382
      else if (NSNotFound != [weightAndSlant rangeOfString: @"Demibold"].location)
 
383
        {
 
384
          FcPatternAddInteger(_pat, FC_WEIGHT, FC_WEIGHT_DEMIBOLD);
 
385
        }
 
386
      else if (NSNotFound != [weightAndSlant rangeOfString: @"Bold"].location)
 
387
        {
 
388
          FcPatternAddInteger(_pat, FC_WEIGHT, FC_WEIGHT_BOLD);
 
389
        }
 
390
      else if (NSNotFound != [weightAndSlant rangeOfString: @"Black"].location)
 
391
        {
 
392
          FcPatternAddInteger(_pat, FC_WEIGHT, FC_WEIGHT_BLACK);
 
393
        }
 
394
 
 
395
      if (NSNotFound != [weightAndSlant rangeOfString: @"Italic"].location)
 
396
        {
 
397
          FcPatternAddInteger(_pat, FC_SLANT, FC_SLANT_ITALIC);
 
398
        }
 
399
      else if (NSNotFound != [weightAndSlant rangeOfString: @"Oblique"].location)
 
400
        {
 
401
          FcPatternAddInteger(_pat, FC_SLANT, FC_SLANT_OBLIQUE);
 
402
        }
 
403
 
 
404
      if (NSNotFound != [weightAndSlant rangeOfString: @"Condensed"].location)
 
405
        {
 
406
          FcPatternAddInteger(_pat, FC_WIDTH, FC_WIDTH_CONDENSED);
 
407
        }
 
408
      else if (NSNotFound != [weightAndSlant rangeOfString: @"Expanded"].location)
 
409
        {
 
410
          FcPatternAddInteger(_pat, FC_WIDTH, FC_WIDTH_EXPANDED);
 
411
        }
 
412
    }
 
413
}
 
414
 
 
415
- (void)addVisibleName: (NSString*)name
 
416
{
 
417
  FcPatternAddString(_pat, FC_FULLNAME, (const FcChar8 *)[name UTF8String]);
 
418
}
 
419
 
 
420
- (void)addFamilyName: (NSString*)name
 
421
{
 
422
  FcPatternAddString(_pat, FC_FAMILY, (const FcChar8 *)[name UTF8String]);
 
423
}
 
424
 
 
425
- (void)addStyleName: (NSString*)style
 
426
{
 
427
  FcPatternAddString(_pat, FC_STYLE, (const FcChar8 *)[style UTF8String]);
 
428
}
 
429
 
 
430
- (void)addTraits: (NSDictionary*)traits
 
431
{
 
432
  if ([traits objectForKey: NSFontSymbolicTrait])
 
433
    {
 
434
      NSFontSymbolicTraits symTraits = [[traits objectForKey: NSFontSymbolicTrait] intValue];
 
435
 
 
436
      if (symTraits & NSFontItalicTrait)
 
437
        {
 
438
          // NOTE: May be overridden by NSFontSlantTrait
 
439
          FcPatternAddInteger(_pat, FC_SLANT, FC_SLANT_ITALIC);
 
440
        }
 
441
      if (symTraits & NSFontBoldTrait)
 
442
        {
 
443
          // NOTE: May be overridden by NSFontWeightTrait
 
444
          FcPatternAddInteger(_pat, FC_WEIGHT, FC_WEIGHT_BOLD);
 
445
        }
 
446
      if (symTraits & NSFontExpandedTrait)
 
447
        {
 
448
          // NOTE: May be overridden by NSFontWidthTrait
 
449
          FcPatternAddInteger(_pat, FC_WIDTH, FC_WIDTH_EXPANDED);
 
450
        }
 
451
      if (symTraits & NSFontCondensedTrait)
 
452
        {
 
453
          // NOTE: May be overridden by NSFontWidthTrait
 
454
          FcPatternAddInteger(_pat, FC_WIDTH, FC_WIDTH_CONDENSED);
 
455
        }
 
456
      if (symTraits & NSFontMonoSpaceTrait)
 
457
        {
 
458
          FcValue value;
 
459
          // If you run "fc-match :spacing=100", you get "DejaVu Sans" even though you would
 
460
          // expect to get "DejaVu Sans Mono". So, we also add "monospace" as a weak family
 
461
          // name to fix the problem.
 
462
          FcPatternAddInteger(_pat, FC_SPACING, FC_MONO);
 
463
          
 
464
          value.type = FcTypeString;
 
465
          value.u.s = (FcChar8*)"monospace";
 
466
          FcPatternAddWeak(_pat, FC_FAMILY, value, FcTrue);
 
467
        }
 
468
      if (symTraits & NSFontVerticalTrait)
 
469
        {
 
470
          // FIXME: What is this supposed to mean?
 
471
        }
 
472
      if (symTraits & NSFontUIOptimizedTrait)
 
473
        {
 
474
          // NOTE: Fontconfig can't express this
 
475
        }
 
476
 
 
477
      {
 
478
        NSFontFamilyClass class = symTraits & NSFontFamilyClassMask;
 
479
        char *addWeakFamilyName = NULL;
 
480
        switch (class)
 
481
          {
 
482
          default:
 
483
          case NSFontUnknownClass:
 
484
          case NSFontOrnamentalsClass:
 
485
          case NSFontScriptsClass:
 
486
          case NSFontSymbolicClass:
 
487
            // FIXME: Is there some way to convey these to Fontconfig?
 
488
            break;
 
489
          case NSFontOldStyleSerifsClass:
 
490
          case NSFontTransitionalSerifsClass:
 
491
          case NSFontModernSerifsClass:
 
492
          case NSFontClarendonSerifsClass:
 
493
          case NSFontSlabSerifsClass:
 
494
          case NSFontFreeformSerifsClass:
 
495
            addWeakFamilyName = "serif";
 
496
            break;
 
497
          case NSFontSansSerifClass:
 
498
            addWeakFamilyName = "sans";
 
499
            break;
 
500
          }
 
501
        if (addWeakFamilyName)
 
502
          {
 
503
            FcValue value;
 
504
            value.type = FcTypeString;
 
505
            value.u.s = (const FcChar8 *)addWeakFamilyName;
 
506
            FcPatternAddWeak(_pat, FC_FAMILY, value, FcTrue);
 
507
          }
 
508
      }
 
509
    }
 
510
 
 
511
  if ([traits objectForKey: NSFontWeightTrait])
 
512
    {
 
513
      /**
 
514
       * Scale: -1 is thinnest, 0 is normal, 1 is heaviest
 
515
       */
 
516
      double weight = [[traits objectForKey: NSFontWeightTrait] doubleValue];
 
517
      int fcWeight;
 
518
 
 
519
      weight = MAX(-1, MIN(1, weight));
 
520
      if (weight <= 0)
 
521
        {
 
522
          fcWeight = FC_WEIGHT_THIN + ((weight + 1.0) * (FC_WEIGHT_NORMAL - FC_WEIGHT_THIN));
 
523
        }
 
524
      else
 
525
        {
 
526
          fcWeight = FC_WEIGHT_NORMAL + (weight * (FC_WEIGHT_ULTRABLACK - FC_WEIGHT_NORMAL));
 
527
        }
 
528
      FcPatternAddInteger(_pat, FC_WEIGHT, fcWeight);
 
529
    }
 
530
 
 
531
  if ([traits objectForKey: NSFontWidthTrait])
 
532
    {
 
533
      /**
 
534
       * Scale: -1 is most condensed, 0 is normal, 1 is most spread apart
 
535
       */
 
536
      double width = [[traits objectForKey: NSFontWidthTrait] doubleValue];
 
537
      int fcWidth;
 
538
 
 
539
      width = MAX(-1, MIN(1, width));
 
540
      if (width <= 0)
 
541
        {
 
542
          fcWidth = FC_WIDTH_ULTRACONDENSED + ((width + 1.0) * (FC_WIDTH_NORMAL - FC_WIDTH_ULTRACONDENSED));
 
543
        }
 
544
      else
 
545
        {
 
546
          fcWidth = FC_WIDTH_NORMAL + (width * (FC_WIDTH_ULTRAEXPANDED - FC_WIDTH_NORMAL));
 
547
        }
 
548
      FcPatternAddInteger(_pat, FC_WIDTH, fcWidth);
 
549
    }
 
550
 
 
551
  if ([traits objectForKey: NSFontSlantTrait])
 
552
    {
 
553
      /**
 
554
       * Scale: -1 is 30 degree counterclockwise slant, 0 is no slant, 1
 
555
       * is 30 degree clockwise slant
 
556
       */
 
557
      double slant = [[traits objectForKey: NSFontSlantTrait] doubleValue];
 
558
 
 
559
      // NOTE: Fontconfig can't express this as a scale
 
560
      if (slant > 0)
 
561
        {
 
562
          FcPatternAddInteger(_pat, FC_SLANT, FC_SLANT_ITALIC);
 
563
        }
 
564
      else
 
565
        {
 
566
          FcPatternAddInteger(_pat, FC_SLANT, FC_SLANT_ROMAN);
 
567
        }
 
568
    }
 
569
}
 
570
 
 
571
- (void)addSize: (NSNumber*)size
 
572
{
 
573
  FcPatternAddDouble(_pat, FC_SIZE, [size doubleValue]);
 
574
}
 
575
 
 
576
- (void)addCharacterSet: (NSCharacterSet*)characterSet
 
577
{
 
578
  if ([characterSet isKindOfClass: [FontconfigCharacterSet class]])
 
579
    {
 
580
      // Fast case
 
581
      FcPatternAddCharSet(_pat, FC_CHARSET, [(FontconfigCharacterSet*)characterSet fontconfigCharSet]);
 
582
    }
 
583
  else
 
584
    {
 
585
      // Slow case
 
586
      FcCharSet *fcSet = FcCharSetCreate();
 
587
      uint32_t plane;
 
588
      for (plane=0; plane<=16; plane++)
 
589
        {
 
590
          if ([characterSet hasMemberInPlane: plane])
 
591
            {
 
592
              uint32_t codePoint;
 
593
              for (codePoint = plane<<16; codePoint <= 0xffff + (plane<<16); codePoint++)
 
594
                {
 
595
                  if ([characterSet longCharacterIsMember: codePoint])
 
596
                    {
 
597
                      FcCharSetAddChar(fcSet, codePoint);
 
598
                    }
 
599
                }
 
600
            }
 
601
        }
 
602
      
 
603
      FcPatternAddCharSet(_pat, FC_CHARSET, fcSet);
 
604
      FcCharSetDestroy(fcSet);
 
605
    }
 
606
}
 
607
 
 
608
#define ADD_TO_PATTERN(key, handlerMethod, valueClass)  \
 
609
  do {                                                  \
 
610
    id value = [_attributes objectForKey: key];         \
 
611
    if (value)                                          \
 
612
      {                                                 \
 
613
        if ([value isKindOfClass: valueClass])          \
 
614
          {                                                             \
 
615
            [self handlerMethod value];                                 \
 
616
          }                                                             \
 
617
        else                                                            \
 
618
          {                                                             \
 
619
            NSLog(@"NSFontDescriptor: Ignoring invalid value %@ for attribute %@", value, key); \
 
620
          }                                                             \
 
621
      }                                                                 \
 
622
  } while (0);
 
623
 
 
624
- (void)addAttributes
 
625
{
 
626
  ADD_TO_PATTERN(NSFontNameAttribute, addName:, [NSString class]);
 
627
  ADD_TO_PATTERN(NSFontVisibleNameAttribute, addVisibleName:, [NSString class]);
 
628
  ADD_TO_PATTERN(NSFontFamilyAttribute, addFamilyName:, [NSString class]);
 
629
  ADD_TO_PATTERN(NSFontFaceAttribute, addStyleName:, [NSString class]);
 
630
  ADD_TO_PATTERN(NSFontTraitsAttribute, addTraits:, [NSDictionary class]);
 
631
  ADD_TO_PATTERN(NSFontSizeAttribute, addSize:, [NSNumber class]);
 
632
  ADD_TO_PATTERN(NSFontCharacterSetAttribute, addCharacterSet:, [NSCharacterSet class]);
 
633
}
 
634
 
 
635
- (FcPattern *)createPatternWithAttributes: (NSDictionary *)attributes
 
636
{
 
637
  _attributes = attributes;
 
638
  _pat = FcPatternCreate();
 
639
  [self addAttributes];
 
640
 
 
641
  return _pat;
 
642
}
 
643
 
 
644
@end
 
645
 
 
646
 
 
647
@implementation FontconfigPatternParser
 
648
 
 
649
- (NSString*)readFontconfigString: (const char *)key fromPattern: (FcPattern*)pat
 
650
{
 
651
  unsigned char *string = NULL;
 
652
  if (FcResultMatch == FcPatternGetString(pat, key, 0, &string))
 
653
    {
 
654
      if (string)
 
655
        {
 
656
          return [NSString stringWithUTF8String: (const char *)string];
 
657
        }
 
658
    }
 
659
  return nil;
 
660
}
 
661
 
 
662
- (NSNumber*)readFontconfigInteger: (const char *)key fromPattern: (FcPattern*)pat
 
663
{
 
664
  int value;
 
665
  if (FcResultMatch == FcPatternGetInteger(pat, key, 0, &value))
 
666
    {
 
667
      return [NSNumber numberWithInt: value];
 
668
    }
 
669
  return nil;
 
670
}
 
671
 
 
672
- (NSNumber*)readFontconfigDouble: (const char *)key fromPattern: (FcPattern*)pat
 
673
{
 
674
  double value;
 
675
  if (FcResultMatch == FcPatternGetDouble(pat, key, 0, &value))
 
676
    {
 
677
      return [NSNumber numberWithDouble: value];
 
678
    }
 
679
  return nil;
 
680
}
 
681
 
 
682
 
 
683
 
 
684
- (NSString*)readNameFromPattern: (FcPattern*)pat
 
685
{
 
686
  // FIXME: Hack which generates a PostScript-style name from the
 
687
  // family name and style
 
688
 
 
689
  NSString *family = [self readFontconfigString: FC_FAMILY fromPattern: pat];
 
690
  NSString *style = [self readFontconfigString: FC_STYLE fromPattern: pat];
 
691
  if (style)
 
692
    {
 
693
      return [NSString stringWithFormat: @"%@-%@", family, style];
 
694
    }
 
695
  else
 
696
    {
 
697
      return family;
 
698
    }
 
699
}
 
700
- (NSString*)readVisibleNameFromPattern: (FcPattern*)pat
 
701
{
 
702
  // FIXME: try to get the localized one
 
703
  return [self readFontconfigString: FC_FULLNAME fromPattern: pat];
 
704
}
 
705
- (NSString*)readFamilyNameFromPattern: (FcPattern*)pat
 
706
{
 
707
  // FIXME: try to get the localized one
 
708
  return [self readFontconfigString: FC_FAMILY fromPattern: pat];
 
709
}
 
710
- (NSString*)readStyleNameFromPattern: (FcPattern*)pat
 
711
{
 
712
  // FIXME: try to get the localized one
 
713
  return [self readFontconfigString: FC_STYLE fromPattern: pat];
 
714
}
 
715
- (NSDictionary*)readTraitsFromPattern: (FcPattern*)pat
 
716
{
 
717
  NSMutableDictionary *traits = [NSMutableDictionary dictionary];
 
718
 
 
719
  NSFontSymbolicTraits symTraits = 0;
 
720
 
 
721
  int value;
 
722
  if (FcResultMatch == FcPatternGetInteger(pat, FC_SLANT, 0, &value))
 
723
    {
 
724
      if (value == FC_SLANT_ITALIC)
 
725
        {
 
726
          symTraits |= NSFontItalicTrait;
 
727
        }
 
728
    }
 
729
  if (FcResultMatch == FcPatternGetInteger(pat, FC_WEIGHT, 0, &value))
 
730
    {
 
731
      double weight;
 
732
 
 
733
      if (value >= FC_WEIGHT_BOLD)
 
734
        {
 
735
          symTraits |= NSFontBoldTrait;
 
736
        }
 
737
 
 
738
      if (value <= FC_WEIGHT_NORMAL)
 
739
        {
 
740
          weight = ((value - FC_WEIGHT_THIN) / (double)(FC_WEIGHT_NORMAL - FC_WEIGHT_THIN)) - 1.0;
 
741
        }
 
742
      else
 
743
        {
 
744
          weight = (value - FC_WEIGHT_NORMAL) / (double)(FC_WEIGHT_ULTRABLACK - FC_WEIGHT_NORMAL);
 
745
        }
 
746
 
 
747
      [traits setObject: [NSNumber numberWithDouble: weight]
 
748
                 forKey: NSFontWeightTrait];
 
749
    }
 
750
  if (FcResultMatch == FcPatternGetInteger(pat, FC_WIDTH, 0, &value))
 
751
    {
 
752
      double width;
 
753
 
 
754
      if (value >= FC_WIDTH_EXPANDED)
 
755
        {
 
756
          symTraits |= NSFontExpandedTrait;
 
757
        }
 
758
      if (value <= FC_WIDTH_CONDENSED)
 
759
        {
 
760
          symTraits |= NSFontCondensedTrait;
 
761
        }
 
762
 
 
763
      if (value <= FC_WIDTH_NORMAL)
 
764
        {
 
765
          width = ((value - FC_WIDTH_ULTRACONDENSED) / (double)(FC_WIDTH_NORMAL - FC_WIDTH_ULTRACONDENSED)) - 1.0;
 
766
        }
 
767
      else
 
768
        {
 
769
          width = (value - FC_WIDTH_NORMAL) / (double)(FC_WIDTH_ULTRAEXPANDED - FC_WIDTH_NORMAL);
 
770
        }
 
771
 
 
772
      [traits setObject: [NSNumber numberWithDouble: width]
 
773
                 forKey: NSFontWidthTrait];
 
774
    }
 
775
  if (FcResultMatch == FcPatternGetInteger(pat, FC_SPACING, 0, &value))
 
776
    {
 
777
      if (value == FC_MONO || value == FC_CHARCELL)
 
778
        {
 
779
          symTraits |= NSFontMonoSpaceTrait;
 
780
        }
 
781
    }
 
782
 
 
783
  if (symTraits != 0)
 
784
    {
 
785
      [traits setObject: [NSNumber numberWithUnsignedInt: symTraits]
 
786
                 forKey: NSFontSymbolicTrait];
 
787
    }
 
788
 
 
789
  return traits;
 
790
}
 
791
 
 
792
- (NSNumber*)readSizeFromPattern: (FcPattern*)pat
 
793
{
 
794
  return [self readFontconfigDouble: FC_SIZE fromPattern: pat];
 
795
}
 
796
 
 
797
- (NSCharacterSet*)readCharacterSetFromPattern: (FcPattern*)pat
 
798
{
 
799
  FcCharSet *value;
 
800
  if (FcResultMatch == FcPatternGetCharSet(pat, FC_CHARSET, 0, &value))
 
801
    {
 
802
      return [[[FontconfigCharacterSet alloc] initWithFontconfigCharSet: value] autorelease];
 
803
    }
 
804
  return nil;
 
805
}
 
806
 
 
807
#define READ_FROM_PATTERN(key, readMethod)      \
 
808
  do {                                          \
 
809
    id result = [self readMethod _pat];         \
 
810
    if (result != nil)                          \
 
811
      {                                         \
 
812
        [_attributes setObject: result          \
 
813
                        forKey: key];           \
 
814
      }                                         \
 
815
  } while (0);
 
816
 
 
817
- (void)parseAttributes
 
818
{
 
819
  READ_FROM_PATTERN(NSFontNameAttribute, readNameFromPattern:);
 
820
  READ_FROM_PATTERN(NSFontVisibleNameAttribute, readVisibleNameFromPattern:);
 
821
  READ_FROM_PATTERN(NSFontFamilyAttribute, readFamilyNameFromPattern:);
 
822
  READ_FROM_PATTERN(NSFontFaceAttribute, readStyleNameFromPattern:);
 
823
  READ_FROM_PATTERN(NSFontTraitsAttribute, readTraitsFromPattern:);
 
824
  READ_FROM_PATTERN(NSFontSizeAttribute, readSizeFromPattern:);
 
825
  READ_FROM_PATTERN(NSFontCharacterSetAttribute, readCharacterSetFromPattern:);
 
826
}
 
827
 
 
828
- (NSDictionary*)attributesFromPattern: (FcPattern *)pat
 
829
{
 
830
  _attributes = [NSMutableDictionary dictionary];
 
831
  _pat = pat;
 
832
  [self parseAttributes];
 
833
  return _attributes;
 
834
}
 
835
 
 
836
@end
 
837
 
 
838
 
 
839
 
 
840
@implementation FontconfigCharacterSet
 
841
 
 
842
- (id)initWithFontconfigCharSet: (FcCharSet*)charset
 
843
{
 
844
  if ((self = [super init]))
 
845
    {
 
846
      _charset = FcCharSetCopy(charset);
 
847
    }
 
848
  return self;
 
849
}
 
850
 
 
851
- (id)mutableCopyWithZone: (NSZone*)aZone
 
852
{
 
853
  return [[NSMutableCharacterSet characterSetWithBitmapRepresentation: 
 
854
                                   [self bitmapRepresentation]] retain];
 
855
}
 
856
 
 
857
- (void)dealloc
 
858
{
 
859
  FcCharSetDestroy(_charset);
 
860
  [super dealloc];
 
861
}
 
862
 
 
863
- (FcCharSet*)fontconfigCharSet
 
864
{
 
865
  return _charset;
 
866
}
 
867
 
 
868
- (BOOL)characterIsMember: (unichar)c
 
869
{
 
870
  return FcCharSetHasChar(_charset, c);
 
871
}
 
872
 
 
873
- (BOOL)longCharacterIsMember: (UTF32Char)c
 
874
{
 
875
  return FcCharSetHasChar(_charset, c);
 
876
}
 
877
 
 
878
// FIXME: Implement for better performance
 
879
//- (NSData *)bitmapRepresentation
 
880
//{
 
881
//}
 
882
 
 
883
@end
 
884