API  0.9.10
CPFontManager.j
Go to the documentation of this file.
1 /*
2  * CPFontManager.j
3  * AppKit
4  *
5  * Created by Tom Robinson.
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 @global CPApp
26 
28 CPBoldFontMask = 1 << 1;
39 
40 
44 
45 /*
46  modifyFont: sender's tag
47 */
56 
60 @implementation CPFontManager : CPObject
61 {
62  CPArray _availableFonts;
63 
64  id _target;
65  SEL _action;
66 
67  id _delegate;
68 
69  CPFont _selectedFont;
70  BOOL _multiple;
71 
72  CPDictionary _activeChange;
73 
74  unsigned _fontAction;
75 }
76 
77 // Getting the Shared Font Manager
83 {
85  CPSharedFontManager = [[CPFontManagerFactory alloc] init];
86 
87  return CPSharedFontManager;
88 }
89 
90 // Changing the Default Font Conversion Classes
95 + (void)setFontManagerFactory:(Class)aClass
96 {
97  CPFontManagerFactory = aClass;
98 }
103 + (void)setFontPanelFactory:(Class)aClass
104 {
105  CPFontPanelFactory = aClass;
106 }
107 
108 
109 - (id)init
110 {
111  if (self = [super init])
112  {
113  _action = @selector(changeFont:);
114  }
115 
116  return self;
117 }
118 
122 - (CPArray)availableFonts
123 {
124  if (!_availableFonts)
125  {
126  _availableFonts = [];
127 
128 #if PLATFORM(DOM)
129  _CPFontDetectSpan = document.createElement("span");
130  _CPFontDetectSpan.fontSize = "24px";
131  _CPFontDetectSpan.appendChild(document.createTextNode("mmmmmmmmmml"));
132  var div = document.createElement("div");
133  div.style.position = "absolute";
134  div.style.top = "-1000px";
135  div.appendChild(_CPFontDetectSpan);
136  document.getElementsByTagName("body")[0].appendChild(div);
137 
138  _CPFontDetectReferenceFonts = _CPFontDetectPickTwoDifferentFonts(["monospace", "serif", "sans-serif", "cursive"]);
139 
140  for (var i = 0; i < _CPFontDetectAllFonts.length; i++)
141  {
142  var available = _CPFontDetectFontAvailable(_CPFontDetectAllFonts[i]);
143  if (available)
144  _availableFonts.push(_CPFontDetectAllFonts[i]);
145  }
146 #else
147  // If there's no font detection, just assume all fonts are available.
148  _availableFonts = _CPFontDetectAllFonts;
149 #endif
150  }
151  return _availableFonts;
152 }
153 
158 - (CPArray)fontWithNameIsAvailable:(CPString)aFontName
159 {
160  return _CPFontDetectFontAvailable(aFontName);
161 }
162 
163 - (void)setSelectedFont:(CPFont)aFont isMultiple:(BOOL)aFlag
164 {
165  _selectedFont = aFont;
166  _multiple = aFlag;
167 
168  // TODO Notify CPFontPanel when it exists.
169 }
170 
172 {
173  return _selectedFont;
174 }
175 
176 - (int)weightOfFont:(CPFont)aFont
177 {
178  // TODO Weight 5 is a normal of book weight and 9 and above is bold, but it would be nice to be more
179  // precise than that.
180  return [aFont isBold] ? 9 : 5;
181 }
182 
183 - (CPFontTraitMask)traitsOfFont:(CPFont)aFont
184 {
185  return ([aFont isBold] ? CPBoldFontMask : 0) | ([aFont isItalic] ? CPItalicFontMask : 0);
186 }
187 
188 - (CPFont)convertFont:(CPFont)aFont
189 {
190  if (!_activeChange)
191  return aFont;
192 
193  var addTraits = [_activeChange valueForKey:@"addTraits"];
194 
195  if (addTraits)
196  aFont = [self convertFont:aFont toHaveTrait:addTraits];
197 
198  return aFont;
199 }
200 
201 - (CPFont)convertFont:(CPFont)aFont toHaveTrait:(CPFontTraitMask)addTraits
202 {
203  if (!aFont)
204  return nil;
205 
206  var shouldBeBold = ([aFont isBold] || (addTraits & CPBoldFontMask)) && !(addTraits & CPUnboldFontMask),
207  shouldBeItalic = ([aFont isItalic] || (addTraits & CPItalicFontMask)) && !(addTraits & CPUnitalicFontMask),
208  shouldBeSize = [aFont size];
209 
210  // XXX On the current platform there will always be a bold/italic version of each font, but still leave
211  // || aFont in here for future platforms.
212  aFont = [CPFont _fontWithName:[aFont familyName] size:shouldBeSize bold:shouldBeBold italic:shouldBeItalic] || aFont;
213 
214  return aFont;
215 }
216 
217 - (CPFont)convertFont:(CPFont)aFont toFace:(CPString)aTypeface
218 {
219  if (!aFont)
220  return nil;
221 
222  var shouldBeBold = [aFont isBold],
223  shouldBeItalic = [aFont isItalic],
224  shouldBeSize = [aFont size];
225 
226  aFont = [CPFont _fontWithName:aTypeface size:shouldBeSize bold:shouldBeBold italic:shouldBeItalic] || aFont;
227 
228  return aFont;
229 }
230 
231 - (@action)addFontTrait:(id)sender
232 {
233  var tag = [sender tag];
234  _activeChange = tag === nil ? @{} : @{ @"addTraits": tag };
235  _fontAction = CPAddTraitFontAction;
236 
237  [self sendAction];
238 }
239 
240 - (BOOL)sendAction
241 {
242  return [CPApp sendAction:_action to:_target from:self];
243 }
244 
245 
250 - (CPFontPanel)fontPanel:(BOOL)createIt
251 {
252  var panel = nil,
253  panelExists = [CPFontPanelFactory sharedFontPanelExists];
254 
255  if ((panelExists) || (!panelExists && createIt))
256  panel = [CPFontPanelFactory sharedFontPanel];
257 
258  return panel;
259 }
260 
268 - (CPFont)convertFont:(CPFont)aFont toHaveTrait:(CPFontTraitMask)fontTrait
269 {
270  var attributes = [[[aFont fontDescriptor] fontAttributes] copy],
271  symbolicTrait = [[aFont fontDescriptor] symbolicTraits];
272 
273  if (fontTrait & CPBoldFontMask)
274  symbolicTrait |= CPFontBoldTrait;
275 
276  if (fontTrait & CPItalicFontMask)
277  symbolicTrait |= CPFontItalicTrait;
278 
279  if (fontTrait & CPUnboldFontMask) /* FIXME: this only change CPFontSymbolicTrait what about CPFontWeightTrait */
280  symbolicTrait &= ~CPFontBoldTrait;
281 
282  if (fontTrait & CPUnitalicFontMask)
283  symbolicTrait &= ~CPFontItalicTrait;
284 
285  if (fontTrait & CPExpandedFontMask)
286  symbolicTrait |= CPFontExpandedTrait;
287 
288  if (fontTrait & CPSmallCapsFontMask)
289  symbolicTrait |= CPFontSmallCapsTrait;
290 
291  if (![attributes containsKey:CPFontTraitsAttribute])
292  [attributes setObject:[CPDictionary dictionaryWithObject:[CPNumber numberWithUnsignedInt:symbolicTrait]
293  forKey:CPFontSymbolicTrait]
294  forKey:CPFontTraitsAttribute];
295  else
296  [[attributes objectForKey:CPFontTraitsAttribute] setObject:[CPNumber numberWithUnsignedInt:symbolicTrait]
297  forKey:CPFontSymbolicTrait];
298 
299  return [[aFont class] fontWithDescriptor:[CPFontDescriptor fontDescriptorWithFontAttributes:attributes] size:0.0];
300 }
301 
308 - (CPFont)convertFont:(CPFont)aFont toNotHaveTrait:(CPFontTraitMask)fontTrait
309 {
310  var attributes = [[[aFont fontDescriptor] fontAttributes] copy],
311  symbolicTrait = [[aFont fontDescriptor] symbolicTraits];
312 
313  if ((fontTrait & CPBoldFontMask) || (fontTrait & CPUnboldFontMask)) /* FIXME: see convertFont:toHaveTrait: about CPFontWeightTrait */
314  symbolicTrait &= ~CPFontBoldTrait;
315 
316  if ((fontTrait & CPItalicFontMask) || (fontTrait & CPUnitalicFontMask))
317  symbolicTrait &= ~CPFontItalicTrait;
318 
319  if (fontTrait & CPExpandedFontMask)
320  symbolicTrait &= ~CPFontExpandedTrait;
321 
322  if (fontTrait & CPSmallCapsFontMask)
323  symbolicTrait &= ~CPFontSmallCapsTrait;
324 
325  if (![attributes containsKey:CPFontTraitsAttribute])
326  [attributes setObject:[CPDictionary dictionaryWithObject:[CPNumber numberWithUnsignedInt:symbolicTrait]
327  forKey:CPFontSymbolicTrait]
328  forKey:CPFontTraitsAttribute];
329  else
330  [[attributes objectForKey:CPFontTraitsAttribute] setObject:[CPNumber numberWithUnsignedInt:symbolicTrait]
331  forKey:CPFontSymbolicTrait];
332 
333  return [[aFont class] fontWithDescriptor:[CPFontDescriptor fontDescriptorWithFontAttributes:attributes] size:0.0];
334 }
335 
342 - (CPFont)convertFont:(CPFont)aFont toSize:(float)aSize
343 {
344  var descriptor = [aFont fontDescriptor];
345 
346  return [[aFont class] fontWithDescriptor: descriptor size:aSize]
347 }
348 
349 - (void)orderFrontFontPanel:(id)sender
350 {
351  [[self fontPanel:YES] orderFront:sender];
352 }
353 
354 - (void)modifyFont:(id)sender
355 {
356  _fontAction = [sender tag];
357  [self sendAction];
358 
359  if (_selectedFont)
360  [self setSelectedFont:[self convertFont:_selectedFont] isMultiple:NO];
361 }
362 
367 - (void)modifyFontViaPanel:(id)sender
368 {
369  _fontAction = CPViaPanelFontAction;
370  if (_selectedFont)
371  [self setSelectedFont:[self convertFont:_selectedFont] isMultiple:NO];
372 
373  [self sendAction];
374 }
375 
381 - (CPFont)convertFont:(CPFont)aFont
382 {
383  var newFont = nil;
384  switch (_fontAction)
385  {
387  newFont = aFont;
388  break;
389 
391  newFont = [[self fontPanel:NO] panelConvertFont:aFont];
392  break;
393 
395  newFont = aFont;
396  if (!_activeChange)
397  break;
398 
399  var addTraits = [_activeChange valueForKey:@"addTraits"];
400 
401  if (addTraits)
402  newFont = [self convertFont:aFont toHaveTrait:addTraits];
403  break;
404 
405  case CPSizeUpFontAction:
406  newFont = [self convertFont:aFont toSize:[aFont size] + 1.0]; /* any limit ? */
407  break;
408 
410  if ([aFont size] > 1)
411  newFont = [self convertFont:aFont toSize:[aFont size] - 1.0];
412  /* else CPBeep() :-p */
413  break;
414 
415  default:
416  CPLog.trace(@"-[" + [self className] + " " + _cmd + "] unsupported font action: " + _fontAction + " aFont unchanged");
417  newFont = aFont;
418  break;
419  }
420 
421  return newFont;
422 }
423 
424 @end
425 
426 var _CPFontDetectSpan,
427  _CPFontDetectReferenceFonts,
428  _CPFontDetectAllFonts = [
429  /* "04b_21", "A Charming Font", "Abadi MT Condensed", "Abadi MT Condensed Extra Bold", "Abadi MT Condensed Light", "Academy Engraved LET", "Agency FB", "Alba", "Alba Matter", "Alba Super", "Algerian",*/
430  "American Typewriter",
431  /* "Andale Mono", "Andale Mono IPA", "Andy", */
432  "Apple Chancery", "Arial", "Arial Black", "Arial Narrow", "Arial Rounded MT Bold", "Arial Unicode MS",
433  /* "Avant Garde", "Avantgarde", "Baby Kruffy", "Base 02", "Baskerville", "Baskerville Old Face", "Bauhaus 93", "Beesknees ITC", "Bell MT", "Berlin Sans FB", "Berlin Sans FB Demi", "Bernard MT Condensed", "Bickley Script",*/
434  "Big Caslon", "Bitstream Vera Sans", "Bitstream Vera Sans Mono", "Bitstream Vera Serif",
435  /* "Blackadder ITC", "Blackletter686 BT", "Bodoni MT", "Bodoni MT Black", "Bodoni MT Condensed", "Bodoni MT Poster Compressed", "Book Antiqua", "Bookman", "Bookman Old Style", "Bradley Hand ITC", "Braggadocio", "Britannic Bold", "Broadway", "Broadway BT",*/
436  "Brush Script MT",
437  /* "BudHand", "CAMPBELL", "Calibri", "Californian FB", "Calisto MT", "Calligraph421 BT",*/
438  "Cambria",
439  /* "Candara", "Capitals",*/
440  "Caslon", "Castellar", "Cataneo BT", "Centaur", "Century Gothic", "Century Schoolbook", "Century Schoolbook L",
441  /* "Champignon", "Charcoal", "Charter", "Charter BT", "Chicago", "Chick", "Chiller", "ClearlyU", "Colonna MT",*/
442  "Comic Sans", "Comic Sans MS", "Consolas", "Constantia", "Cooper Black", "Copperplate", "Copperplate Gothic Bold", "Copperplate Gothic Light", "Corbel", "Courier", "Courier New",
443  /* "Croobie", "Curlz MT", "Desdemona", "Didot", "DomBold BT", "Edwardian Script ITC", "Engravers MT", "Eras Bold ITC", "Eras Demi ITC", "Eras Light ITC", "Eras Medium ITC", "Eurostile", "FIRSTHOME", "Fat", "Felix Titling", "Fine Hand", "Fixed", "Footlight MT Light", "Forte", "Franklin Gothic Book", "Franklin Gothic Demi", "Franklin Gothic Demi Cond", "Franklin Gothic Heavy", "Franklin Gothic Medium", "Franklin Gothic Medium Cond", "Freestyle Script", "French Script MT", "Freshbot", "Frosty",*/
444  "Futura",
445  /* "GENUINE", "Gadget", "Garamond",*/
446  "Geneva", "Georgia", "Georgia Ref", "Geeza Pro", "Gigi", "Gill Sans", "Gill Sans MT", "Gill Sans MT Condensed", "Gill Sans MT Ext Condensed Bold", "Gill Sans Ultra Bold", "Gill Sans Ultra Bold Condensed",
447  /* "GlooGun", "Gloucester MT Extra Condensed", "Goudy Old Style", "Goudy Stout", "Haettenschweiler", "Harlow Solid Italic", "Harrington",*/
448  "Helvetica", "Helvetica Narrow", "Helvetica Neue", "Herculanum", "High Tower Text", "Highlight LET", "Hoefler Text", "Impact", "Imprint MT Shadow",
449  /* "Informal Roman", "Jenkins v2.0", "John Handy LET", "Jokerman", "Jokerman LET", "Jokewood", "Juice ITC", "Kabel Ult BT", "Kartika", "Kino MT", "Kristen ITC", "Kunstler Script", "La Bamba LET", */
450  "Lucida", "Lucida Bright", "Lucida Calligraphy", "Lucida Console", "Lucida Fax", "Lucida Grande", "Lucida Handwriting", "Lucida Sans", "Lucida Sans Typewriter", "Lucida Sans Unicode",
451  /* "Luxi Mono", "Luxi Sans", "Luxi Serif", "MARKETPRO", "MS Reference Sans Serif", "MS Reference Serif", "Magneto", "Maiandra GD", */
452  "Marker Felt",
453  /* "Matisse ITC", "Matura MT Script Capitals", "Mead Bold", "Mekanik LET", "Mercurius Script MT Bold", */
454  "Microsoft Sans Serif", "Milano LET", "Minion Web", "MisterEarl BT", "Mistral", "Monaco", "Monotype Corsiva", "Monotype.com", "New Century Schoolbook", "New York", "News Gothic MT",
455  /* "Niagara Engraved", "Niagara Solid", "Nimbus Mono L", "Nimbus Roman No9 L", "OCR A Extended", "OCRB", "Odessa LET", "Old English Text MT", "OldDreadfulNo7 BT", "One Stroke Script LET", "Onyx", "Optima", "Orange LET", "Palace Script MT", "Palatino", "Palatino Linotype", */
456  "Papyrus",
457  /* "ParkAvenue BT", "Pepita MT", "Perpetua", "Perpetua Titling MT", "Placard Condensed", "Playbill", "Poornut", "Pristina", "Pump Demi Bold LET", "Pussycat", "Quixley LET", "Rage Italic", "Rage Italic LET", "Ravie", "Rockwell", "Rockwell Condensed", "Rockwell Extra Bold", "Ruach LET", "Runic MT Condensed", "Sand", "Script MT Bold", "Scruff LET", "Segoe UI", "Showcard Gothic", "Skia", "Smudger LET", "Snap ITC", "Square721 BT", "Staccato222 BT", "Stencil", "Sylfaen", */
458  "Tahoma", "Techno", "Tempus Sans ITC", "Terminal", "Textile", "Times", "Times New Roman", "Tiranti Solid LET", "Trebuchet MS",
459  /* "Tw Cen MT", "Tw Cen MT Condensed", "Tw Cen MT Condensed Extra Bold", "URW Antiqua T", "URW Bookman L", "URW Chancery L", "URW Gothic L", "URW Palladio L", "Univers", "University Roman LET", "Utopia", */
460  "Verdana", "Verdana Ref", /* "Victorian LET", "Viner Hand ITC", "Vivaldi", "Vladimir Script", "Vrinda", "Weltron Urban", "Westwood LET", "Wide Latin", "Zapf Chancery", */
461  "Zapfino"];
462 
463 // Compare against the reference fonts. Return true if it produces a different size than at least one of them.
464 var _CPFontDetectFontAvailable = function(font)
465 {
466  for (var i = 0; i < _CPFontDetectReferenceFonts.length; i++)
467  if (_CPFontDetectCompareFonts(_CPFontDetectReferenceFonts[i], font))
468  return true;
469  return false;
470 };
471 
472 var _CPFontDetectCache = {};
473 
474 // Compares two given fonts. Returns true if they produce different sizes (i.e. fontA didn't fallback to fontB)
475 var _CPFontDetectCompareFonts = function(fontA, fontB)
476 {
477  var a;
478  if (_CPFontDetectCache[fontA])
479  a = _CPFontDetectCache[fontA];
480 
481  else
482  {
483  _CPFontDetectSpan.style.fontFamily = '"' + fontA + '"';
484  _CPFontDetectCache[fontA] = a = { w: _CPFontDetectSpan.offsetWidth, h: _CPFontDetectSpan.offsetHeight };
485  }
486 
487  _CPFontDetectSpan.style.fontFamily = '"' + fontB + '", "' + fontA + '"';
488  var bWidth = _CPFontDetectSpan.offsetWidth,
489  bHeight = _CPFontDetectSpan.offsetHeight;
490 
491  return (a.w != bWidth || a.h != bHeight);
492 };
493 
494 // Test the candidate fonts pairwise until we find two that are different. Otherwise return the first.
495 var _CPFontDetectPickTwoDifferentFonts = function(candidates)
496 {
497  for (var i = 0; i < candidates.length; i++)
498  for (var j = 0; j < i; j++)
499  if (_CPFontDetectCompareFonts(candidates[i], candidates[j]))
500  return [candidates[i], candidates[j]];
501  return [candidates[0]];
502 };
503 
505 
507 
511 - (id)target
512 {
513  return _target;
514 }
515 
519 - (void)setTarget:(id)aValue
520 {
521  _target = aValue;
522 }
523 
527 - (SEL)action
528 {
529  return _action;
530 }
531 
535 - (void)setAction:(SEL)aValue
536 {
537  _action = aValue;
538 }
539 
543 - (id)delegate
544 {
545  return _delegate;
546 }
547 
551 - (void)setDelegate:(id)aValue
552 {
553  _delegate = aValue;
554 }
555 
559 - (BOOL)isMultiple
560 {
561  return _multiple;
562 }
563 
567 - (void)setMultiple:(BOOL)aValue
568 {
569  _multiple = aValue;
570 }
571 
572 @end
CPUnboldFontMask
Definition: CPFontManager.j:29
Definition: CPFont.h:2
CPAddTraitFontAction
Definition: CPFontManager.j:50
CPLighterFontAction
Definition: CPFontManager.j:54
CPUnitalicFontMask
Definition: CPFontManager.j:38
CPString className()
Definition: CPObject.j:527
CPPosterFontMask
Definition: CPFontManager.j:35
CPFontDescriptor fontDescriptorWithFontAttributes:(CPDictionary attributes)
CPFont convertFont:toSize:(CPFont aFont, [toSize] float aSize)
CPFontSmallCapsTrait
void orderFront:(id sender)
Definition: CPFontPanel.j:215
CPNarrowFontMask
Definition: CPFontManager.j:31
CPFontExpandedTrait
id numberWithUnsignedInt:(unsigned anUnsignedInt)
Definition: CPNumber.j:102
A mutable key-value pair collection.
Definition: CPDictionary.h:2
CPCondensedFontMask
Definition: CPFontManager.j:33
CPNonStandardCharacterSetFontMask
Definition: CPFontManager.j:30
An immutable string (collection of characters).
Definition: CPString.h:2
CPBoldFontMask
Definition: CPFontManager.j:28
BOOL isBold()
Definition: CPFont.j:556
CPFont panelConvertFont:(CPFont aFont)
Definition: CPFontPanel.j:236
CPFixedPitchFontMask
Definition: CPFontManager.j:37
CPFontDescriptor fontDescriptor()
Definition: CPFont.j:453
global CPApp CPItalicFontMask
Definition: CPFontManager.j:27
CPCompressedFontMask
Definition: CPFontManager.j:36
BOOL isItalic()
Definition: CPFont.j:564
void setFontManagerFactory:(Class aClass)
Definition: CPFontManager.j:95
int tag
CPFontManager sharedFontManager()
Definition: CPFontManager.j:82
CPFontSymbolicTraits symbolicTraits()
CPFontBoldTrait
CPDictionary fontAttributes()
CPFontTraitsAttribute
CPFont convertFont:toHaveTrait:(CPFont aFont, [toHaveTrait] CPFontTraitMask addTraits)
CPHeavierFontAction
Definition: CPFontManager.j:53
CPRemoveTraitFontAction
Definition: CPFontManager.j:55
CPExpandedFontMask
Definition: CPFontManager.j:32
CPFontItalicTrait
CPString familyName()
Definition: CPFont.j:393
var CPSharedFontManager
Definition: CPFontManager.j:41
CPFontPanel fontPanel:(BOOL createIt)
float size()
Definition: CPFont.j:375
CPSizeUpFontAction
Definition: CPFontManager.j:51
var CPFontPanelFactory
Definition: CPFontManager.j:43
CPNoFontChangeAction
Definition: CPFontManager.j:48
Class class()
Definition: CPObject.j:179
CPViaPanelFontAction
Definition: CPFontManager.j:49
void setSelectedFont:isMultiple:(CPFont aFont, [isMultiple] BOOL aFlag)
A bridged object to native Javascript numbers.
Definition: CPNumber.h:2
CPFont selectedFont()
var CPFontManagerFactory
Definition: CPFontManager.j:42
CPSizeDownFontAction
Definition: CPFontManager.j:52
CPSmallCapsFontMask
Definition: CPFontManager.j:34
CPDictionary copy()
Definition: CPDictionary.j:292
CPFont convertFont:(CPFont aFont)
id dictionaryWithObject:forKey:(id anObject, [forKey] id aKey)
Definition: CPDictionary.j:81
CPArray availableFonts()