PageRenderTime 23ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 1ms

/third_party/WebKit/Source/platform/fonts/mac/FontFamilyMatcherMac.mm

https://gitlab.com/0072016/Facebook-SDK-
Objective C++ | 293 lines | 192 code | 43 blank | 58 comment | 53 complexity | da5dc13e3a2f0dd86f087048c7c03263 MD5 | raw file
  1. /*
  2. * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
  3. * Copyright (C) 2007 Nicholas Shanks <webkit@nickshanks.com>
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions
  7. * are met:
  8. *
  9. * 1. Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. * 2. Redistributions in binary form must reproduce the above copyright
  12. * notice, this list of conditions and the following disclaimer in the
  13. * documentation and/or other materials provided with the distribution.
  14. * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
  15. * its contributors may be used to endorse or promote products derived
  16. * from this software without specific prior written permission.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
  19. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  20. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  21. * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
  22. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  23. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  24. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  25. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  27. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. */
  29. #import "platform/fonts/mac/FontFamilyMatcherMac.h"
  30. #import <AppKit/AppKit.h>
  31. #import <Foundation/Foundation.h>
  32. #import <math.h>
  33. #include "platform/fonts/FontTraits.h"
  34. #include "platform/LayoutTestSupport.h"
  35. #include "platform/mac/VersionUtilMac.h"
  36. #import "wtf/HashSet.h"
  37. #import "wtf/text/AtomicStringHash.h"
  38. @interface NSFont (YosemiteAdditions)
  39. + (NSFont*)systemFontOfSize:(CGFloat)size weight:(CGFloat)weight;
  40. @end
  41. namespace {
  42. static CGFloat toYosemiteFontWeight(blink::FontWeight fontWeight)
  43. {
  44. static uint64_t nsFontWeights[] = {
  45. 0xbfe99999a0000000, // NSFontWeightUltraLight
  46. 0xbfe3333340000000, // NSFontWeightThin
  47. 0xbfd99999a0000000, // NSFontWeightLight
  48. 0x0000000000000000, // NSFontWeightRegular
  49. 0x3fcd70a3e0000000, // NSFontWeightMedium
  50. 0x3fd3333340000000, // NSFontWeightSemibold
  51. 0x3fd99999a0000000, // NSFontWeightBold
  52. 0x3fe1eb8520000000, // NSFontWeightHeavy
  53. 0x3fe3d70a40000000, // NSFontWeightBlack
  54. };
  55. ASSERT(fontWeight >= 0 && fontWeight <= 8);
  56. CGFloat* weight = reinterpret_cast<CGFloat*>(&nsFontWeights[fontWeight]);
  57. return *weight;
  58. }
  59. }
  60. namespace blink {
  61. const NSFontTraitMask SYNTHESIZED_FONT_TRAITS = (NSBoldFontMask | NSItalicFontMask);
  62. const NSFontTraitMask IMPORTANT_FONT_TRAITS = (NSCompressedFontMask
  63. | NSCondensedFontMask
  64. | NSExpandedFontMask
  65. | NSItalicFontMask
  66. | NSNarrowFontMask
  67. | NSPosterFontMask
  68. | NSSmallCapsFontMask );
  69. static BOOL acceptableChoice(NSFontTraitMask desiredTraits, NSFontTraitMask candidateTraits)
  70. {
  71. desiredTraits &= ~SYNTHESIZED_FONT_TRAITS;
  72. return (candidateTraits & desiredTraits) == desiredTraits;
  73. }
  74. static BOOL betterChoice(NSFontTraitMask desiredTraits, int desiredWeight,
  75. NSFontTraitMask chosenTraits, int chosenWeight,
  76. NSFontTraitMask candidateTraits, int candidateWeight)
  77. {
  78. if (!acceptableChoice(desiredTraits, candidateTraits))
  79. return NO;
  80. // A list of the traits we care about.
  81. // The top item in the list is the worst trait to mismatch; if a font has this
  82. // and we didn't ask for it, we'd prefer any other font in the family.
  83. const NSFontTraitMask masks[] = {
  84. NSPosterFontMask,
  85. NSSmallCapsFontMask,
  86. NSItalicFontMask,
  87. NSCompressedFontMask,
  88. NSCondensedFontMask,
  89. NSExpandedFontMask,
  90. NSNarrowFontMask,
  91. 0
  92. };
  93. int i = 0;
  94. NSFontTraitMask mask;
  95. while ((mask = masks[i++])) {
  96. BOOL desired = (desiredTraits & mask) != 0;
  97. BOOL chosenHasUnwantedTrait = desired != ((chosenTraits & mask) != 0);
  98. BOOL candidateHasUnwantedTrait = desired != ((candidateTraits & mask) != 0);
  99. if (!candidateHasUnwantedTrait && chosenHasUnwantedTrait)
  100. return YES;
  101. if (!chosenHasUnwantedTrait && candidateHasUnwantedTrait)
  102. return NO;
  103. }
  104. int chosenWeightDeltaMagnitude = abs(chosenWeight - desiredWeight);
  105. int candidateWeightDeltaMagnitude = abs(candidateWeight - desiredWeight);
  106. // If both are the same distance from the desired weight, prefer the candidate if it is further from medium.
  107. if (chosenWeightDeltaMagnitude == candidateWeightDeltaMagnitude)
  108. return abs(candidateWeight - 6) > abs(chosenWeight - 6);
  109. // Otherwise, prefer the one closer to the desired weight.
  110. return candidateWeightDeltaMagnitude < chosenWeightDeltaMagnitude;
  111. }
  112. // Family name is somewhat of a misnomer here. We first attempt to find an exact match
  113. // comparing the desiredFamily to the PostScript name of the installed fonts. If that fails
  114. // we then do a search based on the family names of the installed fonts.
  115. NSFont* MatchNSFontFamily(NSString* desiredFamily, NSFontTraitMask desiredTraits, FontWeight desiredWeight, float size)
  116. {
  117. if ([desiredFamily isEqualToString:@"BlinkMacSystemFont"]) {
  118. // On OSX 10.9, the default system font depends on the SDK version. When
  119. // compiled against the OSX 10.10 SDK, the font is .LucidaGrandeUI. When
  120. // compiled against the OSX 10.6 SDK, the font is Lucida Grande. Layout
  121. // tests don't support different expectations based on the SDK version,
  122. // so force layout tests to use "Lucida Grande". Once the 10.10 SDK
  123. // switch is made, this should be changed to return .LucidaGrandeUI and
  124. // the Layout Expectations should be updated. http://crbug.com/515836.
  125. if (LayoutTestSupport::isRunningLayoutTest() && IsOSMavericks()) {
  126. if (desiredWeight >= blink::FontWeightBold)
  127. return [NSFont fontWithName:@"Lucida Grande Bold" size:size];
  128. else
  129. return [NSFont fontWithName:@"Lucida Grande" size:size];
  130. }
  131. NSFont* font = nil;
  132. if (IsOSMavericksOrEarlier()) {
  133. // On older OSX versions, only bold and regular are available.
  134. if (desiredWeight >= blink::FontWeightBold)
  135. font = [NSFont boldSystemFontOfSize:size];
  136. else
  137. font = [NSFont systemFontOfSize:size];
  138. }
  139. else {
  140. // On OSX 10.10+, the default system font has more weights.
  141. font = [NSFont systemFontOfSize:size weight:toYosemiteFontWeight(desiredWeight)];
  142. }
  143. if (desiredTraits & IMPORTANT_FONT_TRAITS)
  144. font = [[NSFontManager sharedFontManager] convertFont:font toHaveTrait:desiredTraits];
  145. return font;
  146. }
  147. NSFontManager *fontManager = [NSFontManager sharedFontManager];
  148. // Do a simple case insensitive search for a matching font family.
  149. // NSFontManager requires exact name matches.
  150. // This addresses the problem of matching arial to Arial, etc., but perhaps not all the issues.
  151. NSEnumerator *e = [[fontManager availableFontFamilies] objectEnumerator];
  152. NSString *availableFamily;
  153. while ((availableFamily = [e nextObject])) {
  154. if ([desiredFamily caseInsensitiveCompare:availableFamily] == NSOrderedSame)
  155. break;
  156. }
  157. int appKitFontWeight = toAppKitFontWeight(desiredWeight);
  158. if (!availableFamily) {
  159. // Match by PostScript name.
  160. NSEnumerator *availableFonts = [[fontManager availableFonts] objectEnumerator];
  161. NSString *availableFont;
  162. NSFont *nameMatchedFont = nil;
  163. NSFontTraitMask desiredTraitsForNameMatch = desiredTraits | (appKitFontWeight >= 7 ? NSBoldFontMask : 0);
  164. while ((availableFont = [availableFonts nextObject])) {
  165. if ([desiredFamily caseInsensitiveCompare:availableFont] == NSOrderedSame) {
  166. nameMatchedFont = [NSFont fontWithName:availableFont size:size];
  167. // Special case Osaka-Mono. According to <rdar://problem/3999467>, we need to
  168. // treat Osaka-Mono as fixed pitch.
  169. if ([desiredFamily caseInsensitiveCompare:@"Osaka-Mono"] == NSOrderedSame && desiredTraitsForNameMatch == 0)
  170. return nameMatchedFont;
  171. NSFontTraitMask traits = [fontManager traitsOfFont:nameMatchedFont];
  172. if ((traits & desiredTraitsForNameMatch) == desiredTraitsForNameMatch)
  173. return [fontManager convertFont:nameMatchedFont toHaveTrait:desiredTraitsForNameMatch];
  174. availableFamily = [nameMatchedFont familyName];
  175. break;
  176. }
  177. }
  178. }
  179. // Found a family, now figure out what weight and traits to use.
  180. BOOL choseFont = false;
  181. int chosenWeight = 0;
  182. NSFontTraitMask chosenTraits = 0;
  183. NSString *chosenFullName = 0;
  184. NSArray *fonts = [fontManager availableMembersOfFontFamily:availableFamily];
  185. unsigned n = [fonts count];
  186. unsigned i;
  187. for (i = 0; i < n; i++) {
  188. NSArray *fontInfo = [fonts objectAtIndex:i];
  189. // Array indices must be hard coded because of lame AppKit API.
  190. NSString *fontFullName = [fontInfo objectAtIndex:0];
  191. NSInteger fontWeight = [[fontInfo objectAtIndex:2] intValue];
  192. NSFontTraitMask fontTraits = [[fontInfo objectAtIndex:3] unsignedIntValue];
  193. BOOL newWinner;
  194. if (!choseFont)
  195. newWinner = acceptableChoice(desiredTraits, fontTraits);
  196. else
  197. newWinner = betterChoice(desiredTraits, appKitFontWeight, chosenTraits, chosenWeight, fontTraits, fontWeight);
  198. if (newWinner) {
  199. choseFont = YES;
  200. chosenWeight = fontWeight;
  201. chosenTraits = fontTraits;
  202. chosenFullName = fontFullName;
  203. if (chosenWeight == appKitFontWeight && (chosenTraits & IMPORTANT_FONT_TRAITS) == (desiredTraits & IMPORTANT_FONT_TRAITS))
  204. break;
  205. }
  206. }
  207. if (!choseFont)
  208. return nil;
  209. NSFont *font = [NSFont fontWithName:chosenFullName size:size];
  210. if (!font)
  211. return nil;
  212. NSFontTraitMask actualTraits = 0;
  213. if (desiredTraits & NSFontItalicTrait)
  214. actualTraits = [fontManager traitsOfFont:font];
  215. int actualWeight = [fontManager weightOfFont:font];
  216. bool syntheticBold = appKitFontWeight >= 7 && actualWeight < 7;
  217. bool syntheticItalic = (desiredTraits & NSFontItalicTrait) && !(actualTraits & NSFontItalicTrait);
  218. // There are some malformed fonts that will be correctly returned by -fontWithFamily:traits:weight:size: as a match for a particular trait,
  219. // though -[NSFontManager traitsOfFont:] incorrectly claims the font does not have the specified trait. This could result in applying
  220. // synthetic bold on top of an already-bold font, as reported in <http://bugs.webkit.org/show_bug.cgi?id=6146>. To work around this
  221. // problem, if we got an apparent exact match, but the requested traits aren't present in the matched font, we'll try to get a font from
  222. // the same family without those traits (to apply the synthetic traits to later).
  223. NSFontTraitMask nonSyntheticTraits = desiredTraits;
  224. if (syntheticBold)
  225. nonSyntheticTraits &= ~NSBoldFontMask;
  226. if (syntheticItalic)
  227. nonSyntheticTraits &= ~NSItalicFontMask;
  228. if (nonSyntheticTraits != desiredTraits) {
  229. NSFont *fontWithoutSyntheticTraits = [fontManager fontWithFamily:availableFamily traits:nonSyntheticTraits weight:chosenWeight size:size];
  230. if (fontWithoutSyntheticTraits)
  231. font = fontWithoutSyntheticTraits;
  232. }
  233. return font;
  234. }
  235. int toAppKitFontWeight(FontWeight fontWeight)
  236. {
  237. static int appKitFontWeights[] = {
  238. 2, // FontWeight100
  239. 3, // FontWeight200
  240. 4, // FontWeight300
  241. 5, // FontWeight400
  242. 6, // FontWeight500
  243. 8, // FontWeight600
  244. 9, // FontWeight700
  245. 10, // FontWeight800
  246. 12, // FontWeight900
  247. };
  248. return appKitFontWeights[fontWeight];
  249. }
  250. } // namespace blink