Diff
Modified: trunk/LayoutTests/ChangeLog (213435 => 213436)
--- trunk/LayoutTests/ChangeLog 2017-03-05 19:14:12 UTC (rev 213435)
+++ trunk/LayoutTests/ChangeLog 2017-03-05 20:14:02 UTC (rev 213436)
@@ -1,3 +1,15 @@
+2017-03-04 Myles C. Maxfield <mmaxfi...@apple.com>
+
+ Update CSSFontSelector's matching algorithm to understand ranges
+ https://bugs.webkit.org/show_bug.cgi?id=168892
+
+ Reviewed by Jon Lee.
+
+ Update CSS Font Loading API test to accept font-stretch values.
+
+ * fast/text/font-face-_javascript_-expected.txt:
+ * fast/text/font-face-_javascript_.html:
+
2017-03-05 Carlos Garcia Campos <cgar...@igalia.com>
[GTK] Two file reset tests are failing in the bots since they were added in r213042
Modified: trunk/LayoutTests/fast/text/font-face-_javascript_-expected.txt (213435 => 213436)
--- trunk/LayoutTests/fast/text/font-face-_javascript_-expected.txt 2017-03-05 19:14:12 UTC (rev 213435)
+++ trunk/LayoutTests/fast/text/font-face-_javascript_-expected.txt 2017-03-05 20:14:02 UTC (rev 213436)
@@ -16,7 +16,7 @@
PASS new FontFace('family_name', 'url(\'asdf\')', {'weight': 'bolder'}).weight is "bold"
PASS new FontFace('family_name', 'url(\'asdf\')', {'weight': 'lighter'}).weight is "200"
PASS new FontFace('family_name', 'url(\'asdf\')', {'weight': 'inherit'}).weight threw exception SyntaxError (DOM Exception 12): The string did not match the expected pattern..
-PASS new FontFace('family_name', 'url(\'asdf\')', {'stretch': 'stretch_name'}).stretch is "normal"
+PASS new FontFace('family_name', 'url(\'asdf\')', {'stretch': 'ultra-expanded'}).stretch is "ultra-expanded"
PASS new FontFace('family_name', 'url(\'asdf\')', {'unicodeRange': 'U+26'}).unicodeRange is "U+26-26"
PASS new FontFace('family_name', 'url(\'asdf\')', {'unicodeRange': 'U+0-7F'}).unicodeRange is "U+0-7f"
PASS new FontFace('family_name', 'url(\'asdf\')', {'variant': 'variant_name'}).variant threw exception SyntaxError (DOM Exception 12): The string did not match the expected pattern..
@@ -25,7 +25,7 @@
PASS new FontFace('family_name', 'url(\'asdf\')', {'featureSettings': '\'titl\''}).featureSettings is "'titl' 1"
PASS everything.style is "italic"
PASS everything.weight is "bold"
-PASS everything.stretch is "normal"
+PASS everything.stretch is "extra-expanded"
PASS everything.unicodeRange is "U+26-26"
PASS everything.variant is "small-caps"
PASS everything.featureSettings is "'titl' 1"
@@ -32,7 +32,7 @@
PASS everything.family is "other_family_name"
PASS everything.style is "normal"
PASS everything.weight is "300"
-PASS everything.stretch is "normal"
+PASS everything.stretch is "condensed"
PASS everything.unicodeRange is "U+0-7f"
PASS everything.variant is "stacked-fractions"
PASS everything.featureSettings is "'smcp' 1"
Modified: trunk/LayoutTests/fast/text/font-face-_javascript_.html (213435 => 213436)
--- trunk/LayoutTests/fast/text/font-face-_javascript_.html 2017-03-05 19:14:12 UTC (rev 213435)
+++ trunk/LayoutTests/fast/text/font-face-_javascript_.html 2017-03-05 20:14:02 UTC (rev 213436)
@@ -24,7 +24,7 @@
shouldBeEqualToString("new FontFace('family_name', 'url(\\'asdf\\')', {'weight': 'bolder'}).weight", "bold");
shouldBeEqualToString("new FontFace('family_name', 'url(\\'asdf\\')', {'weight': 'lighter'}).weight", "200");
shouldThrow("new FontFace('family_name', 'url(\\'asdf\\')', {'weight': 'inherit'}).weight");
-shouldBeEqualToString("new FontFace('family_name', 'url(\\'asdf\\')', {'stretch': 'stretch_name'}).stretch", "normal");
+shouldBeEqualToString("new FontFace('family_name', 'url(\\'asdf\\')', {'stretch': 'ultra-expanded'}).stretch", "ultra-expanded");
shouldBeEqualToString("new FontFace('family_name', 'url(\\'asdf\\')', {'unicodeRange': 'U+26'}).unicodeRange", "U+26-26");
shouldBeEqualToString("new FontFace('family_name', 'url(\\'asdf\\')', {'unicodeRange': 'U+0-7F'}).unicodeRange", "U+0-7f");
shouldThrow("new FontFace('family_name', 'url(\\'asdf\\')', {'variant': 'variant_name'}).variant");
@@ -32,10 +32,10 @@
shouldBeEqualToString("new FontFace('family_name', 'url(\\'asdf\\')', {'variant': 'small-caps common-ligatures'}).variant", "common-ligatures small-caps");
shouldBeEqualToString("new FontFace('family_name', 'url(\\'asdf\\')', {'featureSettings': '\\'titl\\''}).featureSettings", "'titl' 1");
-var everything = new FontFace('family_name', 'url(\'asdf\')', {'style': 'italic', 'weight': 'bold', 'stretch': 'stretch_name', 'unicodeRange': 'U+26', 'variant': 'small-caps', 'featureSettings': '\'titl\''});
+var everything = new FontFace('family_name', 'url(\'asdf\')', {'style': 'italic', 'weight': 'bold', 'stretch': 'extra-expanded', 'unicodeRange': 'U+26', 'variant': 'small-caps', 'featureSettings': '\'titl\''});
shouldBeEqualToString("everything.style", "italic");
shouldBeEqualToString("everything.weight", "bold");
-shouldBeEqualToString("everything.stretch", "normal");
+shouldBeEqualToString("everything.stretch", "extra-expanded");
shouldBeEqualToString("everything.unicodeRange", "U+26-26");
shouldBeEqualToString("everything.variant", "small-caps");
shouldBeEqualToString("everything.featureSettings", "'titl' 1");
@@ -46,8 +46,8 @@
shouldBeEqualToString("everything.style", "normal");
everything.weight = "300";
shouldBeEqualToString("everything.weight", "300");
-everything.stretch = "other_stretch_name";
-shouldBeEqualToString("everything.stretch", "normal");
+everything.stretch = "condensed";
+shouldBeEqualToString("everything.stretch", "condensed");
everything.unicodeRange = "U+0-7F";
shouldBeEqualToString("everything.unicodeRange", "U+0-7f");
everything.variant = "stacked-fractions";
Modified: trunk/Source/WebCore/CMakeLists.txt (213435 => 213436)
--- trunk/Source/WebCore/CMakeLists.txt 2017-03-05 19:14:12 UTC (rev 213435)
+++ trunk/Source/WebCore/CMakeLists.txt 2017-03-05 20:14:02 UTC (rev 213436)
@@ -2200,6 +2200,7 @@
platform/graphics/FontCascade.cpp
platform/graphics/FontCascadeFonts.cpp
platform/graphics/FontDescription.cpp
+ platform/graphics/FontSelectionAlgorithm.cpp
platform/graphics/FontTaggedSettings.cpp
platform/graphics/FontGenericFamilies.cpp
platform/graphics/FontPlatformData.cpp
Modified: trunk/Source/WebCore/ChangeLog (213435 => 213436)
--- trunk/Source/WebCore/ChangeLog 2017-03-05 19:14:12 UTC (rev 213435)
+++ trunk/Source/WebCore/ChangeLog 2017-03-05 20:14:02 UTC (rev 213436)
@@ -1,3 +1,125 @@
+2017-03-04 Myles C. Maxfield <mmaxfi...@apple.com>
+
+ Update CSSFontSelector's matching algorithm to understand ranges
+ https://bugs.webkit.org/show_bug.cgi?id=168892
+
+ Reviewed by Jon Lee.
+
+ This patch migrates the font selection algorithm out of FontCacheCoreText and into its own file which can be shared
+ among all ports. It then migrates our web font selection algorithm to use it.
+
+ This patch doesn't actually change the parsing rules; it just changes the internal machinery for how fonts get
+ selected. Therefore, this patch simply makes zero-length ranges from the discrete values the parser emits, and passes
+ those zero-length ranges to the range-based font selection routine. This means that this patch doesn't actually
+ change the results of the font selection algorithm.
+
+ One of the inputs to the new font selection algorithm is font-stretch, which previously was not parsed inside
+ @font-face blocks or the CSS Font Loading API. This patch therefore adds parsing support and modifies the existing
+ tests for these pieces to expect parsing to work. Because the font selection algorithm itself is shared between
+ installed fonts and webfonts, this patch doesn't add any additional tests for it (because it is already covered under
+ existing tests).
+
+ No new tests because there is no behavior change.
+
+ * CMakeLists.txt:
+ * WebCore.xcodeproj/project.pbxproj: Add new file for the font selection algorithm.
+ * css/CSSFontFace.cpp:
+ (WebCore::CSSFontFace::calculateStretch): Used on @font-face blocks and the CSS Font Loading API.
+ (WebCore::CSSFontFace::setStretch): Fill out the previously stubbed function.
+ * css/CSSFontFace.h: Add the range member variable to CSSFontFaces.
+ * css/CSSFontFaceSet.cpp:
+ (WebCore::CSSFontFaceSet::ensureLocalFontFacesForFamilyRegistered): Now that we care about font-stretch, we need to
+ look it up for local fonts too. This current approach of an extra FontSelectionValue hanging around with a
+ FontTraitsMask is very ugly and will be removed soon (https://bugs.webkit.org/show_bug.cgi?id=168889 and
+ https://bugs.webkit.org/show_bug.cgi?id=168890).
+ (WebCore::computeFontStretch): Used on @font-face blocks and the CSS Font Loading API.
+ (WebCore::CSSFontFaceSet::matchingFaces): Educate about font-stretch.
+ (WebCore::CSSFontFaceSet::fontFace): Migrate to the new font-selection algorithm.
+ (WebCore::fontFaceComparator): Deleted.
+ * css/CSSFontFaceSet.h:
+ * css/CSSFontSelector.cpp:
+ (WebCore::CSSFontSelector::addFontFaceRule): Educate about font-stretch.
+ (WebCore::CSSFontSelector::fontRangesForFamily): Ditto.
+ * css/FontFace.cpp:
+ (WebCore::FontFace::setStretch): Ditto.
+ (WebCore::FontFace::stretch): Ditto.
+ * css/parser/CSSPropertyParser.cpp:
+ (WebCore::CSSPropertyParser::parseFontFaceDescriptor): Ditto.
+ * platform/graphics/FontCache.h: Ditto.
+ * platform/graphics/FontDescription.h:
+ * platform/graphics/FontSelectionAlgorithm.cpp: Added.
+ (WebCore::FontSelectionAlgorithm::filterCapability):
+ (WebCore::FontSelectionAlgorithm::indexOfBestCapabilities):
+ (WebCore::fontSelectionRequestForTraitsMask):
+ (WebCore::initialFontSelectionCapabilitiesForTraitsMask):
+ (WebCore::fontSelectionCapabilitiesForTraitsMask):
+ * platform/graphics/FontSelectionAlgorithm.h: Added. Taken from FontCacheCoreText.cpp.
+ (WebCore::FontSelectionValue::FontSelectionValue):
+ (WebCore::FontSelectionValue::operator float):
+ (WebCore::FontSelectionValue::rawValue):
+ (WebCore::FontSelectionValue::maximumValue):
+ (WebCore::FontSelectionValue::minimumValue):
+ (WebCore::FontSelectionValue::operator+):
+ (WebCore::FontSelectionValue::operator-):
+ (WebCore::FontSelectionValue::operator*):
+ (WebCore::FontSelectionValue::operator/):
+ (WebCore::FontSelectionValue::operator==):
+ (WebCore::FontSelectionValue::operator!=):
+ (WebCore::FontSelectionValue::operator<):
+ (WebCore::FontSelectionValue::operator<=):
+ (WebCore::FontSelectionValue::operator>):
+ (WebCore::FontSelectionValue::operator>=):
+ (WebCore::FontSelectionRange::isValid):
+ (WebCore::FontSelectionRange::expand):
+ (WebCore::FontSelectionRange::includes):
+ (WebCore::FontSelectionRequest::operator==):
+ (WebCore::FontSelectionRequest::operator!=):
+ (WebCore::FontSelectionRequestKey::FontSelectionRequestKey):
+ (WebCore::FontSelectionRequestKey::isHashTableDeletedValue):
+ (WebCore::FontSelectionRequestKey::operator==):
+ (WebCore::FontSelectionRequestKeyHash::hash):
+ (WebCore::FontSelectionRequestKeyHash::equal):
+ (WebCore::FontSelectionCapabilities::expand):
+ (WebCore::FontSelectionAlgorithm::FontSelectionAlgorithm):
+ (WebCore::FontSelectionAlgorithm::iterateActiveCapabilitiesWithReturn):
+ (WebCore::FontSelectionAlgorithm::iterateActiveCapabilities):
+ * platform/graphics/cocoa/FontCacheCoreText.cpp: Moved to FontSelectionAlgorithm.
+ (WebCore::stretchFromCoreTextTraits):
+ (WebCore::FontDatabase::capabilitiesForFontDescriptor):
+ (WebCore::findClosestFont):
+ (WebCore::calculateFontSelectionRequest):
+ (WebCore::platformFontLookupWithFamily):
+ (WebCore::FontCache::getTraitsInFamily): Deleted.
+ (WebCore::iterateActiveFontsWithReturn): Deleted.
+ (WebCore::iterateActiveFonts): Deleted.
+ (WebCore::findClosestStretch): Deleted.
+ (WebCore::filterStretch): Deleted.
+ (WebCore::findClosestStyle): Deleted.
+ (WebCore::filterStyle): Deleted.
+ (WebCore::findClosestWeight): Deleted.
+ (WebCore::filterWeight): Deleted.
+ (WebCore::computeTargetWeight): Deleted.
+ * platform/text/TextFlags.h: Moved to FontSelectionAlgorithm.
+ (WebCore::FontSelectionValue::FontSelectionValue): Deleted.
+ (WebCore::FontSelectionValue::operator float): Deleted.
+ (WebCore::FontSelectionValue::operator+): Deleted.
+ (WebCore::FontSelectionValue::operator-): Deleted.
+ (WebCore::FontSelectionValue::operator*): Deleted.
+ (WebCore::FontSelectionValue::operator/): Deleted.
+ (WebCore::FontSelectionValue::operator==): Deleted.
+ (WebCore::FontSelectionValue::operator!=): Deleted.
+ (WebCore::FontSelectionValue::operator<): Deleted.
+ (WebCore::FontSelectionValue::operator<=): Deleted.
+ (WebCore::FontSelectionValue::operator>): Deleted.
+ (WebCore::FontSelectionValue::operator>=): Deleted.
+ (WebCore::FontSelectionValue::rawValue): Deleted.
+ (WebCore::FontSelectionValue::maximumValue): Deleted.
+ (WebCore::FontSelectionValue::minimumValue): Deleted.
+ (WebCore::FontSelectionRange::isValid): Deleted.
+ (WebCore::FontSelectionRange::expand): Deleted.
+ (WebCore::FontSelectionRange::includes): Deleted.
+ (WebCore::FontSelectionCapabilities::expand): Deleted.
+
2017-03-05 Simon Fraser <simon.fra...@apple.com>
Make some RenderLayer tree traversal in RenderLayerBacking more generic
Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (213435 => 213436)
--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2017-03-05 19:14:12 UTC (rev 213435)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2017-03-05 20:14:02 UTC (rev 213436)
@@ -5747,6 +5747,8 @@
C280833F1C6DC26F001451B6 /* JSFontFace.h in Headers */ = {isa = PBXBuildFile; fileRef = C280833E1C6DC22C001451B6 /* JSFontFace.h */; };
C28083401C6DC275001451B6 /* JSFontFace.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C280833D1C6DC22C001451B6 /* JSFontFace.cpp */; };
C28083421C6DC96A001451B6 /* JSFontFaceCustom.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C28083411C6DC96A001451B6 /* JSFontFaceCustom.cpp */; };
+ C2AB0AF61E6B3C6C001348C5 /* FontSelectionAlgorithm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2AB0AF41E6B3C6C001348C5 /* FontSelectionAlgorithm.cpp */; };
+ C2AB0AF71E6B3C6C001348C5 /* FontSelectionAlgorithm.h in Headers */ = {isa = PBXBuildFile; fileRef = C2AB0AF51E6B3C6C001348C5 /* FontSelectionAlgorithm.h */; settings = {ATTRIBUTES = (Private, ); }; };
C2E1F43F1D6254E10094625C /* BreakLines.h in Headers */ = {isa = PBXBuildFile; fileRef = BCEA4816097D93020094C9E4 /* BreakLines.h */; settings = {ATTRIBUTES = (Private, ); }; };
C2F4E78A1E45BEA1006D7105 /* ComplexTextController.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2F4E7881E45AEDF006D7105 /* ComplexTextController.cpp */; };
C2F4E78C1E45C3EF006D7105 /* ComplexTextController.h in Headers */ = {isa = PBXBuildFile; fileRef = C2F4E7891E45AEDF006D7105 /* ComplexTextController.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -13873,6 +13875,8 @@
C280833D1C6DC22C001451B6 /* JSFontFace.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSFontFace.cpp; sourceTree = "<group>"; };
C280833E1C6DC22C001451B6 /* JSFontFace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSFontFace.h; sourceTree = "<group>"; };
C28083411C6DC96A001451B6 /* JSFontFaceCustom.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSFontFaceCustom.cpp; sourceTree = "<group>"; };
+ C2AB0AF41E6B3C6C001348C5 /* FontSelectionAlgorithm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FontSelectionAlgorithm.cpp; sourceTree = "<group>"; };
+ C2AB0AF51E6B3C6C001348C5 /* FontSelectionAlgorithm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FontSelectionAlgorithm.h; sourceTree = "<group>"; };
C2F4E7881E45AEDF006D7105 /* ComplexTextController.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ComplexTextController.cpp; sourceTree = "<group>"; };
C2F4E7891E45AEDF006D7105 /* ComplexTextController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ComplexTextController.h; sourceTree = "<group>"; };
C330A22113EC196B0000B45B /* ColorChooser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ColorChooser.h; sourceTree = "<group>"; };
@@ -22369,6 +22373,8 @@
501BAAA813950E2C00F7ACEB /* WindRule.h */,
379919941200DDF400EA041C /* WOFFFileFormat.cpp */,
379919951200DDF400EA041C /* WOFFFileFormat.h */,
+ C2AB0AF41E6B3C6C001348C5 /* FontSelectionAlgorithm.cpp */,
+ C2AB0AF51E6B3C6C001348C5 /* FontSelectionAlgorithm.h */,
);
path = graphics;
sourceTree = "<group>";
@@ -27639,6 +27645,7 @@
854FE7350A2297BE0058D7AD /* NodeIterator.h in Headers */,
A818721B0977D3C0005826D9 /* NodeList.h in Headers */,
63189AE30E83A33300012E41 /* NodeRareData.h in Headers */,
+ C2AB0AF71E6B3C6C001348C5 /* FontSelectionAlgorithm.h in Headers */,
63D7B32D0E78CD3F00F7617C /* NodeRenderStyle.h in Headers */,
E43105BB16750F1600DB2FB8 /* NodeTraversal.h in Headers */,
9382AAB40D8C386100F357A6 /* NodeWithIndex.h in Headers */,
@@ -29506,6 +29513,7 @@
1A8A645B1D19FCFC00D0E00F /* ApplePayShippingContactSelectedEvent.cpp in Sources */,
1A8A645F1D19FCFC00D0E00F /* ApplePayShippingMethodSelectedEvent.cpp in Sources */,
1A8A64621D19FCFC00D0E00F /* ApplePayValidateMerchantEvent.cpp in Sources */,
+ C2AB0AF61E6B3C6C001348C5 /* FontSelectionAlgorithm.cpp in Sources */,
1A8F6BBC0DB55CDC001DB794 /* ApplicationCache.cpp in Sources */,
1A8F6BBE0DB55CDC001DB794 /* ApplicationCacheGroup.cpp in Sources */,
24F54EAC101FE914000AE741 /* ApplicationCacheHost.cpp in Sources */,
Modified: trunk/Source/WebCore/css/CSSFontFace.cpp (213435 => 213436)
--- trunk/Source/WebCore/css/CSSFontFace.cpp 2017-03-05 19:14:12 UTC (rev 213435)
+++ trunk/Source/WebCore/css/CSSFontFace.cpp 2017-03-05 20:14:02 UTC (rev 213436)
@@ -194,6 +194,46 @@
return FontWeight400Mask;
}
+std::optional<FontSelectionValue> CSSFontFace::calculateStretch(CSSValue& stretch)
+{
+ if (!is<CSSPrimitiveValue>(stretch))
+ return std::nullopt;
+
+ const auto& primitiveValue = downcast<CSSPrimitiveValue>(stretch);
+ if (primitiveValue.isPercentage() || primitiveValue.isNumber()) {
+ auto value = primitiveValue.floatValue();
+ if (value < static_cast<float>(FontSelectionValue::minimumValue()))
+ return FontSelectionValue::minimumValue();
+ if (value > static_cast<float>(FontSelectionValue::maximumValue()))
+ return FontSelectionValue::maximumValue();
+ return FontSelectionValue(value);
+ }
+
+ switch (primitiveValue.valueID()) {
+ case CSSValueUltraCondensed:
+ return FontSelectionValue(50);
+ case CSSValueExtraCondensed:
+ return FontSelectionValue(62.5f);
+ case CSSValueCondensed:
+ return FontSelectionValue(75);
+ case CSSValueSemiCondensed:
+ return FontSelectionValue(87.5f);
+ case CSSValueNormal:
+ return FontSelectionValue(100);
+ case CSSValueSemiExpanded:
+ return FontSelectionValue(112.5f);
+ case CSSValueExpanded:
+ return FontSelectionValue(125);
+ case CSSValueExtraExpanded:
+ return FontSelectionValue(150);
+ case CSSValueUltraExpanded:
+ return FontSelectionValue(200);
+ default:
+ ASSERT_NOT_REACHED();
+ return std::nullopt;
+ }
+}
+
bool CSSFontFace::setWeight(CSSValue& weight)
{
if (auto mask = calculateWeightMask(weight)) {
@@ -212,6 +252,24 @@
return false;
}
+bool CSSFontFace::setStretch(CSSValue& stretch)
+{
+ if (auto parsedStretch = calculateStretch(stretch)) {
+ m_stretch = FontSelectionRange(parsedStretch.value(), parsedStretch.value());
+
+ if (m_cssConnection)
+ m_cssConnection->mutableProperties().setProperty(CSSPropertyFontStretch, &stretch);
+
+ iterateClients(m_clients, [&](Client& client) {
+ client.fontPropertyChanged(*this);
+ });
+
+ return true;
+ }
+
+ return false;
+}
+
bool CSSFontFace::setUnicodeRange(CSSValue& unicodeRange)
{
if (!is<CSSValueList>(unicodeRange))
Modified: trunk/Source/WebCore/css/CSSFontFace.h (213435 => 213436)
--- trunk/Source/WebCore/css/CSSFontFace.h 2017-03-05 19:14:12 UTC (rev 213435)
+++ trunk/Source/WebCore/css/CSSFontFace.h 2017-03-05 20:14:02 UTC (rev 213436)
@@ -26,6 +26,7 @@
#pragma once
#include "CSSFontFaceRule.h"
+#include "FontSelectionAlgorithm.h"
#include "FontTaggedSettings.h"
#include "TextFlags.h"
#include "Timer.h"
@@ -65,6 +66,7 @@
bool setFamilies(CSSValue&);
bool setStyle(CSSValue&);
bool setWeight(CSSValue&);
+ bool setStretch(CSSValue&);
bool setUnicodeRange(CSSValue&);
bool setVariantLigatures(CSSValue&);
bool setVariantPosition(CSSValue&);
@@ -78,17 +80,21 @@
struct UnicodeRange;
const CSSValueList* families() const { return m_families.get(); }
FontTraitsMask traitsMask() const { return m_traitsMask; }
+ FontSelectionRange stretch() const { return m_stretch; }
const Vector<UnicodeRange>& ranges() const { return m_ranges; }
const FontFeatureSettings& featureSettings() const { return m_featureSettings; }
const FontVariantSettings& variantSettings() const { return m_variantSettings; }
void setVariantSettings(const FontVariantSettings& variantSettings) { m_variantSettings = variantSettings; }
void setTraitsMask(FontTraitsMask traitsMask) { m_traitsMask = traitsMask; }
+ void setStretch(FontSelectionRange stretch) { m_stretch = stretch; }
bool isLocalFallback() const { return m_isLocalFallback; }
Status status() const { return m_status; }
StyleRuleFontFace* cssConnection() const { return m_cssConnection.get(); }
+ FontSelectionCapabilities fontSelectionCapabilities() const { return fontSelectionCapabilitiesForTraitsMask(m_traitsMask, m_stretch); }
static std::optional<FontTraitsMask> calculateStyleMask(CSSValue& style);
static std::optional<FontTraitsMask> calculateWeightMask(CSSValue& weight);
+ static std::optional<FontSelectionValue> calculateStretch(CSSValue& stretch);
class Client;
void addClient(Client&);
@@ -173,6 +179,7 @@
HashSet<Client*> m_clients;
WeakPtr<FontFace> m_wrapper;
Status m_status { Status::Pending };
+ FontSelectionRange m_stretch { FontSelectionValue(100), FontSelectionValue(100) };
bool m_isLocalFallback { false };
bool m_sourcesPopulated { false };
bool m_mayBePurged { true };
Modified: trunk/Source/WebCore/css/CSSFontFaceSet.cpp (213435 => 213436)
--- trunk/Source/WebCore/css/CSSFontFaceSet.cpp 2017-03-05 19:14:12 UTC (rev 213435)
+++ trunk/Source/WebCore/css/CSSFontFaceSet.cpp 2017-03-05 20:14:02 UTC (rev 213436)
@@ -101,18 +101,19 @@
if (m_locallyInstalledFacesLookupTable.contains(familyName))
return;
- Vector<FontTraitsMask> traitsMasks = FontCache::singleton().getTraitsInFamily(familyName);
- if (traitsMasks.isEmpty())
+ Vector<FontCache::TraitsAndStretch> traitsAndStretch = FontCache::singleton().getTraitsAndStretchInFamily(familyName);
+ if (traitsAndStretch.isEmpty())
return;
Vector<Ref<CSSFontFace>> faces;
- for (auto mask : traitsMasks) {
+ for (auto item : traitsAndStretch) {
Ref<CSSFontFace> face = CSSFontFace::create(nullptr, nullptr, nullptr, true);
Ref<CSSValueList> familyList = CSSValueList::createCommaSeparated();
familyList->append(CSSValuePool::singleton().createFontFamilyValue(familyName));
face->setFamilies(familyList.get());
- face->setTraitsMask(mask);
+ face->setTraitsMask(item.traits);
+ face->setStretch(item.stretch);
face->adoptSource(std::make_unique<CSSFontFaceSource>(face.get(), familyName));
ASSERT(!face->allSourcesFailed());
faces.append(WTFMove(face));
@@ -311,6 +312,17 @@
return static_cast<FontTraitsMask>(static_cast<unsigned>(styleMask) | static_cast<unsigned>(weightMask));
}
+static std::optional<FontSelectionValue> computeFontStretch(MutableStyleProperties& style)
+{
+ RefPtr<CSSValue> stretchValue = style.getPropertyCSSValue(CSSPropertyFontStretch).get();
+ if (!stretchValue)
+ stretchValue = CSSValuePool::singleton().createIdentifierValue(CSSValueNormal).ptr();
+
+ if (auto stretchOptional = CSSFontFace::calculateStretch(*stretchValue))
+ return stretchOptional.value();
+ return std::nullopt;
+}
+
static HashSet<UChar32> codePointsFromString(StringView stringView)
{
HashSet<UChar32> result;
@@ -340,6 +352,12 @@
else
return Exception { SYNTAX_ERR };
+ FontSelectionValue stretch;
+ if (auto stretchOptional = computeFontStretch(style.get()))
+ stretch = stretchOptional.value();
+ else
+ return Exception { SYNTAX_ERR };
+
auto family = style->getPropertyCSSValue(CSSPropertyFontFamily);
if (!is<CSSValueList>(family.get()))
return Exception { SYNTAX_ERR };
@@ -359,7 +377,7 @@
for (auto codePoint : codePointsFromString(string)) {
bool found = false;
for (auto& family : familyOrder) {
- auto* faces = fontFace(fontTraitsMask, family);
+ auto* faces = fontFace(fontTraitsMask, stretch, family);
if (!faces)
continue;
for (auto& constituentFace : faces->constituentFaces()) {
@@ -394,67 +412,8 @@
return true;
}
-static bool fontFaceComparator(FontTraitsMask desiredTraitsMaskForComparison, const CSSFontFace& first, const CSSFontFace& second)
+CSSSegmentedFontFace* CSSFontFaceSet::fontFace(FontTraitsMask traitsMask, FontSelectionValue stretch, const AtomicString& family)
{
- FontTraitsMask firstTraitsMask = first.traitsMask();
- FontTraitsMask secondTraitsMask = second.traitsMask();
-
- bool firstHasDesiredStyle = firstTraitsMask & desiredTraitsMaskForComparison & FontStyleMask;
- bool secondHasDesiredStyle = secondTraitsMask & desiredTraitsMaskForComparison & FontStyleMask;
-
- if (firstHasDesiredStyle != secondHasDesiredStyle)
- return firstHasDesiredStyle;
-
- if ((desiredTraitsMaskForComparison & FontStyleItalicMask) && !first.isLocalFallback() && !second.isLocalFallback()) {
- // Prefer a font that has indicated that it can only support italics to a font that claims to support
- // all styles. The specialized font is more likely to be the one the author wants used.
- bool firstRequiresItalics = (firstTraitsMask & FontStyleItalicMask) && !(firstTraitsMask & FontStyleNormalMask);
- bool secondRequiresItalics = (secondTraitsMask & FontStyleItalicMask) && !(secondTraitsMask & FontStyleNormalMask);
- if (firstRequiresItalics != secondRequiresItalics)
- return firstRequiresItalics;
- }
-
- if (secondTraitsMask & desiredTraitsMaskForComparison & FontWeightMask)
- return false;
- if (firstTraitsMask & desiredTraitsMaskForComparison & FontWeightMask)
- return true;
-
- // http://www.w3.org/TR/2011/WD-css3-fonts-20111004/#font-matching-algorithm says :
- // - If the desired weight is less than 400, weights below the desired weight are checked in descending order followed by weights above the desired weight in ascending order until a match is found.
- // - If the desired weight is greater than 500, weights above the desired weight are checked in ascending order followed by weights below the desired weight in descending order until a match is found.
- // - If the desired weight is 400, 500 is checked first and then the rule for desired weights less than 400 is used.
- // - If the desired weight is 500, 400 is checked first and then the rule for desired weights less than 400 is used.
-
- static const unsigned fallbackRuleSets = 9;
- static const unsigned rulesPerSet = 8;
- static const FontTraitsMask weightFallbackRuleSets[fallbackRuleSets][rulesPerSet] = {
- { FontWeight200Mask, FontWeight300Mask, FontWeight400Mask, FontWeight500Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
- { FontWeight100Mask, FontWeight300Mask, FontWeight400Mask, FontWeight500Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
- { FontWeight200Mask, FontWeight100Mask, FontWeight400Mask, FontWeight500Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
- { FontWeight500Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
- { FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
- { FontWeight700Mask, FontWeight800Mask, FontWeight900Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask },
- { FontWeight800Mask, FontWeight900Mask, FontWeight600Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask },
- { FontWeight900Mask, FontWeight700Mask, FontWeight600Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask },
- { FontWeight800Mask, FontWeight700Mask, FontWeight600Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask }
- };
-
- unsigned ruleSetIndex = 0;
- for (; !(desiredTraitsMaskForComparison & (1 << (FontWeight100Bit + ruleSetIndex))); ruleSetIndex++) { }
-
- const FontTraitsMask* weightFallbackRule = weightFallbackRuleSets[ruleSetIndex];
- for (unsigned i = 0; i < rulesPerSet; ++i) {
- if (secondTraitsMask & weightFallbackRule[i])
- return false;
- if (firstTraitsMask & weightFallbackRule[i])
- return true;
- }
-
- return false;
-}
-
-CSSSegmentedFontFace* CSSFontFaceSet::fontFace(FontTraitsMask traitsMask, const AtomicString& family)
-{
auto iterator = m_facesLookupTable.find(family);
if (iterator == m_facesLookupTable.end())
return nullptr;
@@ -487,12 +446,40 @@
}
}
- std::stable_sort(candidateFontFaces.begin(), candidateFontFaces.end(), [traitsMask](const CSSFontFace& first, const CSSFontFace& second) {
- return fontFaceComparator(traitsMask, first, second);
- });
- for (auto& candidate : candidateFontFaces)
- face->appendFontFace(candidate.get());
+ if (!candidateFontFaces.isEmpty()) {
+ Vector<FontSelectionCapabilities> capabilities;
+ capabilities.reserveInitialCapacity(candidateFontFaces.size());
+ for (auto& face : candidateFontFaces)
+ capabilities.uncheckedAppend(face.get().fontSelectionCapabilities());
+ FontSelectionAlgorithm fontSelectionAlgorithm(fontSelectionRequestForTraitsMask(traitsMask, stretch), capabilities);
+ std::stable_sort(candidateFontFaces.begin(), candidateFontFaces.end(), [&fontSelectionAlgorithm](const CSSFontFace& first, const CSSFontFace& second) {
+ auto firstCapabilities = first.fontSelectionCapabilities();
+ auto secondCapabilities = second.fontSelectionCapabilities();
+ auto stretchDistanceFirst = fontSelectionAlgorithm.stretchDistance(firstCapabilities).distance;
+ auto stretchDistanceSecond = fontSelectionAlgorithm.stretchDistance(secondCapabilities).distance;
+ if (stretchDistanceFirst < stretchDistanceSecond)
+ return true;
+ if (stretchDistanceFirst > stretchDistanceSecond)
+ return false;
+
+ auto styleDistanceFirst = fontSelectionAlgorithm.styleDistance(firstCapabilities).distance;
+ auto styleDistanceSecond = fontSelectionAlgorithm.styleDistance(secondCapabilities).distance;
+ if (styleDistanceFirst < styleDistanceSecond)
+ return true;
+ if (styleDistanceFirst > styleDistanceSecond)
+ return false;
+
+ auto weightDistanceFirst = fontSelectionAlgorithm.weightDistance(firstCapabilities).distance;
+ auto weightDistanceSecond = fontSelectionAlgorithm.weightDistance(secondCapabilities).distance;
+ if (weightDistanceFirst < weightDistanceSecond)
+ return true;
+ return false;
+ });
+ for (auto& candidate : candidateFontFaces)
+ face->appendFontFace(candidate.get());
+ }
+
return face.get();
}
Modified: trunk/Source/WebCore/css/CSSFontFaceSet.h (213435 => 213436)
--- trunk/Source/WebCore/css/CSSFontFaceSet.h 2017-03-05 19:14:12 UTC (rev 213435)
+++ trunk/Source/WebCore/css/CSSFontFaceSet.h 2017-03-05 20:14:02 UTC (rev 213436)
@@ -67,7 +67,7 @@
ExceptionOr<bool> check(const String& font, const String& text);
- CSSSegmentedFontFace* fontFace(FontTraitsMask, const AtomicString& family);
+ CSSSegmentedFontFace* fontFace(FontTraitsMask, FontSelectionValue stretch, const AtomicString& family);
enum class Status { Loading, Loaded };
Status status() const { return m_status; }
Modified: trunk/Source/WebCore/css/CSSFontSelector.cpp (213435 => 213436)
--- trunk/Source/WebCore/css/CSSFontSelector.cpp 2017-03-05 19:14:12 UTC (rev 213435)
+++ trunk/Source/WebCore/css/CSSFontSelector.cpp 2017-03-05 20:14:02 UTC (rev 213436)
@@ -145,6 +145,7 @@
RefPtr<CSSValue> fontFamily = style.getPropertyCSSValue(CSSPropertyFontFamily);
RefPtr<CSSValue> fontStyle = style.getPropertyCSSValue(CSSPropertyFontStyle);
RefPtr<CSSValue> fontWeight = style.getPropertyCSSValue(CSSPropertyFontWeight);
+ RefPtr<CSSValue> fontStretch = style.getPropertyCSSValue(CSSPropertyFontStretch);
RefPtr<CSSValue> src = ""
RefPtr<CSSValue> unicodeRange = style.getPropertyCSSValue(CSSPropertyUnicodeRange);
RefPtr<CSSValue> featureSettings = style.getPropertyCSSValue(CSSPropertyFontFeatureSettings);
@@ -167,6 +168,9 @@
if (!fontWeight)
fontWeight = CSSValuePool::singleton().createIdentifierValue(CSSValueNormal);
+ if (!fontStretch)
+ fontStretch = CSSValuePool::singleton().createIdentifierValue(CSSValueNormal);
+
CSSValueList* rangeList = downcast<CSSValueList>(unicodeRange.get());
CSSValueList& srcList = downcast<CSSValueList>(*src);
@@ -182,6 +186,8 @@
return;
if (!fontFace->setWeight(*fontWeight))
return;
+ if (!fontFace->setStretch(*fontStretch))
+ return;
if (rangeList && !fontFace->setUnicodeRange(*rangeList))
return;
if (variantLigatures && !fontFace->setVariantLigatures(*variantLigatures))
@@ -295,7 +301,7 @@
bool resolveGenericFamilyFirst = familyName == standardFamily;
AtomicString familyForLookup = resolveGenericFamilyFirst ? resolveGenericFamily(m_document, fontDescription, familyName) : familyName;
- auto* face = m_cssFontFaceSet->fontFace(fontDescription.traitsMask(), familyForLookup);
+ auto* face = m_cssFontFaceSet->fontFace(fontDescription.traitsMask(), fontDescription.stretch(), familyForLookup);
if (!face) {
if (!resolveGenericFamilyFirst)
familyForLookup = resolveGenericFamily(m_document, fontDescription, familyName);
Modified: trunk/Source/WebCore/css/FontFace.cpp (213435 => 213436)
--- trunk/Source/WebCore/css/FontFace.cpp 2017-03-05 19:14:12 UTC (rev 213435)
+++ trunk/Source/WebCore/css/FontFace.cpp 2017-03-05 20:14:02 UTC (rev 213436)
@@ -184,9 +184,16 @@
return { };
}
-ExceptionOr<void> FontFace::setStretch(const String&)
+ExceptionOr<void> FontFace::setStretch(const String& stretch)
{
- // We don't support font-stretch. Swallow the call.
+ if (stretch.isEmpty())
+ return Exception { SYNTAX_ERR };
+
+ bool success = false;
+ if (auto value = parseString(stretch, CSSPropertyFontStretch))
+ success = m_backing->setStretch(*value);
+ if (!success)
+ return Exception { SYNTAX_ERR };
return { };
}
@@ -318,7 +325,33 @@
String FontFace::stretch() const
{
- return ASCIILiteral("normal");
+ m_backing->updateStyleIfNeeded();
+ auto stretch = m_backing->stretch();
+
+ auto rangeIsSingleValue = [](FontSelectionRange range, FontSelectionValue value) -> bool {
+ return range.minimum == value && range.maximum == value;
+ };
+
+ if (rangeIsSingleValue(stretch, FontSelectionValue(50)))
+ return ASCIILiteral("ultra-condensed");
+ if (rangeIsSingleValue(stretch, FontSelectionValue(62.5f)))
+ return ASCIILiteral("extra-condensed");
+ if (rangeIsSingleValue(stretch, FontSelectionValue(75)))
+ return ASCIILiteral("condensed");
+ if (rangeIsSingleValue(stretch, FontSelectionValue(87.5f)))
+ return ASCIILiteral("semi-condensed");
+ if (rangeIsSingleValue(stretch, FontSelectionValue(100)))
+ return ASCIILiteral("normal");
+ if (rangeIsSingleValue(stretch, FontSelectionValue(112.5f)))
+ return ASCIILiteral("semi-expanded");
+ if (rangeIsSingleValue(stretch, FontSelectionValue(125)))
+ return ASCIILiteral("expanded");
+ if (rangeIsSingleValue(stretch, FontSelectionValue(150)))
+ return ASCIILiteral("extra-expanded");
+ if (rangeIsSingleValue(stretch, FontSelectionValue(200)))
+ return ASCIILiteral("ultra-expanded");
+
+ return String::format("%f-%f", static_cast<float>(stretch.minimum), static_cast<float>(stretch.maximum));
}
String FontFace::unicodeRange() const
Modified: trunk/Source/WebCore/css/parser/CSSPropertyParser.cpp (213435 => 213436)
--- trunk/Source/WebCore/css/parser/CSSPropertyParser.cpp 2017-03-05 19:14:12 UTC (rev 213435)
+++ trunk/Source/WebCore/css/parser/CSSPropertyParser.cpp 2017-03-05 20:14:02 UTC (rev 213436)
@@ -4185,9 +4185,7 @@
parsedValue = consumeFontFaceUnicodeRange(m_range);
break;
case CSSPropertyFontStretch:
- // FIXME: Implement this.
- m_range.consumeIncludingWhitespace();
- parsedValue = CSSValuePool::singleton().createIdentifierValue(CSSValueNormal);
+ parsedValue = consumeFontStretch(m_range);
break;
case CSSPropertyFontStyle: {
CSSValueID id = m_range.consumeIncludingWhitespace().id();
Modified: trunk/Source/WebCore/platform/graphics/FontCache.h (213435 => 213436)
--- trunk/Source/WebCore/platform/graphics/FontCache.h 2017-03-05 19:14:12 UTC (rev 213435)
+++ trunk/Source/WebCore/platform/graphics/FontCache.h 2017-03-05 20:14:02 UTC (rev 213436)
@@ -203,7 +203,11 @@
// This function exists so CSSFontSelector can have a unified notion of preinstalled fonts and @font-face.
// It comes into play when you create an @font-face which shares a family name as a preinstalled font.
- Vector<FontTraitsMask> getTraitsInFamily(const AtomicString&);
+ struct TraitsAndStretch {
+ FontTraitsMask traits;
+ FontSelectionRange stretch;
+ };
+ Vector<TraitsAndStretch> getTraitsAndStretchInFamily(const AtomicString&);
WEBCORE_EXPORT RefPtr<Font> fontForFamily(const FontDescription&, const AtomicString&, const FontFeatureSettings* fontFaceFeatures = nullptr, const FontVariantSettings* fontFaceVariantSettings = nullptr, bool checkingAlternateName = false);
WEBCORE_EXPORT Ref<Font> lastResortFallbackFont(const FontDescription&);
Modified: trunk/Source/WebCore/platform/graphics/FontDescription.h (213435 => 213436)
--- trunk/Source/WebCore/platform/graphics/FontDescription.h 2017-03-05 19:14:12 UTC (rev 213435)
+++ trunk/Source/WebCore/platform/graphics/FontDescription.h 2017-03-05 20:14:02 UTC (rev 213436)
@@ -26,6 +26,7 @@
#define FontDescription_h
#include "CSSValueKeywords.h"
+#include "FontSelectionAlgorithm.h"
#include "FontTaggedSettings.h"
#include "TextFlags.h"
#include "WebKitFontFamilyNames.h"
Added: trunk/Source/WebCore/platform/graphics/FontSelectionAlgorithm.cpp (0 => 213436)
--- trunk/Source/WebCore/platform/graphics/FontSelectionAlgorithm.cpp (rev 0)
+++ trunk/Source/WebCore/platform/graphics/FontSelectionAlgorithm.cpp 2017-03-05 20:14:02 UTC (rev 213436)
@@ -0,0 +1,239 @@
+/*
+ * 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. 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 INC. 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.
+ */
+
+#include "config.h"
+#include "FontSelectionAlgorithm.h"
+
+namespace WebCore {
+
+auto FontSelectionAlgorithm::stretchDistance(FontSelectionCapabilities capabilities) const -> DistanceResult
+{
+ auto width = capabilities.width;
+ ASSERT(width.isValid());
+ if (width.includes(m_request.width))
+ return { FontSelectionValue(), m_request.width };
+
+ if (m_request.width >= FontSelectionValue(100)) {
+ if (width.minimum > m_request.width)
+ return { width.minimum - m_request.width, width.minimum };
+ ASSERT(width.maximum < m_request.width);
+ auto threshold = std::max(m_request.width, m_capabilitiesBounds.width.maximum);
+ return { threshold - width.maximum, width.maximum };
+ }
+
+ if (width.maximum < m_request.width)
+ return { m_request.width - width.maximum, width.maximum };
+ ASSERT(width.minimum > m_request.width);
+ auto threshold = std::min(m_request.width, m_capabilitiesBounds.width.minimum);
+ return { width.minimum - threshold, width.minimum };
+}
+
+auto FontSelectionAlgorithm::styleDistance(FontSelectionCapabilities capabilities) const -> DistanceResult
+{
+ auto slope = capabilities.slope;
+ ASSERT(slope.isValid());
+ if (slope.includes(m_request.slope))
+ return { FontSelectionValue(), m_request.slope };
+
+ if (m_request.slope >= italicThreshold()) {
+ if (slope.minimum > m_request.slope)
+ return { slope.minimum - m_request.slope, slope.minimum };
+ ASSERT(m_request.slope > slope.maximum);
+ auto threshold = std::max(m_request.slope, m_capabilitiesBounds.slope.maximum);
+ return { threshold - slope.maximum, slope.maximum };
+ }
+
+ if (m_request.slope >= FontSelectionValue()) {
+ if (slope.maximum >= FontSelectionValue() && slope.maximum < m_request.slope)
+ return { m_request.slope - slope.maximum, slope.maximum };
+ if (slope.minimum > m_request.slope)
+ return { slope.minimum, slope.minimum };
+ ASSERT(slope.maximum < FontSelectionValue());
+ auto threshold = std::max(m_request.slope, m_capabilitiesBounds.slope.maximum);
+ return { threshold - slope.maximum, slope.maximum };
+ }
+
+ if (m_request.slope > -italicThreshold()) {
+ if (slope.minimum > m_request.slope && slope.minimum <= FontSelectionValue())
+ return { slope.minimum - m_request.slope, slope.minimum };
+ if (slope.maximum < m_request.slope)
+ return { -slope.maximum, slope.maximum };
+ ASSERT(slope.minimum > FontSelectionValue());
+ auto threshold = std::min(m_request.slope, m_capabilitiesBounds.slope.minimum);
+ return { slope.minimum - threshold, slope.minimum };
+ }
+
+ if (slope.maximum < m_request.slope)
+ return { m_request.slope - slope.maximum, slope.maximum };
+ ASSERT(slope.minimum > m_request.slope);
+ auto threshold = std::min(m_request.slope, m_capabilitiesBounds.slope.minimum);
+ return { slope.minimum - threshold, slope.minimum };
+}
+
+auto FontSelectionAlgorithm::weightDistance(FontSelectionCapabilities capabilities) const -> DistanceResult
+{
+ auto weight = capabilities.weight;
+ ASSERT(weight.isValid());
+ if (weight.includes(m_request.weight))
+ return { FontSelectionValue(), m_request.weight };
+
+ // The spec states: "If the desired weight is 400, 500 is checked first ... If the desired weight is 500, 400 is checked first"
+ FontSelectionValue offset(1);
+ if (m_request.weight == FontSelectionValue(400) && weight.includes(FontSelectionValue(500)))
+ return { offset, FontSelectionValue(500) };
+ if (m_request.weight == FontSelectionValue(500) && weight.includes(FontSelectionValue(400)))
+ return { offset, FontSelectionValue(400) };
+
+ if (m_request.weight <= weightSearchThreshold()) {
+ if (weight.maximum < m_request.weight)
+ return { m_request.weight - weight.maximum + offset, weight.maximum };
+ ASSERT(weight.minimum > m_request.weight);
+ auto threshold = std::min(m_request.weight, m_capabilitiesBounds.weight.minimum);
+ return { weight.minimum - threshold + offset, weight.minimum };
+ }
+
+ if (weight.minimum > m_request.weight)
+ return { weight.minimum - m_request.weight + offset, weight.minimum };
+ ASSERT(weight.maximum < m_request.weight);
+ auto threshold = std::max(m_request.weight, m_capabilitiesBounds.weight.maximum);
+ return { threshold - weight.maximum + offset, weight.maximum };
+}
+
+void FontSelectionAlgorithm::filterCapability(DistanceResult(FontSelectionAlgorithm::*computeDistance)(FontSelectionCapabilities) const, FontSelectionRange FontSelectionCapabilities::*inclusionRange)
+{
+ std::optional<FontSelectionValue> smallestDistance;
+ FontSelectionValue closestValue;
+ iterateActiveCapabilities([&](FontSelectionCapabilities capabilities, size_t) {
+ auto distanceResult = (this->*computeDistance)(capabilities);
+ if (!smallestDistance || distanceResult.distance < smallestDistance.value()) {
+ smallestDistance = distanceResult.distance;
+ closestValue = distanceResult.value;
+ }
+ });
+ ASSERT(smallestDistance);
+ iterateActiveCapabilities([&](auto& capabilities, size_t i) {
+ if (!(capabilities.*inclusionRange).includes(closestValue))
+ m_filter[i] = false;
+ });
+}
+
+size_t FontSelectionAlgorithm::indexOfBestCapabilities()
+{
+ filterCapability(&FontSelectionAlgorithm::stretchDistance, &FontSelectionCapabilities::width);
+ filterCapability(&FontSelectionAlgorithm::styleDistance, &FontSelectionCapabilities::slope);
+ filterCapability(&FontSelectionAlgorithm::weightDistance, &FontSelectionCapabilities::weight);
+
+ auto result = iterateActiveCapabilitiesWithReturn<size_t>([](FontSelectionCapabilities, size_t i) {
+ return i;
+ });
+ ASSERT(result);
+ return result.value_or(0);
+}
+
+FontSelectionRequest fontSelectionRequestForTraitsMask(FontTraitsMask traitsMask, FontSelectionValue stretch)
+{
+ FontSelectionRequest result;
+ if (traitsMask & FontWeight100Mask)
+ result.weight = FontSelectionValue(100);
+ else if (traitsMask & FontWeight200Mask)
+ result.weight = FontSelectionValue(200);
+ else if (traitsMask & FontWeight300Mask)
+ result.weight = FontSelectionValue(300);
+ else if (traitsMask & FontWeight400Mask)
+ result.weight = FontSelectionValue(400);
+ else if (traitsMask & FontWeight500Mask)
+ result.weight = FontSelectionValue(500);
+ else if (traitsMask & FontWeight600Mask)
+ result.weight = FontSelectionValue(600);
+ else if (traitsMask & FontWeight700Mask)
+ result.weight = FontSelectionValue(700);
+ else if (traitsMask & FontWeight800Mask)
+ result.weight = FontSelectionValue(800);
+ else {
+ ASSERT(traitsMask & FontWeight900Mask);
+ result.weight = FontSelectionValue(900);
+ }
+
+ result.width = stretch;
+
+ if (traitsMask & FontStyleNormalMask)
+ result.slope = FontSelectionValue();
+ else {
+ ASSERT(traitsMask & FontStyleItalicMask);
+ result.slope = italicThreshold();
+ }
+
+ return result;
+}
+
+static FontSelectionCapabilities initialFontSelectionCapabilitiesForTraitsMask(FontTraitsMask traitsMask)
+{
+ FontSelectionCapabilities result;
+ if (traitsMask & FontWeight100Mask)
+ result.weight = { FontSelectionValue(100), FontSelectionValue(100) };
+ else if (traitsMask & FontWeight200Mask)
+ result.weight = { FontSelectionValue(200), FontSelectionValue(200) };
+ else if (traitsMask & FontWeight300Mask)
+ result.weight = { FontSelectionValue(300), FontSelectionValue(300) };
+ else if (traitsMask & FontWeight400Mask)
+ result.weight = { FontSelectionValue(400), FontSelectionValue(400) };
+ else if (traitsMask & FontWeight500Mask)
+ result.weight = { FontSelectionValue(500), FontSelectionValue(500) };
+ else if (traitsMask & FontWeight600Mask)
+ result.weight = { FontSelectionValue(600), FontSelectionValue(600) };
+ else if (traitsMask & FontWeight700Mask)
+ result.weight = { FontSelectionValue(700), FontSelectionValue(700) };
+ else if (traitsMask & FontWeight800Mask)
+ result.weight = { FontSelectionValue(800), FontSelectionValue(800) };
+ else {
+ ASSERT(traitsMask & FontWeight900Mask);
+ result.weight = { FontSelectionValue(900), FontSelectionValue(900) };
+ }
+
+ if (traitsMask & FontStyleNormalMask)
+ result.slope = { FontSelectionValue(), FontSelectionValue() };
+ else {
+ ASSERT(traitsMask & FontStyleItalicMask);
+ result.slope = { italicThreshold(), italicThreshold() };
+ }
+
+ return result;
+}
+
+FontSelectionCapabilities fontSelectionCapabilitiesForTraitsMask(FontTraitsMask traitsMask, FontSelectionValue stretch)
+{
+ FontSelectionCapabilities result = initialFontSelectionCapabilitiesForTraitsMask(traitsMask);
+ result.width = { stretch, stretch };
+ return result;
+}
+
+FontSelectionCapabilities fontSelectionCapabilitiesForTraitsMask(FontTraitsMask traitsMask, FontSelectionRange stretch)
+{
+ FontSelectionCapabilities result = initialFontSelectionCapabilitiesForTraitsMask(traitsMask);
+ result.width = stretch;
+ return result;
+}
+
+}
Property changes on: trunk/Source/WebCore/platform/graphics/FontSelectionAlgorithm.cpp
___________________________________________________________________
Added: svn:eol-style
+native
\ No newline at end of property
Added: trunk/Source/WebCore/platform/graphics/FontSelectionAlgorithm.h (0 => 213436)
--- trunk/Source/WebCore/platform/graphics/FontSelectionAlgorithm.h (rev 0)
+++ trunk/Source/WebCore/platform/graphics/FontSelectionAlgorithm.h 2017-03-05 20:14:02 UTC (rev 213436)
@@ -0,0 +1,365 @@
+/*
+ * 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. 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 INC. 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.
+ */
+
+#pragma once
+
+#include "TextFlags.h"
+#include <wtf/GetPtr.h>
+#include <wtf/Hasher.h>
+#include <wtf/NeverDestroyed.h>
+#include <wtf/Optional.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+// Unclamped, unchecked, signed fixed-point number representing a value used for font variations.
+// Sixteen bits in total, one sign bit, two fractional bits, means the smallest positive representable value is 0.25,
+// the maximum representable value is 8191.75, and the minimum representable value is -8192.
+class FontSelectionValue {
+public:
+ FontSelectionValue() = default;
+
+ // Explicit because it is lossy.
+ explicit FontSelectionValue(int x)
+ : m_backing(x * fractionalEntropy)
+ {
+ }
+
+ // Explicit because it is lossy.
+ explicit FontSelectionValue(float x)
+ : m_backing(x * fractionalEntropy)
+ {
+ }
+
+ operator float() const
+ {
+ // floats have 23 fractional bits, but only 14 fractional bits are necessary, so every value can be represented losslessly.
+ return m_backing / static_cast<float>(fractionalEntropy);
+ }
+
+ FontSelectionValue operator+(const FontSelectionValue other) const;
+ FontSelectionValue operator-(const FontSelectionValue other) const;
+ FontSelectionValue operator*(const FontSelectionValue other) const;
+ FontSelectionValue operator/(const FontSelectionValue other) const;
+ FontSelectionValue operator-() const;
+ bool operator==(const FontSelectionValue other) const;
+ bool operator!=(const FontSelectionValue other) const;
+ bool operator<(const FontSelectionValue other) const;
+ bool operator<=(const FontSelectionValue other) const;
+ bool operator>(const FontSelectionValue other) const;
+ bool operator>=(const FontSelectionValue other) const;
+
+ int16_t rawValue() const
+ {
+ return m_backing;
+ }
+
+ static FontSelectionValue maximumValue()
+ {
+ static NeverDestroyed<FontSelectionValue> result = FontSelectionValue(std::numeric_limits<int16_t>::max(), RawTag::RawTag);
+ return result.get();
+ }
+
+ static FontSelectionValue minimumValue()
+ {
+ static NeverDestroyed<FontSelectionValue> result = FontSelectionValue(std::numeric_limits<int16_t>::min(), RawTag::RawTag);
+ return result.get();
+ }
+
+private:
+ enum class RawTag { RawTag };
+
+ FontSelectionValue(int16_t rawValue, RawTag)
+ : m_backing(rawValue)
+ {
+ }
+
+ static constexpr int fractionalEntropy = 4;
+ int16_t m_backing { 0 };
+};
+
+inline FontSelectionValue FontSelectionValue::operator+(const FontSelectionValue other) const
+{
+ return FontSelectionValue(m_backing + other.m_backing, RawTag::RawTag);
+}
+
+inline FontSelectionValue FontSelectionValue::operator-(const FontSelectionValue other) const
+{
+ return FontSelectionValue(m_backing - other.m_backing, RawTag::RawTag);
+}
+
+inline FontSelectionValue FontSelectionValue::operator*(const FontSelectionValue other) const
+{
+ return FontSelectionValue(static_cast<int32_t>(m_backing) * other.m_backing / fractionalEntropy, RawTag::RawTag);
+}
+
+inline FontSelectionValue FontSelectionValue::operator/(const FontSelectionValue other) const
+{
+ return FontSelectionValue(static_cast<int32_t>(m_backing) / other.m_backing * fractionalEntropy, RawTag::RawTag);
+}
+
+inline FontSelectionValue FontSelectionValue::operator-() const
+{
+ return FontSelectionValue(-m_backing, RawTag::RawTag);
+}
+
+inline bool FontSelectionValue::operator==(const FontSelectionValue other) const
+{
+ return m_backing == other.m_backing;
+}
+
+inline bool FontSelectionValue::operator!=(const FontSelectionValue other) const
+{
+ return !operator==(other);
+}
+
+inline bool FontSelectionValue::operator<(const FontSelectionValue other) const
+{
+ return m_backing < other.m_backing;
+}
+
+inline bool FontSelectionValue::operator<=(const FontSelectionValue other) const
+{
+ return m_backing <= other.m_backing;
+}
+
+inline bool FontSelectionValue::operator>(const FontSelectionValue other) const
+{
+ return m_backing > other.m_backing;
+}
+
+inline bool FontSelectionValue::operator>=(const FontSelectionValue other) const
+{
+ return m_backing >= other.m_backing;
+}
+
+static inline FontSelectionValue italicThreshold()
+{
+ static NeverDestroyed<FontSelectionValue> result = FontSelectionValue(20);
+ return result.get();
+}
+
+static inline FontSelectionValue boldThreshold()
+{
+ static NeverDestroyed<FontSelectionValue> result = FontSelectionValue(600);
+ return result.get();
+}
+
+static inline FontSelectionValue weightSearchThreshold()
+{
+ static NeverDestroyed<FontSelectionValue> result = FontSelectionValue(500);
+ return result.get();
+}
+
+// [Inclusive, Inclusive]
+struct FontSelectionRange {
+ FontSelectionRange(FontSelectionValue minimum, FontSelectionValue maximum)
+ : minimum(minimum)
+ , maximum(maximum)
+ {
+ }
+
+ bool operator==(const FontSelectionRange& other) const
+ {
+ return minimum == other.minimum
+ && maximum == other.maximum;
+ }
+
+ bool isValid() const
+ {
+ return minimum <= maximum;
+ }
+
+ void expand(const FontSelectionRange& other)
+ {
+ ASSERT(other.isValid());
+ if (!isValid())
+ *this = other;
+ else {
+ minimum = std::min(minimum, other.minimum);
+ maximum = std::max(maximum, other.maximum);
+ }
+ ASSERT(isValid());
+ }
+
+ bool includes(FontSelectionValue target) const
+ {
+ return target >= minimum && target <= maximum;
+ }
+
+ FontSelectionValue minimum { FontSelectionValue(1) };
+ FontSelectionValue maximum { FontSelectionValue(0) };
+};
+
+struct FontSelectionRequest {
+ bool operator==(const FontSelectionRequest& other) const
+ {
+ return weight == other.weight
+ && width == other.width
+ && slope == other.slope;
+ }
+
+ bool operator!=(const FontSelectionRequest& other) const
+ {
+ return !operator==(other);
+ }
+
+ FontSelectionValue weight;
+ FontSelectionValue width;
+ FontSelectionValue slope;
+};
+
+// Only used for HashMaps. We don't want to put the bool into FontSelectionRequest
+// because FontSelectionRequest needs to be as small as possible because it's inside
+// every FontDescription.
+struct FontSelectionRequestKey {
+ FontSelectionRequestKey() = default;
+
+ FontSelectionRequestKey(FontSelectionRequest request)
+ : request(request)
+ {
+ }
+
+ explicit FontSelectionRequestKey(WTF::HashTableDeletedValueType)
+ : isDeletedValue(true)
+ {
+ }
+
+ bool isHashTableDeletedValue() const
+ {
+ return isDeletedValue;
+ }
+
+ bool operator==(const FontSelectionRequestKey& other) const
+ {
+ return request == other.request
+ && isDeletedValue == other.isDeletedValue;
+ }
+
+ FontSelectionRequest request;
+ bool isDeletedValue { false };
+};
+
+struct FontSelectionRequestKeyHash {
+ static unsigned hash(const FontSelectionRequestKey& key)
+ {
+ IntegerHasher hasher;
+ hasher.add(key.request.weight.rawValue());
+ hasher.add(key.request.width.rawValue());
+ hasher.add(key.request.slope.rawValue());
+ hasher.add(key.isDeletedValue);
+ return hasher.hash();
+ }
+
+ static bool equal(const FontSelectionRequestKey& a, const FontSelectionRequestKey& b)
+ {
+ return a == b;
+ }
+
+ static const bool safeToCompareToEmptyOrDeleted = true;
+};
+
+struct FontSelectionCapabilities {
+ void expand(const FontSelectionCapabilities& capabilities)
+ {
+ weight.expand(capabilities.weight);
+ width.expand(capabilities.width);
+ slope.expand(capabilities.slope);
+ }
+
+ FontSelectionRange weight { FontSelectionValue(400), FontSelectionValue(400) };
+ FontSelectionRange width { FontSelectionValue(100), FontSelectionValue(100) };
+ FontSelectionRange slope { FontSelectionValue(), FontSelectionValue() };
+};
+
+class FontSelectionAlgorithm {
+public:
+ FontSelectionAlgorithm() = delete;
+
+ FontSelectionAlgorithm(FontSelectionRequest request, const Vector<FontSelectionCapabilities>& capabilities, std::optional<FontSelectionCapabilities> capabilitiesBounds = std::nullopt)
+ : m_request(request)
+ , m_capabilities(capabilities)
+ , m_filter(new bool[m_capabilities.size()])
+ {
+ ASSERT(!m_capabilities.isEmpty());
+ if (capabilitiesBounds)
+ m_capabilitiesBounds = capabilitiesBounds.value();
+ else {
+ for (auto capabilities : m_capabilities)
+ m_capabilitiesBounds.expand(capabilities);
+ }
+ for (size_t i = 0; i < m_capabilities.size(); ++i)
+ m_filter[i] = true;
+ }
+
+ struct DistanceResult {
+ FontSelectionValue distance;
+ FontSelectionValue value;
+ };
+
+ DistanceResult stretchDistance(FontSelectionCapabilities) const;
+ DistanceResult styleDistance(FontSelectionCapabilities) const;
+ DistanceResult weightDistance(FontSelectionCapabilities) const;
+
+ size_t indexOfBestCapabilities();
+
+private:
+ template <typename T>
+ using IterateActiveCapabilitiesWithReturnCallback = std::function<std::optional<T>(FontSelectionCapabilities, size_t)>;
+
+ template <typename T>
+ inline std::optional<T> iterateActiveCapabilitiesWithReturn(IterateActiveCapabilitiesWithReturnCallback<T> callback)
+ {
+ for (size_t i = 0; i < m_capabilities.size(); ++i) {
+ if (!m_filter[i])
+ continue;
+ if (auto result = callback(m_capabilities[i], i))
+ return result;
+ }
+ return std::nullopt;
+ }
+
+ template <typename T>
+ inline void iterateActiveCapabilities(T callback)
+ {
+ iterateActiveCapabilitiesWithReturn<int>([&](FontSelectionCapabilities capabilities, size_t i) -> std::optional<int> {
+ callback(capabilities, i);
+ return std::nullopt;
+ });
+ }
+
+ void filterCapability(DistanceResult(FontSelectionAlgorithm::*computeDistance)(FontSelectionCapabilities) const, FontSelectionRange FontSelectionCapabilities::*inclusionRange);
+
+ FontSelectionRequest m_request;
+ FontSelectionCapabilities m_capabilitiesBounds;
+ const Vector<FontSelectionCapabilities>& m_capabilities;
+ std::unique_ptr<bool[]> m_filter;
+};
+
+FontSelectionRequest fontSelectionRequestForTraitsMask(FontTraitsMask, FontSelectionValue stretch);
+FontSelectionCapabilities fontSelectionCapabilitiesForTraitsMask(FontTraitsMask, FontSelectionValue stretch);
+FontSelectionCapabilities fontSelectionCapabilitiesForTraitsMask(FontTraitsMask, FontSelectionRange stretch);
+
+}
Property changes on: trunk/Source/WebCore/platform/graphics/FontSelectionAlgorithm.h
___________________________________________________________________
Added: svn:eol-style
+native
\ No newline at end of property
Added: svn:keywords
+Author Date Id Rev URL
\ No newline at end of property
Modified: trunk/Source/WebCore/platform/graphics/cocoa/FontCacheCoreText.cpp (213435 => 213436)
--- trunk/Source/WebCore/platform/graphics/cocoa/FontCacheCoreText.cpp 2017-03-05 19:14:12 UTC (rev 213435)
+++ trunk/Source/WebCore/platform/graphics/cocoa/FontCacheCoreText.cpp 2017-03-05 20:14:02 UTC (rev 213436)
@@ -662,8 +662,21 @@
return nullptr;
}
-Vector<FontTraitsMask> FontCache::getTraitsInFamily(const AtomicString& familyName)
+static FontSelectionValue stretchFromCoreTextTraits(CFDictionaryRef traits)
{
+ auto widthNumber = static_cast<CFNumberRef>(CFDictionaryGetValue(traits, kCTFontWidthTrait));
+ if (widthNumber) {
+ // FIXME: The normalization from Core Text's [-1, 1] range to CSS's [50%, 200%] range isn't perfect.
+ float ctWidth;
+ auto success = CFNumberGetValue(widthNumber, kCFNumberFloatType, &ctWidth);
+ ASSERT_UNUSED(success, success);
+ return FontSelectionValue(ctWidth < 0.5 ? ctWidth * 50 + 100 : ctWidth * 150 + 50);
+ }
+ return FontSelectionValue(100);
+}
+
+auto FontCache::getTraitsAndStretchInFamily(const AtomicString& familyName) -> Vector<TraitsAndStretch>
+{
auto familyNameStr = familyName.string().createCFString();
CFTypeRef keys[] = { kCTFontFamilyNameAttribute };
CFTypeRef values[] = { familyNameStr.get() };
@@ -677,8 +690,8 @@
if (!numMatches)
return { };
- Vector<FontTraitsMask> traitsMasks;
- traitsMasks.reserveInitialCapacity(numMatches);
+ Vector<TraitsAndStretch> result;
+ result.reserveInitialCapacity(numMatches);
for (CFIndex i = 0; i < numMatches; ++i) {
auto traits = adoptCF((CFDictionaryRef)CTFontDescriptorCopyAttribute((CTFontDescriptorRef)CFArrayGetValueAtIndex(matchedDescriptors.get(), i), kCTFontTraitsAttribute));
CFNumberRef resultRef = (CFNumberRef)CFDictionaryGetValue(traits.get(), kCTFontSymbolicTrait);
@@ -688,10 +701,11 @@
CFNumberGetValue(resultRef, kCFNumberIntType, &symbolicTraits);
CGFloat weight = 0;
CFNumberGetValue(weightRef, kCFNumberCGFloatType, &weight);
- traitsMasks.uncheckedAppend(toTraitsMask(symbolicTraits, weight));
+ auto stretch = stretchFromCoreTextTraits(traits.get());
+ result.uncheckedAppend({ toTraitsMask(symbolicTraits, weight), { stretch, stretch } });
}
}
- return traitsMasks;
+ return result;
}
static void invalidateFontCache();
@@ -895,14 +909,7 @@
FontSelectionValue slant;
FontSelectionValue weight;
if (traits) {
- auto widthNumber = static_cast<CFNumberRef>(CFDictionaryGetValue(traits.get(), kCTFontWidthTrait));
- if (widthNumber) {
- // FIXME: The normalization from Core Text's [-1, 1] range to CSS's [50%, 200%] range isn't perfect.
- float ctWidth;
- auto success = CFNumberGetValue(widthNumber, kCFNumberFloatType, &ctWidth);
- ASSERT_UNUSED(success, success);
- width = FontSelectionValue(ctWidth < 0.5 ? ctWidth * 50 + 100 : ctWidth * 150 + 50);
- }
+ width = stretchFromCoreTextTraits(traits.get());
auto symbolicTraitsNumber = static_cast<CFNumberRef>(CFDictionaryGetValue(traits.get(), kCTFontSymbolicTrait));
if (symbolicTraitsNumber) {
@@ -926,24 +933,6 @@
return { { weight, weight }, { width, width }, { slant, slant } };
}
- static const FontSelectionValue stretchThreshold()
- {
- static NeverDestroyed<FontSelectionValue> threshold(100);
- return threshold.get();
- }
-
- static const FontSelectionValue italicThreshold()
- {
- static NeverDestroyed<FontSelectionValue> threshold(20);
- return threshold.get();
- }
-
- static const FontSelectionValue weightThreshold()
- {
- static NeverDestroyed<FontSelectionValue> threshold(500);
- return threshold.get();
- }
-
private:
friend class NeverDestroyed<FontDatabase>;
@@ -953,326 +942,61 @@
HashMap<String, InstalledFont> m_postScriptNameToFontDescriptors;
};
-template <typename T>
-using IterateActiveFontsWithReturnCallback = std::function<std::optional<T>(const FontDatabase::InstalledFont&, size_t)>;
-
-template <typename T>
-inline std::optional<T> iterateActiveFontsWithReturn(const FontDatabase::InstalledFontFamily& installedFonts, const std::unique_ptr<bool[]>& filter, IterateActiveFontsWithReturnCallback<T> callback)
+static const FontDatabase::InstalledFont* findClosestFont(const FontDatabase::InstalledFontFamily& familyFonts, FontSelectionRequest fontSelectionRequest)
{
- for (size_t i = 0; i < installedFonts.size(); ++i) {
- if (!filter[i])
- continue;
- if (auto result = callback(installedFonts.installedFonts[i], i))
- return result;
- }
- return std::nullopt;
+ Vector<FontSelectionCapabilities> capabilities;
+ capabilities.reserveInitialCapacity(familyFonts.size());
+ for (auto& font : familyFonts.installedFonts)
+ capabilities.uncheckedAppend(font.capabilities);
+ FontSelectionAlgorithm fontSelectionAlgorithm(fontSelectionRequest, capabilities, familyFonts.capabilities);
+ return &familyFonts.installedFonts[fontSelectionAlgorithm.indexOfBestCapabilities()];
}
-template <typename T>
-inline void iterateActiveFonts(const FontDatabase::InstalledFontFamily& installedFonts, const std::unique_ptr<bool[]>& filter, T callback)
+static FontSelectionRequest calculateFontSelectionRequest(CTFontSymbolicTraits requestedTraits, FontWeight weight, FontSelectionValue stretch)
{
- iterateActiveFontsWithReturn<int>(installedFonts, filter, [&](const FontDatabase::InstalledFont& font, size_t i) -> std::optional<int> {
- callback(font, i);
- return std::nullopt;
- });
-}
-
-static inline std::optional<FontSelectionValue> findClosestStretch(FontSelectionValue targetStretch, const FontDatabase::InstalledFontFamily& installedFonts, const std::unique_ptr<bool[]>& filter)
-{
- std::function<float(const FontDatabase::InstalledFont&)> computeScore;
-
- if (targetStretch >= FontDatabase::stretchThreshold()) {
- auto threshold = std::max(targetStretch, installedFonts.capabilities.width.maximum);
- computeScore = [&, threshold](const FontDatabase::InstalledFont& font) -> float {
- auto width = font.capabilities.width;
- ASSERT(width.isValid());
- if (width.includes(targetStretch))
- return 0;
- ASSERT(width.minimum > targetStretch || width.maximum < targetStretch);
- if (width.minimum > targetStretch)
- return width.minimum - targetStretch;
- ASSERT(width.maximum < targetStretch);
- return threshold - width.maximum;
- };
- } else {
- ASSERT(targetStretch < FontDatabase::stretchThreshold());
- auto threshold = std::min(targetStretch, installedFonts.capabilities.width.minimum);
- computeScore = [&, threshold](const FontDatabase::InstalledFont& font) -> float {
- auto width = font.capabilities.width;
- if (width.includes(targetStretch))
- return 0;
- ASSERT(width.minimum > targetStretch || width.maximum < targetStretch);
- if (width.maximum < targetStretch)
- return targetStretch - width.maximum;
- ASSERT(width.minimum > targetStretch);
- return width.minimum - threshold;
- };
- }
-
- size_t closestIndex = 0;
- std::optional<float> minimumScore;
- iterateActiveFonts(installedFonts, filter, [&](auto& installedFont, size_t i) {
- auto score = computeScore(installedFont);
- if (!minimumScore || score < minimumScore.value()) {
- minimumScore = score;
- closestIndex = i;
- }
- });
-
- if (!minimumScore)
- return std::nullopt;
- auto& winner = installedFonts.installedFonts[closestIndex];
- auto width = winner.capabilities.width;
- if (width.includes(targetStretch))
- return targetStretch;
- if (width.minimum > targetStretch)
- return width.minimum;
- ASSERT(width.maximum < targetStretch);
- return width.maximum;
-}
-
-static inline void filterStretch(FontSelectionValue target, const FontDatabase::InstalledFontFamily& installedFonts, std::unique_ptr<bool[]>& filter)
-{
- iterateActiveFonts(installedFonts, filter, [&](auto& installedFont, size_t i) {
- if (!installedFont.capabilities.width.includes(target))
- filter[i] = false;
- });
-}
-
-static inline std::optional<FontSelectionValue> findClosestStyle(FontSelectionValue targetStyle, const FontDatabase::InstalledFontFamily& installedFonts, const std::unique_ptr<bool[]>& filter)
-{
- std::function<float(const FontDatabase::InstalledFont&)> computeScore;
-
- if (targetStyle >= FontDatabase::italicThreshold()) {
- auto threshold = std::max(targetStyle, installedFonts.capabilities.slope.maximum);
- computeScore = [&, threshold](const FontDatabase::InstalledFont& font) -> float {
- auto slope = font.capabilities.slope;
- ASSERT(slope.isValid());
- if (slope.includes(targetStyle))
- return 0;
- ASSERT(slope.minimum > targetStyle || slope.maximum < targetStyle);
- if (slope.minimum > targetStyle)
- return slope.minimum - targetStyle;
- ASSERT(targetStyle > slope.maximum);
- return threshold - slope.maximum;
- };
- } else if (targetStyle >= FontSelectionValue()) {
- auto threshold = std::max(targetStyle, installedFonts.capabilities.slope.maximum);
- computeScore = [&, threshold](const FontDatabase::InstalledFont& font) -> float {
- auto slope = font.capabilities.slope;
- ASSERT(slope.isValid());
- if (slope.includes(targetStyle))
- return 0;
- ASSERT(slope.minimum > targetStyle || slope.maximum < targetStyle);
- if (slope.maximum >= FontSelectionValue() && slope.maximum < targetStyle)
- return targetStyle - slope.maximum;
- if (slope.minimum > targetStyle)
- return slope.minimum;
- ASSERT(slope.maximum < FontSelectionValue());
- return threshold - slope.maximum;
- };
- } else if (targetStyle > -FontDatabase::italicThreshold()) {
- auto threshold = std::min(targetStyle, installedFonts.capabilities.slope.minimum);
- computeScore = [&, threshold](const FontDatabase::InstalledFont& font) -> float {
- auto slope = font.capabilities.slope;
- ASSERT(slope.isValid());
- if (slope.includes(targetStyle))
- return 0;
- ASSERT(slope.minimum > targetStyle || slope.maximum < targetStyle);
- if (slope.minimum > targetStyle && slope.minimum <= FontSelectionValue())
- return slope.minimum - targetStyle;
- if (slope.maximum < targetStyle)
- return -slope.maximum;
- ASSERT(slope.minimum > FontSelectionValue());
- return slope.minimum - threshold;
- };
- } else {
- ASSERT(targetStyle <= -FontDatabase::italicThreshold());
- auto threshold = std::min(targetStyle, installedFonts.capabilities.slope.minimum);
- computeScore = [&, threshold](const FontDatabase::InstalledFont& font) -> float {
- auto slope = font.capabilities.slope;
- ASSERT(slope.isValid());
- if (slope.includes(targetStyle))
- return 0;
- ASSERT(slope.minimum > targetStyle || slope.maximum < targetStyle);
- if (slope.maximum < targetStyle)
- return targetStyle - slope.maximum;
- ASSERT(slope.minimum > targetStyle);
- return slope.minimum - threshold;
- };
- }
-
- size_t closestIndex = 0;
- std::optional<float> minimumScore;
- iterateActiveFonts(installedFonts, filter, [&](auto& installedFont, size_t i) {
- auto score = computeScore(installedFont);
- if (!minimumScore || score < minimumScore.value()) {
- minimumScore = score;
- closestIndex = i;
- }
- });
-
- if (!minimumScore)
- return std::nullopt;
- auto& winner = installedFonts.installedFonts[closestIndex];
- auto slope = winner.capabilities.slope;
- if (slope.includes(targetStyle))
- return targetStyle;
- if (slope.minimum > targetStyle)
- return slope.minimum;
- ASSERT(slope.maximum < targetStyle);
- return slope.maximum;
-}
-
-static inline void filterStyle(FontSelectionValue target, const FontDatabase::InstalledFontFamily& installedFonts, std::unique_ptr<bool[]>& filter)
-{
- iterateActiveFonts(installedFonts, filter, [&](auto& installedFont, size_t i) {
- if (!installedFont.capabilities.slope.includes(target))
- filter[i] = false;
- });
-}
-
-static inline std::optional<FontSelectionValue> findClosestWeight(FontSelectionValue targetWeight, const FontDatabase::InstalledFontFamily& installedFonts, const std::unique_ptr<bool[]>& filter)
-{
- {
- // The spec states: "If the desired weight is 400, 500 is checked first ... If the desired weight is 500, 400 is checked first"
- IterateActiveFontsWithReturnCallback<FontSelectionValue> searchFor400 = [&](const FontDatabase::InstalledFont& font, size_t) -> std::optional<FontSelectionValue> {
- if (font.capabilities.weight.includes(FontSelectionValue(400)))
- return FontSelectionValue(400);
- return std::nullopt;
- };
- IterateActiveFontsWithReturnCallback<FontSelectionValue> searchFor500 = [&](const FontDatabase::InstalledFont& font, size_t) -> std::optional<FontSelectionValue> {
- if (font.capabilities.weight.includes(FontSelectionValue(500)))
- return FontSelectionValue(500);
- return std::nullopt;
- };
- if (targetWeight == FontSelectionValue(400)) {
- if (auto result = iterateActiveFontsWithReturn(installedFonts, filter, searchFor400))
- return result;
- if (auto result = iterateActiveFontsWithReturn(installedFonts, filter, searchFor500))
- return result;
- } else if (targetWeight == FontSelectionValue(500)) {
- if (auto result = iterateActiveFontsWithReturn(installedFonts, filter, searchFor500))
- return result;
- if (auto result = iterateActiveFontsWithReturn(installedFonts, filter, searchFor400))
- return result;
- }
- }
-
- std::function<float(const FontDatabase::InstalledFont&)> computeScore;
- if (targetWeight <= FontDatabase::weightThreshold()) {
- auto threshold = std::min(targetWeight, installedFonts.capabilities.weight.minimum);
- computeScore = [&, threshold](const FontDatabase::InstalledFont& font) -> FontSelectionValue {
- auto weight = font.capabilities.weight;
- if (weight.includes(targetWeight))
- return FontSelectionValue();
- ASSERT(weight.minimum > targetWeight || weight.maximum < targetWeight);
- if (weight.maximum < targetWeight)
- return targetWeight - weight.maximum;
- ASSERT(weight.minimum > targetWeight);
- return weight.minimum - threshold;
- };
- } else {
- ASSERT(targetWeight > FontDatabase::weightThreshold());
- auto threshold = std::max(targetWeight, installedFonts.capabilities.weight.maximum);
- computeScore = [&, threshold](const FontDatabase::InstalledFont& font) -> FontSelectionValue {
- auto weight = font.capabilities.weight;
- if (weight.includes(targetWeight))
- return FontSelectionValue();
- ASSERT(weight.minimum > targetWeight || weight.maximum < targetWeight);
- if (weight.minimum > targetWeight)
- return weight.minimum - targetWeight;
- ASSERT(weight.maximum < targetWeight);
- return threshold - weight.maximum;
- };
- }
-
- size_t closestIndex = 0;
- std::optional<float> minimumScore;
- iterateActiveFonts(installedFonts, filter, [&](auto& installedFont, size_t i) {
- auto score = computeScore(installedFont);
- if (!minimumScore || score < minimumScore.value()) {
- minimumScore = score;
- closestIndex = i;
- }
- });
-
- if (!minimumScore)
- return std::nullopt;
- auto& winner = installedFonts.installedFonts[closestIndex];
- auto weight = winner.capabilities.weight;
- if (weight.includes(targetWeight))
- return targetWeight;
- if (weight.minimum > targetWeight)
- return weight.minimum;
- ASSERT(weight.maximum < targetWeight);
- return weight.maximum;
-}
-
-static inline void filterWeight(FontSelectionValue target, const FontDatabase::InstalledFontFamily& installedFonts, std::unique_ptr<bool[]>& filter)
-{
- iterateActiveFonts(installedFonts, filter, [&](auto& installedFont, size_t i) {
- if (!installedFont.capabilities.weight.includes(target))
- filter[i] = false;
- });
-}
-
-static inline FontSelectionValue computeTargetWeight(FontWeight weight)
-{
+ FontSelectionRequest result;
switch (weight) {
case FontWeight100:
- return FontSelectionValue(100);
+ result.weight = FontSelectionValue(100);
+ break;
case FontWeight200:
- return FontSelectionValue(200);
+ result.weight = FontSelectionValue(200);
+ break;
case FontWeight300:
- return FontSelectionValue(300);
+ result.weight = FontSelectionValue(300);
+ break;
case FontWeight400:
- return FontSelectionValue(400);
+ result.weight = FontSelectionValue(400);
+ break;
case FontWeight500:
- return FontSelectionValue(500);
+ result.weight = FontSelectionValue(500);
+ break;
case FontWeight600:
- return FontSelectionValue(600);
+ result.weight = FontSelectionValue(600);
+ break;
case FontWeight700:
- return FontSelectionValue(700);
+ result.weight = FontSelectionValue(700);
+ break;
case FontWeight800:
- return FontSelectionValue(800);
+ result.weight = FontSelectionValue(800);
+ break;
case FontWeight900:
- return FontSelectionValue(900);
+ result.weight = FontSelectionValue(900);
+ break;
default:
ASSERT_NOT_REACHED();
- return FontSelectionValue(400);
+ result.weight = FontSelectionValue(400);
+ break;
}
-}
-static const FontDatabase::InstalledFont* findClosestFont(const FontDatabase::InstalledFontFamily& familyFonts, CTFontSymbolicTraits requestedTraits, FontWeight weight, FontSelectionValue stretch)
-{
- ASSERT(!familyFonts.isEmpty());
+ result.width = stretch;
- // Parallel to familyFonts.
- std::unique_ptr<bool[]> filter { new bool[familyFonts.size()] };
- for (size_t i = 0; i < familyFonts.size(); ++i)
- filter[i] = true;
-
- if (auto closestStretch = findClosestStretch(stretch, familyFonts, filter))
- filterStretch(closestStretch.value(), familyFonts, filter);
+ if (requestedTraits & kCTFontTraitItalic)
+ result.slope = italicThreshold();
else
- return nullptr;
+ result.slope = FontSelectionValue();
- FontSelectionValue targetStyle = requestedTraits & kCTFontTraitItalic ? FontDatabase::italicThreshold() : FontSelectionValue();
- if (auto closestStyle = findClosestStyle(targetStyle, familyFonts, filter))
- filterStyle(closestStyle.value(), familyFonts, filter);
- else
- return nullptr;
-
- FontSelectionValue targetWeight = computeTargetWeight(weight);
- if (auto closestWeight = findClosestWeight(targetWeight, familyFonts, filter))
- filterWeight(closestWeight.value(), familyFonts, filter);
- else
- return nullptr;
-
- return iterateActiveFontsWithReturn<const FontDatabase::InstalledFont*>(familyFonts, filter, [](const FontDatabase::InstalledFont& font, size_t) {
- return &font;
- }).value_or(nullptr);
+ return result;
}
#endif // !SHOULD_USE_CORE_TEXT_FONT_LOOKUP
@@ -1283,7 +1007,6 @@
if (!isSystemFont(family) && whitelist.size() && !whitelist.contains(family))
return nullptr;
-
#if SHOULD_USE_CORE_TEXT_FONT_LOOKUP
UNUSED_PARAM(stretch);
return adoptCF(CTFontCreateForCSS(family.string().createCFString().get(), toCoreTextFontWeight(weight), requestedTraits, size));
@@ -1300,8 +1023,8 @@
const auto& postScriptFont = FontDatabase::singleton().fontForPostScriptName(family);
if (!postScriptFont.fontDescriptor)
return nullptr;
- if (((requestedTraits & kCTFontTraitItalic) && postScriptFont.capabilities.slope.maximum < FontDatabase::italicThreshold())
- || (weight >= FontWeight600 && postScriptFont.capabilities.weight.maximum < FontSelectionValue(600))) {
+ if (((requestedTraits & kCTFontTraitItalic) && postScriptFont.capabilities.slope.maximum < italicThreshold())
+ || (weight >= FontWeight600 && postScriptFont.capabilities.weight.maximum < boldThreshold())) {
auto postScriptFamilyName = adoptCF(static_cast<CFStringRef>(CTFontDescriptorCopyAttribute(postScriptFont.fontDescriptor.get(), kCTFontFamilyNameAttribute)));
if (!postScriptFamilyName)
return nullptr;
@@ -1308,7 +1031,7 @@
const auto& familyFonts = FontDatabase::singleton().collectionForFamily(String(postScriptFamilyName.get()));
if (familyFonts.isEmpty())
return nullptr;
- if (const auto* installedFont = findClosestFont(familyFonts, requestedTraits, weight, stretch)) {
+ if (const auto* installedFont = findClosestFont(familyFonts, calculateFontSelectionRequest(requestedTraits, weight, stretch))) {
if (!installedFont->fontDescriptor)
return nullptr;
return adoptCF(CTFontCreateWithFontDescriptor(installedFont->fontDescriptor.get(), size, nullptr));
@@ -1318,7 +1041,7 @@
return adoptCF(CTFontCreateWithFontDescriptor(postScriptFont.fontDescriptor.get(), size, nullptr));
}
- if (const auto* installedFont = findClosestFont(familyFonts, requestedTraits, weight, stretch))
+ if (const auto* installedFont = findClosestFont(familyFonts, calculateFontSelectionRequest(requestedTraits, weight, stretch)))
return adoptCF(CTFontCreateWithFontDescriptor(installedFont->fontDescriptor.get(), size, nullptr));
return nullptr;
Modified: trunk/Source/WebCore/platform/graphics/freetype/FontCacheFreeType.cpp (213435 => 213436)
--- trunk/Source/WebCore/platform/graphics/freetype/FontCacheFreeType.cpp 2017-03-05 19:14:12 UTC (rev 213435)
+++ trunk/Source/WebCore/platform/graphics/freetype/FontCacheFreeType.cpp 2017-03-05 20:14:02 UTC (rev 213436)
@@ -138,7 +138,7 @@
RELEASE_ASSERT_NOT_REACHED();
}
-Vector<FontTraitsMask> FontCache::getTraitsInFamily(const AtomicString&)
+auto FontCache::getTraitsAndStretchInFamily(const AtomicString& familyName) -> Vector<TraitsAndStretch>
{
return { };
}
Modified: trunk/Source/WebCore/platform/graphics/win/FontCacheWin.cpp (213435 => 213436)
--- trunk/Source/WebCore/platform/graphics/win/FontCacheWin.cpp 2017-03-05 19:14:12 UTC (rev 213435)
+++ trunk/Source/WebCore/platform/graphics/win/FontCacheWin.cpp 2017-03-05 20:14:02 UTC (rev 213436)
@@ -559,7 +559,7 @@
return 1;
}
-Vector<FontTraitsMask> FontCache::getTraitsInFamily(const AtomicString& familyName)
+auto FontCache::getTraitsAndStretchInFamily(const AtomicString& familyName) -> Vector<TraitsAndStretch>
{
HWndDC hdc(0);
@@ -572,10 +572,10 @@
TraitsInFamilyProcData procData(familyName);
EnumFontFamiliesEx(hdc, &logFont, traitsInFamilyEnumProc, reinterpret_cast<LPARAM>(&procData), 0);
- Vector<FontTraitsMask> result;
+ Vector<TraitsAndStretch> result;
result.reserveInitialCapacity(procData.m_traitsMasks.size());
for (unsigned mask : procData.m_traitsMasks)
- result.uncheckedAppend(static_cast<FontTraitsMask>(mask));
+ result.uncheckedAppend({ static_cast<FontTraitsMask>(mask), FontSelectionRange(FontSelectionValue(), FontSelectionValue()) });
return result;
}
Modified: trunk/Source/WebCore/platform/text/TextFlags.h (213435 => 213436)
--- trunk/Source/WebCore/platform/text/TextFlags.h 2017-03-05 19:14:12 UTC (rev 213435)
+++ trunk/Source/WebCore/platform/text/TextFlags.h 2017-03-05 20:14:02 UTC (rev 213436)
@@ -25,8 +25,6 @@
#pragma once
-#include <wtf/NeverDestroyed.h>
-
namespace WebCore {
enum TextRenderingMode { AutoTextRendering, OptimizeSpeed, OptimizeLegibility, GeometricPrecision };
@@ -398,162 +396,6 @@
FontWeightMask = FontWeight100Mask | FontWeight200Mask | FontWeight300Mask | FontWeight400Mask | FontWeight500Mask | FontWeight600Mask | FontWeight700Mask | FontWeight800Mask | FontWeight900Mask
};
-// Unclamped, unchecked, signed fixed-point number representing a value used for font variations.
-// Sixteen bits in total, one sign bit, two fractional bits, means the smallest positive representable value is 0.25,
-// the maximum representable value is 8191.75, and the minimum representable value is -8192.
-class FontSelectionValue {
-public:
- FontSelectionValue() = default;
-
- // Explicit because it is lossy.
- explicit FontSelectionValue(int x)
- : m_backing(x * fractionalEntropy)
- {
- }
-
- // Explicit because it is lossy.
- explicit FontSelectionValue(float x)
- : m_backing(x * fractionalEntropy)
- {
- }
-
- operator float() const
- {
- // floats have 23 fractional bits, but only 14 fractional bits are necessary, so every value can be represented losslessly.
- return m_backing / static_cast<float>(fractionalEntropy);
- }
-
- FontSelectionValue operator+(const FontSelectionValue other) const
- {
- return FontSelectionValue(m_backing + other.m_backing, RawTag::RawTag);
- }
-
- FontSelectionValue operator-(const FontSelectionValue other) const
- {
- return FontSelectionValue(m_backing - other.m_backing, RawTag::RawTag);
- }
-
- FontSelectionValue operator*(const FontSelectionValue other) const
- {
- return FontSelectionValue(static_cast<int32_t>(m_backing) * other.m_backing / fractionalEntropy, RawTag::RawTag);
- }
-
- FontSelectionValue operator/(const FontSelectionValue other) const
- {
- return FontSelectionValue(static_cast<int32_t>(m_backing) / other.m_backing * fractionalEntropy, RawTag::RawTag);
- }
-
- FontSelectionValue operator-() const
- {
- return FontSelectionValue(-m_backing, RawTag::RawTag);
- }
-
- bool operator==(const FontSelectionValue other) const
- {
- return m_backing == other.m_backing;
- }
-
- bool operator!=(const FontSelectionValue other) const
- {
- return !operator==(other);
- }
-
- bool operator<(const FontSelectionValue other) const
- {
- return m_backing < other.m_backing;
- }
-
- bool operator<=(const FontSelectionValue other) const
- {
- return m_backing <= other.m_backing;
- }
-
- bool operator>(const FontSelectionValue other) const
- {
- return m_backing > other.m_backing;
- }
-
- bool operator>=(const FontSelectionValue other) const
- {
- return m_backing >= other.m_backing;
- }
-
- int16_t rawValue() const
- {
- return m_backing;
- }
-
- static FontSelectionValue maximumValue()
- {
- static NeverDestroyed<FontSelectionValue> result = FontSelectionValue(std::numeric_limits<int16_t>::max(), RawTag::RawTag);
- return result.get();
- }
-
- static FontSelectionValue minimumValue()
- {
- static NeverDestroyed<FontSelectionValue> result = FontSelectionValue(std::numeric_limits<int16_t>::min(), RawTag::RawTag);
- return result.get();
- }
-
-private:
- enum class RawTag { RawTag };
-
- FontSelectionValue(int16_t rawValue, RawTag)
- : m_backing(rawValue)
- {
- }
-
- static constexpr int fractionalEntropy = 4;
- int16_t m_backing { 0 };
-};
-
-// [Inclusive, Inclusive]
-struct FontSelectionRange {
- bool isValid() const
- {
- return minimum <= maximum;
- }
-
- void expand(const FontSelectionRange& other)
- {
- ASSERT(other.isValid());
- if (!isValid())
- *this = other;
- else {
- minimum = std::min(minimum, other.minimum);
- maximum = std::max(maximum, other.maximum);
- }
- ASSERT(isValid());
- }
-
- bool includes(FontSelectionValue target) const
- {
- return target >= minimum && target <= maximum;
- }
-
- FontSelectionValue minimum { FontSelectionValue(1) };
- FontSelectionValue maximum { FontSelectionValue(0) };
-};
-
-struct FontSelectionRequest {
- FontSelectionValue weight;
- FontSelectionValue width;
- FontSelectionValue slope;
-};
-
-struct FontSelectionCapabilities {
- void expand(const FontSelectionCapabilities& capabilities)
- {
- weight.expand(capabilities.weight);
- width.expand(capabilities.width);
- slope.expand(capabilities.slope);
- }
-
- FontSelectionRange weight;
- FontSelectionRange width;
- FontSelectionRange slope;
-};
-
enum class Kerning {
Auto,
Normal,