API  1.0.0
CPFont.j
Go to the documentation of this file.
1 /*
2  * CPFont.j
3  * AppKit
4  *
5  * Created by Francisco Tolmasky.
6  * Copyright 2008, 280 North, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 
24 
25 CPFontDefaultSystemFontFace = @"Arial, sans-serif";
27 
33 
34 // For internal use only by this class and subclasses
35 _CPFontSystemFacePlaceholder = "_CPFontSystemFacePlaceholder";
36 
37 var _CPFontCache = {},
38  _CPSystemFontCache = {},
39  _CPFontSystemFontFace = CPFontDefaultSystemFontFace,
40  _CPFontSystemFontSize = 12,
41  _CPFontFallbackFaces = CPFontDefaultSystemFontFace.split(", "),
42  _CPFontStripRegExp = new RegExp("(^\\s*[\"']?|[\"']?\\s*$)", "g");
43 
44 
45 #define _CPRealFontSize(aSize) (aSize <= 0 ? _CPFontSystemFontSize : aSize)
46 #define _CPFontNormalizedNames(aName) _CPFontNormalizedNameArray(aName).join(", ")
47 #define _CPCachedFont(aName, aSize, isBold, isItalic) _CPFontCache[_CPFontCreateCSSString(_CPFontNormalizedNames(aName), aSize, isBold, isItalic)]
48 #define _CPUserFont(aName, aSize, isBold, isItalic) _CPCachedFont(aName, aSize, isBold, isItalic) || [[CPFont alloc] _initWithName:aName size:aSize bold:isBold italic:isItalic system:NO]
49 
50 #define _CPSystemFontCacheKey(aSize, isBold) (String(aSize) + (isBold ? "b" : ""))
51 #define _CPCachedSystemFont(aSize, isBold) _CPSystemFontCache[_CPSystemFontCacheKey(aSize, isBold)]
52 #define _CPSystemFont(aSize, isBold) (_CPCachedSystemFont(aSize, isBold) || [[CPFont alloc] _initWithName:_CPFontSystemFacePlaceholder size:aSize bold:isBold italic:NO system:YES])
53 
107 @implementation CPFont : CPObject
108 {
109  CPString _name;
110  float _size;
111  float _ascender;
112  float _descender;
113  float _lineHeight;
114  BOOL _isBold;
115  BOOL _isItalic;
116  BOOL _isSystem;
117 
118  CPString _cssString;
119 }
120 
121 + (void)initialize
122 {
123  if (self !== [CPFont class])
124  return;
125 
126  var systemFontFace = [[CPBundle mainBundle] objectForInfoDictionaryKey:@"CPSystemFontFace"];
127 
128  if (!systemFontFace)
129  systemFontFace = [[CPBundle bundleForClass:[CPView class]] objectForInfoDictionaryKey:@"CPSystemFontFace"];
130 
131  if (systemFontFace)
132  _CPFontSystemFontFace = _CPFontNormalizedNames(systemFontFace);
133 
134  var systemFontSize = [[CPBundle mainBundle] objectForInfoDictionaryKey:@"CPSystemFontSize"];
135 
136  if (!systemFontSize)
137  systemFontSize = [[CPBundle bundleForClass:[CPView class]] objectForInfoDictionaryKey:@"CPSystemFontSize"];
138 
139  if (systemFontSize)
140  _CPFontSystemFontSize = systemFontSize;
141 }
142 
146 + (CPString)systemFontFace
147 {
148  return _CPFontSystemFontFace;
149 }
150 
154 + (CPString)setSystemFontFace:(CPString)aFace
155 {
156  var normalizedFaces = _CPFontNormalizedNames(aFace);
157 
158  if (normalizedFaces === _CPFontSystemFontFace)
159  return;
160 
161  [self _invalidateSystemFontCache];
162  _CPFontSystemFontFace = aFace;
163 }
164 
168 + (float)systemFontSize
169 {
170  return _CPFontSystemFontSize;
171 }
172 
173 + (CPFont)systemFontForControlSize:(CPControlSize)aSize
174 {
175  // TODO These sizes should be themable or made less arbitrary in some other way.
176  switch (aSize)
177  {
178  case CPSmallControlSize:
179  return [self systemFontOfSize:_CPFontSystemFontSize - 1];
180 
181  case CPMiniControlSize:
182  return [self systemFontOfSize:_CPFontSystemFontSize - 2];
183 
185  default:
186  return [self systemFontOfSize:_CPFontSystemFontSize];
187  }
188 }
189 
193 + (float)setSystemFontSize:(float)size
194 {
195  if (size > 0 && size !== _CPFontSystemFontSize)
196  {
197  [self _invalidateSystemFontCache];
198  _CPFontSystemFontSize = size;
199  }
200 }
201 
202 + (void)_invalidateSystemFontCache
203 {
204  var systemSize = String(_CPFontSystemFontSize),
205  currentSize = String(CPFontCurrentSystemSize);
206 
207  for (var key in _CPSystemFontCache)
208  {
209  if (_CPSystemFontCache.hasOwnProperty(key) &&
210  (key.indexOf(systemSize) === 0 || key.indexOf(currentSize) === 0))
211  {
212  delete _CPSystemFontCache[key];
213  }
214  }
215 }
216 
224 + (CPFont)fontWithName:(CPString)aName size:(float)aSize
225 {
226  return _CPUserFont(aName, aSize <= 0 ? _CPFontSystemFontSize : aSize, NO, NO);
227 }
228 
237 + (CPFont)fontWithName:(CPString)aName size:(float)aSize italic:(BOOL)italic
238 {
239  return _CPUserFont(aName, aSize <= 0 ? _CPFontSystemFontSize : aSize, NO, italic);
240 }
241 
249 + (CPFont)boldFontWithName:(CPString)aName size:(float)aSize
250 {
251  return _CPUserFont(aName, aSize <= 0 ? _CPFontSystemFontSize : aSize, YES, NO);
252 }
253 
262 + (CPFont)boldFontWithName:(CPString)aName size:(float)aSize italic:(BOOL)italic
263 {
264  return _CPUserFont(aName, aSize <= 0 ? _CPFontSystemFontSize : aSize, YES, italic);
265 }
266 
270 + (CPFont)_fontWithName:(CPString)aName size:(float)aSize bold:(BOOL)bold italic:(BOOL)italic
271 {
272  return _CPUserFont(aName, aSize <= 0 ? _CPFontSystemFontSize : aSize, bold, italic);
273 }
274 
282 + (CPFont)systemFontOfSize:(CGSize)aSize
283 {
284  return _CPSystemFont(aSize === 0 ? _CPFontSystemFontSize : aSize, NO);
285 }
286 
294 + (CPFont)boldSystemFontOfSize:(CGSize)aSize
295 {
296  return _CPSystemFont(aSize === 0 ? _CPFontSystemFontSize : aSize, YES);
297 }
298 
299 - (id)_initWithName:(CPString)aName size:(float)aSize bold:(BOOL)isBold italic:(BOOL)isItalic system:(BOOL)isSystem
300 {
301  self = [super init];
302 
303  if (self)
304  {
305  _size = aSize;
306  _ascender = 0;
307  _descender = 0;
308  _lineHeight = 0;
309  _isBold = isBold;
310  _isItalic = isItalic;
311  _isSystem = isSystem;
312 
313  if (isSystem)
314  {
315  _name = aName;
316  _cssString = _CPFontCreateCSSString(_CPFontSystemFontFace, _size, _isBold, _isItalic);
317  _CPSystemFontCache[_CPSystemFontCacheKey(_size, _isBold)] = self;
318  }
319  else
320  {
321  _name = _CPFontNormalizedNames(aName);
322  _cssString = _CPFontCreateCSSString(_name, _size, _isBold, _isItalic);
323  _CPFontCache[_cssString] = self;
324  }
325  }
326 
327  return self;
328 }
329 
333 - (float)ascender
334 {
335  var font = _isSystem ? _CPSystemFont(_size, _isBold) : self;
336 
337  if (!font._ascender)
338  [font _getMetrics];
339 
340  return font._ascender;
341 }
342 
347 - (float)descender
348 {
349  var font = _isSystem ? _CPSystemFont(_size, _isBold) : self;
350 
351  if (!font._descender)
352  [font _getMetrics];
353 
354  return font._descender;
355 }
356 
362 - (float)defaultLineHeightForFont
363 {
364  var font = _isSystem ? _CPSystemFont(_size, _isBold) : self;
365 
366  if (!font._lineHeight)
367  [font _getMetrics];
368 
369  return font._lineHeight;
370 }
371 
375 - (float)size
376 {
377  return _CPRealFontSize(_size);
378 }
379 
383 - (CPString)cssString
384 {
385  var font = _isSystem ? _CPSystemFont(_size, _isBold) : self;
386 
387  return font._cssString;
388 }
389 
393 - (CPString)familyName
394 {
395  if (_isSystem)
396  return _CPFontSystemFontFace;
397 
398  return _name;
399 }
400 
401 - (BOOL)isSystemSize
402 {
403  return _size <= 0;
404 }
405 
406 - (BOOL)isEqual:(id)anObject
407 {
408  return [anObject isKindOfClass:[CPFont class]] && [anObject cssString] === _cssString;
409 }
410 
412 {
413  return [CPString stringWithFormat:@"%@ %@", [super description], [self cssString]];
414 }
415 
416 - (id)copy
417 {
418  return [[CPFont alloc] _initWithName:_name size:_size bold:_isBold italic:_isItalic system:_isSystem];
419 }
420 
421 - (void)_getMetrics
422 {
423  var metrics = [CPString metricsOfFont:self];
424 
425  _ascender = [metrics objectForKey:@"ascender"];
426  _descender = [metrics objectForKey:@"descender"];
427  _lineHeight = [metrics objectForKey:@"lineHeight"];
428 }
429 
430 @end
431 
433 
434 - (id)_initWithFontDescriptor:(CPFontDescriptor)fontDescriptor
435 {
436  var aName = [fontDescriptor objectForKey: CPFontNameAttribute] ,
437  aSize = [fontDescriptor pointSize],
438  isBold = [fontDescriptor symbolicTraits] & CPFontBoldTrait,
439  isItalic = [fontDescriptor symbolicTraits] & CPFontItalicTrait;
440 
441  return [self _initWithName:aName size:aSize bold:isBold italic:isItalic system:NO];
442 }
443 
444 + (CPFont)fontWithDescriptor:(CPFontDescriptor)fontDescriptor size:(float)aSize
445 {
446  var aName = [fontDescriptor objectForKey: CPFontNameAttribute],
447  isBold = [fontDescriptor symbolicTraits] & CPFontBoldTrait,
448  isItalic = [fontDescriptor symbolicTraits] & CPFontItalicTrait;
449 
450  return [self _fontWithName:aName size:aSize || [fontDescriptor pointSize] bold:isBold italic:isItalic];
451 }
452 
453 - (CPFontDescriptor)fontDescriptor
454 {
455  var traits = 0;
456 
457  if ([self isBold])
458  traits |= CPFontBoldTrait;
459 
460  if ([self isItalic])
461  traits |= CPFontItalicTrait;
462 
464 }
465 
466 @end
467 
468 
469 var CPFontNameKey = @"CPFontNameKey",
470  CPFontSizeKey = @"CPFontSizeKey",
471  CPFontIsBoldKey = @"CPFontIsBoldKey",
472  CPFontIsItalicKey = @"CPFontIsItalicKey",
473  CPFontIsSystemKey = @"CPFontIsSystemKey";
474 
475 @implementation CPFont (CPCoding)
476 
482 - (id)initWithCoder:(CPCoder)aCoder
483 {
484  var fontName = [aCoder decodeObjectForKey:CPFontNameKey],
485  size = [aCoder decodeFloatForKey:CPFontSizeKey],
486  isBold = [aCoder decodeBoolForKey:CPFontIsBoldKey],
487  isItalic = [aCoder decodeBoolForKey:CPFontIsItalicKey],
488  isSystem = [aCoder decodeBoolForKey:CPFontIsSystemKey];
489 
490  return [self _initWithName:fontName size:size bold:isBold italic:isItalic system:isSystem];
491 }
492 
497 - (void)encodeWithCoder:(CPCoder)aCoder
498 {
499  [aCoder encodeObject:_name forKey:CPFontNameKey];
500  [aCoder encodeFloat:_size forKey:CPFontSizeKey];
501  [aCoder encodeBool:_isBold forKey:CPFontIsBoldKey];
502  [aCoder encodeBool:_isItalic forKey:CPFontIsItalicKey];
503  [aCoder encodeBool:_isSystem forKey:CPFontIsSystemKey];
504 }
505 
506 @end
507 
508 
509 // aName must be normalized
510 var _CPFontCreateCSSString = function(aName, aSize, isBold, isItalic)
511 {
512  var properties = (isItalic ? "italic " : "") + (isBold ? "bold " : "") + _CPRealFontSize(aSize) + "px ";
513 
514  return properties + _CPFontConcatNameWithFallback(aName);
515 };
516 
517 var _CPFontConcatNameWithFallback = function(aName)
518 {
519  var names = _CPFontNormalizedNameArray(aName),
520  fallbackFaces = _CPFontFallbackFaces.slice(0);
521 
522  // Remove the fallback names used in the names passed in
523  for (var i = 0; i < names.length; ++i)
524  {
525  for (var j = 0; j < fallbackFaces.length; ++j)
526  {
527  if (names[i].toLowerCase() === fallbackFaces[j].toLowerCase())
528  {
529  fallbackFaces.splice(j, 1);
530  break;
531  }
532  }
533 
534  if (names[i].indexOf(" ") > 0)
535  names[i] = '"' + names[i] + '"';
536  }
537 
538  return names.concat(fallbackFaces).join(", ");
539 };
540 
541 var _CPFontNormalizedNameArray = function(aName)
542 {
543  var names = aName.split(",");
544 
545  for (var i = 0; i < names.length; ++i)
546  names[i] = names[i].replace(_CPFontStripRegExp, "");
547 
548  return names;
549 };
550 
552 
556 - (BOOL)isBold
557 {
558  return _isBold;
559 }
560 
564 - (BOOL)isItalic
565 {
566  return _isItalic;
567 }
568 
572 - (BOOL)isSystem
573 {
574  return _isSystem;
575 }
576 
577 @end
CPDictionary metricsOfFont:(CPFont aFont)
var CPFontSizeKey
Definition: CPFont.j:470
var CPFontIsSystemKey
Definition: CPFont.j:473
Definition: CPFont.h:2
var isEqual
CPFont systemFontOfSize:(CGSize aSize)
Definition: CPFont.j:282
CPMiniControlSize
Definition: CPControl.j:42
CPFontDefaultSystemFontSize
Definition: CPFont.j:26
CPSmallControlSize
Definition: CPControl.j:41
id objectForKey:(id aKey)
CPString description()
Definition: CPObject.j:358
CPFontDescriptor fontDescriptorWithName:size:(CPString fontName, [size] float size)
An immutable string (collection of characters).
Definition: CPString.h:2
CPBundle mainBundle()
Definition: CPBundle.j:82
CPFontDescriptor fontDescriptorWithSymbolicTraits:(CPFontSymbolicTraits symbolicTraits)
var CPFontNameKey
Definition: CPFont.j:469
CPFontSymbolicTraits symbolicTraits()
CPFontBoldTrait
CPFontCurrentSystemSize
Definition: CPFont.j:32
var CPFontIsBoldKey
Definition: CPFont.j:471
id objectForInfoDictionaryKey:(CPString aKey)
Definition: CPBundle.j:183
Defines methods for use when archiving & restoring (enc/decoding).
Definition: CPCoder.h:2
CPFontItalicTrait
CPString cssString()
Definition: CPFont.j:383
CPControlSize CPRegularControlSize
Definition: CPControl.j:40
CPFontDefaultSystemFontFace
Definition: CPFont.j:25
CPBundle bundleForClass:(Class aClass)
Definition: CPBundle.j:77
Class class()
Definition: CPObject.j:179
var CPFontIsItalicKey
Definition: CPFont.j:472
id alloc()
Definition: CPObject.j:130
Definition: CPView.j:137
id stringWithFormat:(CPString format, [,] ...)
Definition: CPString.j:166
FrameUpdater prototype description