blob: 74cf486623ea84f913eaa4616886c56c2bf79980 [file] [log] [blame]
/*
* Copyright (C) 2017 Apple Inc. All rights reserved.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
* 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.
*/
#include "config.h"
#include "FontCascadeDescription.h"
#include "SystemFontDatabaseCoreText.h"
#include <mutex>
namespace WebCore {
template<typename T, typename U, std::size_t size, std::size_t... indices> std::array<T, size> convertArray(U (&array)[size], std::index_sequence<indices...>)
{
return { { array[indices]... } };
}
template<typename T, typename U, std::size_t size> inline std::array<T, size> convertArray(U (&array)[size])
{
return convertArray<T>(array, std::make_index_sequence<size> { });
}
static inline std::optional<SystemFontKind> matchSystemFontUse(const AtomString& string)
{
if (equalLettersIgnoringASCIICase(string, "-webkit-system-font"_s)
|| equalLettersIgnoringASCIICase(string, "-apple-system"_s)
|| equalLettersIgnoringASCIICase(string, "-apple-system-font"_s)
|| equalLettersIgnoringASCIICase(string, "system-ui"_s)
|| equalLettersIgnoringASCIICase(string, "ui-sans-serif"_s))
return SystemFontKind::SystemUI;
#if HAVE(DESIGN_SYSTEM_UI_FONTS)
if (equalLettersIgnoringASCIICase(string, "ui-serif"_s))
return SystemFontKind::UISerif;
if (equalLettersIgnoringASCIICase(string, "ui-monospace"_s))
return SystemFontKind::UIMonospace;
if (equalLettersIgnoringASCIICase(string, "ui-rounded"_s))
return SystemFontKind::UIRounded;
#endif
static const CFStringRef styles[] = {
kCTUIFontTextStyleHeadline,
kCTUIFontTextStyleBody,
kCTUIFontTextStyleTitle1,
kCTUIFontTextStyleTitle2,
kCTUIFontTextStyleTitle3,
kCTUIFontTextStyleSubhead,
kCTUIFontTextStyleFootnote,
kCTUIFontTextStyleCaption1,
kCTUIFontTextStyleCaption2,
kCTUIFontTextStyleShortHeadline,
kCTUIFontTextStyleShortBody,
kCTUIFontTextStyleShortSubhead,
kCTUIFontTextStyleShortFootnote,
kCTUIFontTextStyleShortCaption1,
kCTUIFontTextStyleTallBody,
kCTUIFontTextStyleTitle0,
kCTUIFontTextStyleTitle4,
};
auto compareAsPointer = [](const AtomString& lhs, const AtomString& rhs) {
return lhs.impl() < rhs.impl();
};
static NeverDestroyed strings = [&compareAsPointer] {
auto result = convertArray<AtomString>(styles);
std::sort(result.begin(), result.end(), compareAsPointer);
return result;
}();
if (std::binary_search(strings.get().begin(), strings.get().end(), string, compareAsPointer))
return SystemFontKind::TextStyle;
return std::nullopt;
}
static inline Vector<RetainPtr<CTFontDescriptorRef>> systemFontCascadeList(const FontDescription& description, const AtomString& cssFamily, SystemFontKind systemFontKind, AllowUserInstalledFonts allowUserInstalledFonts)
{
return SystemFontDatabaseCoreText::singleton().cascadeList(description, cssFamily, systemFontKind, allowUserInstalledFonts);
}
unsigned FontCascadeDescription::effectiveFamilyCount() const
{
// FIXME: Move all the other system font keywords from fontWithFamilySpecialCase() to here.
unsigned result = 0;
for (unsigned i = 0; i < familyCount(); ++i) {
const auto& cssFamily = familyAt(i);
if (auto use = matchSystemFontUse(cssFamily))
result += systemFontCascadeList(*this, cssFamily, *use, shouldAllowUserInstalledFonts()).size();
else
++result;
}
return result;
}
FontFamilySpecification FontCascadeDescription::effectiveFamilyAt(unsigned index) const
{
// The special cases in this function need to match the behavior in FontCacheCoreText.cpp. This code
// is used for regular (element style) lookups, and the code in FontDescriptionCocoa.cpp is used when
// src:local(special-cased-name) is specified inside an @font-face block.
// FIXME: Currently, an @font-face block corresponds to a single item in the font-family: fallback list, which
// means that "src:local(system-ui)" can't follow the Core Text cascade list (the way it does for regular lookups).
// These two behaviors should be unified, which would hopefully allow us to delete this duplicate code.
for (unsigned i = 0; i < familyCount(); ++i) {
const auto& cssFamily = familyAt(i);
if (auto use = matchSystemFontUse(cssFamily)) {
auto cascadeList = systemFontCascadeList(*this, cssFamily, *use, shouldAllowUserInstalledFonts());
if (index < cascadeList.size())
return FontFamilySpecification(cascadeList[index].get());
index -= cascadeList.size();
}
else if (!index)
return cssFamily;
else
--index;
}
ASSERT_NOT_REACHED();
return nullAtom();
}
AtomString FontDescription::platformResolveGenericFamily(UScriptCode script, const AtomString& locale, const AtomString& familyName)
{
ASSERT((locale.isNull() && script == USCRIPT_COMMON) || !locale.isNull());
if (script == USCRIPT_COMMON)
return nullAtom();
// FIXME: Use the system font database to handle standardFamily
if (familyName == serifFamily)
return AtomString { SystemFontDatabaseCoreText::singleton().serifFamily(locale.string()) };
if (familyName == sansSerifFamily)
return AtomString { SystemFontDatabaseCoreText::singleton().sansSerifFamily(locale.string()) };
if (familyName == cursiveFamily)
return AtomString { SystemFontDatabaseCoreText::singleton().cursiveFamily(locale.string()) };
if (familyName == fantasyFamily)
return AtomString { SystemFontDatabaseCoreText::singleton().fantasyFamily(locale.string()) };
if (familyName == monospaceFamily)
return AtomString { SystemFontDatabaseCoreText::singleton().monospaceFamily(locale.string()) };
return nullAtom();
}
}