blob: 6eb4e183e58743f0d5c40280886826bf7376addc [file] [log] [blame]
/*
* Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
* Copyright (C) 2007 Nicholas Shanks <webkit@nickshanks.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "third_party/blink/renderer/platform/fonts/font_cache.h"
#include <memory>
#import <AppKit/AppKit.h>
#import <CoreText/CoreText.h>
#include "base/location.h"
#include "base/mac/bridging.h"
#include "base/metrics/histogram_macros.h"
#include "base/timer/elapsed_timer.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/renderer/platform/font_family_names.h"
#include "third_party/blink/renderer/platform/fonts/font_description.h"
#include "third_party/blink/renderer/platform/fonts/font_face_creation_params.h"
#include "third_party/blink/renderer/platform/fonts/font_platform_data.h"
#include "third_party/blink/renderer/platform/fonts/mac/font_matcher_mac.h"
#include "third_party/blink/renderer/platform/fonts/mac/font_platform_data_mac.h"
#include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/scheduler/public/main_thread.h"
#include "third_party/blink/renderer/platform/web_test_support.h"
#include "third_party/blink/renderer/platform/wtf/functional.h"
#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
#include "third_party/blink/renderer/platform/wtf/wtf.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
// Forward declare Mac SPIs.
// Request for public API: rdar://13803570
@interface NSFont (WebKitSPI)
+ (NSFont*)findFontLike:(NSFont*)font
forString:(NSString*)string
withRange:(NSRange)range
inLanguage:(id)useNil;
+ (NSFont*)findFontLike:(NSFont*)font
forCharacter:(UniChar)uc
inLanguage:(id)useNil;
@end
namespace blink {
const char kColorEmojiFontMac[] = "Apple Color Emoji";
// static
const AtomicString& FontCache::LegacySystemFontFamily() {
return font_family_names::kBlinkMacSystemFont;
}
// static
void FontCache::InvalidateFromAnyThread() {
if (!IsMainThread()) {
Thread::MainThread()
->GetTaskRunner(MainThreadTaskRunnerRestricted())
->PostTask(FROM_HERE,
WTF::BindOnce(&FontCache::InvalidateFromAnyThread));
return;
}
FontCache::Get().Invalidate();
}
static void FontCacheRegisteredFontsChangedNotificationCallback(
CFNotificationCenterRef,
void* observer,
CFStringRef name,
const void*,
CFDictionaryRef) {
DCHECK_EQ(observer, &FontCache::Get());
DCHECK(CFEqual(name, kCTFontManagerRegisteredFontsChangedNotification));
FontCache::InvalidateFromAnyThread();
}
static bool UseHinting() {
// Enable hinting only when antialiasing is disabled in web tests.
return (WebTestSupport::IsRunningWebTest() &&
!WebTestSupport::IsFontAntialiasingEnabledForTest());
}
void FontCache::PlatformInit() {
CFNotificationCenterAddObserver(
CFNotificationCenterGetLocalCenter(), this,
FontCacheRegisteredFontsChangedNotificationCallback,
kCTFontManagerRegisteredFontsChangedNotification, /*object=*/nullptr,
CFNotificationSuspensionBehaviorDeliverImmediately);
}
static inline bool IsAppKitFontWeightBold(NSInteger app_kit_font_weight) {
return app_kit_font_weight >= 7;
}
scoped_refptr<SimpleFontData> FontCache::PlatformFallbackFontForCharacter(
const FontDescription& font_description,
UChar32 character,
const SimpleFontData* font_data_to_substitute,
FontFallbackPriority fallback_priority) {
if (fallback_priority == FontFallbackPriority::kEmojiEmoji) {
scoped_refptr<SimpleFontData> emoji_font =
GetFontData(font_description, AtomicString(kColorEmojiFontMac));
if (emoji_font)
return emoji_font;
}
// FIXME: We should fix getFallbackFamily to take a UChar32
// and remove this split-to-UChar16 code.
UChar code_units[2];
int code_units_length;
if (character <= 0xFFFF) {
code_units[0] = character;
code_units_length = 1;
} else {
code_units[0] = U16_LEAD(character);
code_units[1] = U16_TRAIL(character);
code_units_length = 2;
}
const FontPlatformData& platform_data =
font_data_to_substitute->PlatformData();
NSFont* ns_font = base::mac::CFToNSPtrCast(platform_data.CtFont());
NSString* string = [[NSString alloc]
initWithCharacters:reinterpret_cast<UniChar*>(code_units)
length:code_units_length];
NSFont* substitute_font =
[NSFont findFontLike:ns_font
forString:string
withRange:NSMakeRange(0, code_units_length)
inLanguage:nil];
// FIXME: Remove this SPI usage: http://crbug.com/255122
if (!substitute_font && code_units_length == 1)
substitute_font =
[NSFont findFontLike:ns_font forCharacter:code_units[0] inLanguage:nil];
if (!substitute_font)
return nullptr;
// Use the family name from the AppKit-supplied substitute font, requesting
// the traits, weight, and size we want. One way this does better than the
// original AppKit request is that it takes synthetic bold and oblique into
// account. But it does create the possibility that we could end up with a
// font that doesn't actually cover the characters we need.
NSFontManager* font_manager = NSFontManager.sharedFontManager;
NSFontTraitMask traits;
NSInteger weight;
CGFloat size;
if (ns_font) {
traits = [font_manager traitsOfFont:ns_font];
if (platform_data.synthetic_bold_)
traits |= NSBoldFontMask;
if (platform_data.synthetic_italic_)
traits |= NSFontItalicTrait;
weight = [font_manager weightOfFont:ns_font];
size = [ns_font pointSize];
} else {
// For custom fonts nsFont is nil.
traits = font_description.Style() ? NSFontItalicTrait : 0;
weight = ToAppKitFontWeight(font_description.Weight());
size = font_description.ComputedPixelSize();
}
NSFontTraitMask substitute_font_traits =
[font_manager traitsOfFont:substitute_font];
NSInteger substitute_font_weight =
[font_manager weightOfFont:substitute_font];
if (traits != substitute_font_traits || weight != substitute_font_weight ||
!ns_font) {
if (NSFont* best_variation =
[font_manager fontWithFamily:substitute_font.familyName
traits:traits
weight:weight
size:size]) {
if ((!ns_font ||
[font_manager traitsOfFont:best_variation] !=
substitute_font_traits ||
[font_manager weightOfFont:best_variation] !=
substitute_font_weight) &&
[[best_variation coveredCharacterSet]
longCharacterIsMember:character])
substitute_font = best_variation;
}
}
substitute_font =
UseHinting() ? substitute_font.screenFont : substitute_font.printerFont;
substitute_font_traits = [font_manager traitsOfFont:substitute_font];
substitute_font_weight = [font_manager weightOfFont:substitute_font];
bool synthetic_bold = IsAppKitFontWeightBold(weight) &&
!IsAppKitFontWeightBold(substitute_font_weight);
std::unique_ptr<FontPlatformData> alternate_font = FontPlatformDataFromNSFont(
substitute_font, platform_data.size(), font_description.SpecifiedSize(),
synthetic_bold,
(traits & NSFontItalicTrait) &&
!(substitute_font_traits & NSFontItalicTrait),
font_description.TextRendering(), ResolvedFontFeatures(),
platform_data.Orientation(), font_description.FontOpticalSizing(),
/*variation_settings=*/nullptr);
if (!alternate_font)
return nullptr;
return FontDataFromFontPlatformData(alternate_font.get(), kDoNotRetain);
}
scoped_refptr<SimpleFontData> FontCache::GetLastResortFallbackFont(
const FontDescription& font_description,
ShouldRetain should_retain) {
// FIXME: Would be even better to somehow get the user's default font here.
// For now we'll pick the default that the user would get without changing
// any prefs.
scoped_refptr<SimpleFontData> simple_font_data =
GetFontData(font_description, font_family_names::kTimes,
AlternateFontName::kAllowAlternate, should_retain);
if (simple_font_data)
return simple_font_data;
// The Times fallback will almost always work, but in the highly unusual case
// where the user doesn't have it, we fall back on Lucida Grande because
// that's guaranteed to be there, according to Nathan Taylor. This is good
// enough to avoid a crash at least.
return GetFontData(font_description, font_family_names::kLucidaGrande,
AlternateFontName::kAllowAlternate, should_retain);
}
std::unique_ptr<FontPlatformData> FontCache::CreateFontPlatformData(
const FontDescription& font_description,
const FontFaceCreationParams& creation_params,
float font_size,
AlternateFontName alternate_name) {
NSFontTraitMask traits = font_description.Style() ? NSFontItalicTrait : 0;
float size = font_size;
NSFont* matched_font = nullptr;
if (alternate_name == AlternateFontName::kLocalUniqueFace &&
RuntimeEnabledFeatures::FontSrcLocalMatchingEnabled()) {
matched_font = MatchUniqueFont(creation_params.Family(), size);
} else {
matched_font = MatchNSFontFamily(creation_params.Family(), traits,
font_description.Weight(), size);
}
if (!matched_font)
return nullptr;
NSFontManager* font_manager = NSFontManager.sharedFontManager;
NSFontTraitMask actual_traits = 0;
if (font_description.Style())
actual_traits = [font_manager traitsOfFont:matched_font];
NSInteger actual_weight = [font_manager weightOfFont:matched_font];
NSFont* platform_font =
UseHinting() ? matched_font.screenFont : matched_font.printerFont;
NSInteger app_kit_weight = ToAppKitFontWeight(font_description.Weight());
bool synthetic_bold_requested = (IsAppKitFontWeightBold(app_kit_weight) &&
!IsAppKitFontWeightBold(actual_weight)) ||
font_description.IsSyntheticBold();
bool synthetic_bold =
synthetic_bold_requested && font_description.SyntheticBoldAllowed();
bool synthetic_italic_requested =
((traits & NSFontItalicTrait) && !(actual_traits & NSFontItalicTrait)) ||
font_description.IsSyntheticItalic();
bool synthetic_italic =
synthetic_italic_requested && font_description.SyntheticItalicAllowed();
// FontPlatformData::typeface() is null in the case of Chromium out-of-process
// font loading failing. Out-of-process loading occurs for registered fonts
// stored in non-system locations. When loading fails, we do not want to use
// the returned FontPlatformData since it will not have a valid SkTypeface.
std::unique_ptr<FontPlatformData> platform_data = FontPlatformDataFromNSFont(
platform_font, size, font_description.SpecifiedSize(), synthetic_bold,
synthetic_italic, font_description.TextRendering(),
ResolvedFontFeatures(), font_description.Orientation(),
font_description.FontOpticalSizing(),
font_description.VariationSettings());
if (!platform_data || !platform_data->Typeface()) {
return nullptr;
}
return platform_data;
}
} // namespace blink