Changeset 213436 in webkit


Ignore:
Timestamp:
Mar 5, 2017 12:14:02 PM (7 years ago)
Author:
mmaxfield@apple.com
Message:

Update CSSFontSelector's matching algorithm to understand ranges
https://bugs.webkit.org/show_bug.cgi?id=168892

Reviewed by Jon Lee.

Source/WebCore:

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.

LayoutTests:

Update CSS Font Loading API test to accept font-stretch values.

  • fast/text/font-face-javascript-expected.txt:
  • fast/text/font-face-javascript.html:
Location:
trunk
Files:
2 added
19 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r213434 r213436  
     12017-03-04  Myles C. Maxfield  <mmaxfield@apple.com>
     2
     3        Update CSSFontSelector's matching algorithm to understand ranges
     4        https://bugs.webkit.org/show_bug.cgi?id=168892
     5
     6        Reviewed by Jon Lee.
     7
     8        Update CSS Font Loading API test to accept font-stretch values.
     9
     10        * fast/text/font-face-javascript-expected.txt:
     11        * fast/text/font-face-javascript.html:
     12
    1132017-03-05  Carlos Garcia Campos  <cgarcia@igalia.com>
    214
  • trunk/LayoutTests/fast/text/font-face-javascript-expected.txt

    r212412 r213436  
    1717PASS new FontFace('family_name', 'url(\'asdf\')', {'weight': 'lighter'}).weight is "200"
    1818PASS new FontFace('family_name', 'url(\'asdf\')', {'weight': 'inherit'}).weight threw exception SyntaxError (DOM Exception 12): The string did not match the expected pattern..
    19 PASS new FontFace('family_name', 'url(\'asdf\')', {'stretch': 'stretch_name'}).stretch is "normal"
     19PASS new FontFace('family_name', 'url(\'asdf\')', {'stretch': 'ultra-expanded'}).stretch is "ultra-expanded"
    2020PASS new FontFace('family_name', 'url(\'asdf\')', {'unicodeRange': 'U+26'}).unicodeRange is "U+26-26"
    2121PASS new FontFace('family_name', 'url(\'asdf\')', {'unicodeRange': 'U+0-7F'}).unicodeRange is "U+0-7f"
     
    2626PASS everything.style is "italic"
    2727PASS everything.weight is "bold"
    28 PASS everything.stretch is "normal"
     28PASS everything.stretch is "extra-expanded"
    2929PASS everything.unicodeRange is "U+26-26"
    3030PASS everything.variant is "small-caps"
     
    3333PASS everything.style is "normal"
    3434PASS everything.weight is "300"
    35 PASS everything.stretch is "normal"
     35PASS everything.stretch is "condensed"
    3636PASS everything.unicodeRange is "U+0-7f"
    3737PASS everything.variant is "stacked-fractions"
  • trunk/LayoutTests/fast/text/font-face-javascript.html

    r212405 r213436  
    2525shouldBeEqualToString("new FontFace('family_name', 'url(\\'asdf\\')', {'weight': 'lighter'}).weight", "200");
    2626shouldThrow("new FontFace('family_name', 'url(\\'asdf\\')', {'weight': 'inherit'}).weight");
    27 shouldBeEqualToString("new FontFace('family_name', 'url(\\'asdf\\')', {'stretch': 'stretch_name'}).stretch", "normal");
     27shouldBeEqualToString("new FontFace('family_name', 'url(\\'asdf\\')', {'stretch': 'ultra-expanded'}).stretch", "ultra-expanded");
    2828shouldBeEqualToString("new FontFace('family_name', 'url(\\'asdf\\')', {'unicodeRange': 'U+26'}).unicodeRange", "U+26-26");
    2929shouldBeEqualToString("new FontFace('family_name', 'url(\\'asdf\\')', {'unicodeRange': 'U+0-7F'}).unicodeRange", "U+0-7f");
     
    3333shouldBeEqualToString("new FontFace('family_name', 'url(\\'asdf\\')', {'featureSettings': '\\'titl\\''}).featureSettings", "'titl' 1");
    3434
    35 var everything = new FontFace('family_name', 'url(\'asdf\')', {'style': 'italic', 'weight': 'bold', 'stretch': 'stretch_name', 'unicodeRange': 'U+26', 'variant': 'small-caps', 'featureSettings': '\'titl\''});
     35var everything = new FontFace('family_name', 'url(\'asdf\')', {'style': 'italic', 'weight': 'bold', 'stretch': 'extra-expanded', 'unicodeRange': 'U+26', 'variant': 'small-caps', 'featureSettings': '\'titl\''});
    3636shouldBeEqualToString("everything.style", "italic");
    3737shouldBeEqualToString("everything.weight", "bold");
    38 shouldBeEqualToString("everything.stretch", "normal");
     38shouldBeEqualToString("everything.stretch", "extra-expanded");
    3939shouldBeEqualToString("everything.unicodeRange", "U+26-26");
    4040shouldBeEqualToString("everything.variant", "small-caps");
     
    4747everything.weight = "300";
    4848shouldBeEqualToString("everything.weight", "300");
    49 everything.stretch = "other_stretch_name";
    50 shouldBeEqualToString("everything.stretch", "normal");
     49everything.stretch = "condensed";
     50shouldBeEqualToString("everything.stretch", "condensed");
    5151everything.unicodeRange = "U+0-7F";
    5252shouldBeEqualToString("everything.unicodeRange", "U+0-7f");
  • trunk/Source/WebCore/CMakeLists.txt

    r213423 r213436  
    22012201    platform/graphics/FontCascadeFonts.cpp
    22022202    platform/graphics/FontDescription.cpp
     2203    platform/graphics/FontSelectionAlgorithm.cpp
    22032204    platform/graphics/FontTaggedSettings.cpp
    22042205    platform/graphics/FontGenericFamilies.cpp
  • trunk/Source/WebCore/ChangeLog

    r213435 r213436  
     12017-03-04  Myles C. Maxfield  <mmaxfield@apple.com>
     2
     3        Update CSSFontSelector's matching algorithm to understand ranges
     4        https://bugs.webkit.org/show_bug.cgi?id=168892
     5
     6        Reviewed by Jon Lee.
     7
     8        This patch migrates the font selection algorithm out of FontCacheCoreText and into its own file which can be shared
     9        among all ports. It then migrates our web font selection algorithm to use it.
     10
     11        This patch doesn't actually change the parsing rules; it just changes the internal machinery for how fonts get
     12        selected. Therefore, this patch simply makes zero-length ranges from the discrete values the parser emits, and passes
     13        those zero-length ranges to the range-based font selection routine. This means that this patch doesn't actually
     14        change the results of the font selection algorithm.
     15
     16        One of the inputs to the new font selection algorithm is font-stretch, which previously was not parsed inside
     17        @font-face blocks or the CSS Font Loading API. This patch therefore adds parsing support and modifies the existing
     18        tests for these pieces to expect parsing to work. Because the font selection algorithm itself is shared between
     19        installed fonts and webfonts, this patch doesn't add any additional tests for it (because it is already covered under
     20        existing tests).
     21
     22        No new tests because there is no behavior change.
     23
     24        * CMakeLists.txt:
     25        * WebCore.xcodeproj/project.pbxproj: Add new file for the font selection algorithm.
     26        * css/CSSFontFace.cpp:
     27        (WebCore::CSSFontFace::calculateStretch): Used on @font-face blocks and the CSS Font Loading API.
     28        (WebCore::CSSFontFace::setStretch): Fill out the previously stubbed function.
     29        * css/CSSFontFace.h: Add the range member variable to CSSFontFaces.
     30        * css/CSSFontFaceSet.cpp:
     31        (WebCore::CSSFontFaceSet::ensureLocalFontFacesForFamilyRegistered): Now that we care about font-stretch, we need to
     32        look it up for local fonts too. This current approach of an extra FontSelectionValue hanging around with a
     33        FontTraitsMask is very ugly and will be removed soon (https://bugs.webkit.org/show_bug.cgi?id=168889 and
     34        https://bugs.webkit.org/show_bug.cgi?id=168890).
     35        (WebCore::computeFontStretch): Used on @font-face blocks and the CSS Font Loading API.
     36        (WebCore::CSSFontFaceSet::matchingFaces): Educate about font-stretch.
     37        (WebCore::CSSFontFaceSet::fontFace): Migrate to the new font-selection algorithm.
     38        (WebCore::fontFaceComparator): Deleted.
     39        * css/CSSFontFaceSet.h:
     40        * css/CSSFontSelector.cpp:
     41        (WebCore::CSSFontSelector::addFontFaceRule): Educate about font-stretch.
     42        (WebCore::CSSFontSelector::fontRangesForFamily): Ditto.
     43        * css/FontFace.cpp:
     44        (WebCore::FontFace::setStretch): Ditto.
     45        (WebCore::FontFace::stretch): Ditto.
     46        * css/parser/CSSPropertyParser.cpp:
     47        (WebCore::CSSPropertyParser::parseFontFaceDescriptor): Ditto.
     48        * platform/graphics/FontCache.h: Ditto.
     49        * platform/graphics/FontDescription.h:
     50        * platform/graphics/FontSelectionAlgorithm.cpp: Added.
     51        (WebCore::FontSelectionAlgorithm::filterCapability):
     52        (WebCore::FontSelectionAlgorithm::indexOfBestCapabilities):
     53        (WebCore::fontSelectionRequestForTraitsMask):
     54        (WebCore::initialFontSelectionCapabilitiesForTraitsMask):
     55        (WebCore::fontSelectionCapabilitiesForTraitsMask):
     56        * platform/graphics/FontSelectionAlgorithm.h: Added. Taken from FontCacheCoreText.cpp.
     57        (WebCore::FontSelectionValue::FontSelectionValue):
     58        (WebCore::FontSelectionValue::operator float):
     59        (WebCore::FontSelectionValue::rawValue):
     60        (WebCore::FontSelectionValue::maximumValue):
     61        (WebCore::FontSelectionValue::minimumValue):
     62        (WebCore::FontSelectionValue::operator+):
     63        (WebCore::FontSelectionValue::operator-):
     64        (WebCore::FontSelectionValue::operator*):
     65        (WebCore::FontSelectionValue::operator/):
     66        (WebCore::FontSelectionValue::operator==):
     67        (WebCore::FontSelectionValue::operator!=):
     68        (WebCore::FontSelectionValue::operator<):
     69        (WebCore::FontSelectionValue::operator<=):
     70        (WebCore::FontSelectionValue::operator>):
     71        (WebCore::FontSelectionValue::operator>=):
     72        (WebCore::FontSelectionRange::isValid):
     73        (WebCore::FontSelectionRange::expand):
     74        (WebCore::FontSelectionRange::includes):
     75        (WebCore::FontSelectionRequest::operator==):
     76        (WebCore::FontSelectionRequest::operator!=):
     77        (WebCore::FontSelectionRequestKey::FontSelectionRequestKey):
     78        (WebCore::FontSelectionRequestKey::isHashTableDeletedValue):
     79        (WebCore::FontSelectionRequestKey::operator==):
     80        (WebCore::FontSelectionRequestKeyHash::hash):
     81        (WebCore::FontSelectionRequestKeyHash::equal):
     82        (WebCore::FontSelectionCapabilities::expand):
     83        (WebCore::FontSelectionAlgorithm::FontSelectionAlgorithm):
     84        (WebCore::FontSelectionAlgorithm::iterateActiveCapabilitiesWithReturn):
     85        (WebCore::FontSelectionAlgorithm::iterateActiveCapabilities):
     86        * platform/graphics/cocoa/FontCacheCoreText.cpp: Moved to FontSelectionAlgorithm.
     87        (WebCore::stretchFromCoreTextTraits):
     88        (WebCore::FontDatabase::capabilitiesForFontDescriptor):
     89        (WebCore::findClosestFont):
     90        (WebCore::calculateFontSelectionRequest):
     91        (WebCore::platformFontLookupWithFamily):
     92        (WebCore::FontCache::getTraitsInFamily): Deleted.
     93        (WebCore::iterateActiveFontsWithReturn): Deleted.
     94        (WebCore::iterateActiveFonts): Deleted.
     95        (WebCore::findClosestStretch): Deleted.
     96        (WebCore::filterStretch): Deleted.
     97        (WebCore::findClosestStyle): Deleted.
     98        (WebCore::filterStyle): Deleted.
     99        (WebCore::findClosestWeight): Deleted.
     100        (WebCore::filterWeight): Deleted.
     101        (WebCore::computeTargetWeight): Deleted.
     102        * platform/text/TextFlags.h: Moved to FontSelectionAlgorithm.
     103        (WebCore::FontSelectionValue::FontSelectionValue): Deleted.
     104        (WebCore::FontSelectionValue::operator float): Deleted.
     105        (WebCore::FontSelectionValue::operator+): Deleted.
     106        (WebCore::FontSelectionValue::operator-): Deleted.
     107        (WebCore::FontSelectionValue::operator*): Deleted.
     108        (WebCore::FontSelectionValue::operator/): Deleted.
     109        (WebCore::FontSelectionValue::operator==): Deleted.
     110        (WebCore::FontSelectionValue::operator!=): Deleted.
     111        (WebCore::FontSelectionValue::operator<): Deleted.
     112        (WebCore::FontSelectionValue::operator<=): Deleted.
     113        (WebCore::FontSelectionValue::operator>): Deleted.
     114        (WebCore::FontSelectionValue::operator>=): Deleted.
     115        (WebCore::FontSelectionValue::rawValue): Deleted.
     116        (WebCore::FontSelectionValue::maximumValue): Deleted.
     117        (WebCore::FontSelectionValue::minimumValue): Deleted.
     118        (WebCore::FontSelectionRange::isValid): Deleted.
     119        (WebCore::FontSelectionRange::expand): Deleted.
     120        (WebCore::FontSelectionRange::includes): Deleted.
     121        (WebCore::FontSelectionCapabilities::expand): Deleted.
     122
    11232017-03-05  Simon Fraser  <simon.fraser@apple.com>
    2124
  • trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj

    r213423 r213436  
    57485748                C28083401C6DC275001451B6 /* JSFontFace.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C280833D1C6DC22C001451B6 /* JSFontFace.cpp */; };
    57495749                C28083421C6DC96A001451B6 /* JSFontFaceCustom.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C28083411C6DC96A001451B6 /* JSFontFaceCustom.cpp */; };
     5750                C2AB0AF61E6B3C6C001348C5 /* FontSelectionAlgorithm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2AB0AF41E6B3C6C001348C5 /* FontSelectionAlgorithm.cpp */; };
     5751                C2AB0AF71E6B3C6C001348C5 /* FontSelectionAlgorithm.h in Headers */ = {isa = PBXBuildFile; fileRef = C2AB0AF51E6B3C6C001348C5 /* FontSelectionAlgorithm.h */; settings = {ATTRIBUTES = (Private, ); }; };
    57505752                C2E1F43F1D6254E10094625C /* BreakLines.h in Headers */ = {isa = PBXBuildFile; fileRef = BCEA4816097D93020094C9E4 /* BreakLines.h */; settings = {ATTRIBUTES = (Private, ); }; };
    57515753                C2F4E78A1E45BEA1006D7105 /* ComplexTextController.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2F4E7881E45AEDF006D7105 /* ComplexTextController.cpp */; };
     
    1387413876                C280833E1C6DC22C001451B6 /* JSFontFace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSFontFace.h; sourceTree = "<group>"; };
    1387513877                C28083411C6DC96A001451B6 /* JSFontFaceCustom.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSFontFaceCustom.cpp; sourceTree = "<group>"; };
     13878                C2AB0AF41E6B3C6C001348C5 /* FontSelectionAlgorithm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FontSelectionAlgorithm.cpp; sourceTree = "<group>"; };
     13879                C2AB0AF51E6B3C6C001348C5 /* FontSelectionAlgorithm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FontSelectionAlgorithm.h; sourceTree = "<group>"; };
    1387613880                C2F4E7881E45AEDF006D7105 /* ComplexTextController.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ComplexTextController.cpp; sourceTree = "<group>"; };
    1387713881                C2F4E7891E45AEDF006D7105 /* ComplexTextController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ComplexTextController.h; sourceTree = "<group>"; };
     
    2237022374                                379919941200DDF400EA041C /* WOFFFileFormat.cpp */,
    2237122375                                379919951200DDF400EA041C /* WOFFFileFormat.h */,
     22376                                C2AB0AF41E6B3C6C001348C5 /* FontSelectionAlgorithm.cpp */,
     22377                                C2AB0AF51E6B3C6C001348C5 /* FontSelectionAlgorithm.h */,
    2237222378                        );
    2237322379                        path = graphics;
     
    2764027646                                A818721B0977D3C0005826D9 /* NodeList.h in Headers */,
    2764127647                                63189AE30E83A33300012E41 /* NodeRareData.h in Headers */,
     27648                                C2AB0AF71E6B3C6C001348C5 /* FontSelectionAlgorithm.h in Headers */,
    2764227649                                63D7B32D0E78CD3F00F7617C /* NodeRenderStyle.h in Headers */,
    2764327650                                E43105BB16750F1600DB2FB8 /* NodeTraversal.h in Headers */,
     
    2950729514                                1A8A645F1D19FCFC00D0E00F /* ApplePayShippingMethodSelectedEvent.cpp in Sources */,
    2950829515                                1A8A64621D19FCFC00D0E00F /* ApplePayValidateMerchantEvent.cpp in Sources */,
     29516                                C2AB0AF61E6B3C6C001348C5 /* FontSelectionAlgorithm.cpp in Sources */,
    2950929517                                1A8F6BBC0DB55CDC001DB794 /* ApplicationCache.cpp in Sources */,
    2951029518                                1A8F6BBE0DB55CDC001DB794 /* ApplicationCacheGroup.cpp in Sources */,
  • trunk/Source/WebCore/css/CSSFontFace.cpp

    r212513 r213436  
    195195}
    196196
     197std::optional<FontSelectionValue> CSSFontFace::calculateStretch(CSSValue& stretch)
     198{
     199    if (!is<CSSPrimitiveValue>(stretch))
     200        return std::nullopt;
     201
     202    const auto& primitiveValue = downcast<CSSPrimitiveValue>(stretch);
     203    if (primitiveValue.isPercentage() || primitiveValue.isNumber()) {
     204        auto value = primitiveValue.floatValue();
     205        if (value < static_cast<float>(FontSelectionValue::minimumValue()))
     206            return FontSelectionValue::minimumValue();
     207        if (value > static_cast<float>(FontSelectionValue::maximumValue()))
     208            return FontSelectionValue::maximumValue();
     209        return FontSelectionValue(value);
     210    }
     211
     212    switch (primitiveValue.valueID()) {
     213    case CSSValueUltraCondensed:
     214        return FontSelectionValue(50);
     215    case CSSValueExtraCondensed:
     216        return FontSelectionValue(62.5f);
     217    case CSSValueCondensed:
     218        return FontSelectionValue(75);
     219    case CSSValueSemiCondensed:
     220        return FontSelectionValue(87.5f);
     221    case CSSValueNormal:
     222        return FontSelectionValue(100);
     223    case CSSValueSemiExpanded:
     224        return FontSelectionValue(112.5f);
     225    case CSSValueExpanded:
     226        return FontSelectionValue(125);
     227    case CSSValueExtraExpanded:
     228        return FontSelectionValue(150);
     229    case CSSValueUltraExpanded:
     230        return FontSelectionValue(200);
     231    default:
     232        ASSERT_NOT_REACHED();
     233        return std::nullopt;
     234    }
     235}
     236
    197237bool CSSFontFace::setWeight(CSSValue& weight)
    198238{
     
    202242        if (m_cssConnection)
    203243            m_cssConnection->mutableProperties().setProperty(CSSPropertyFontWeight, &weight);
     244
     245        iterateClients(m_clients, [&](Client& client) {
     246            client.fontPropertyChanged(*this);
     247        });
     248
     249        return true;
     250    }
     251
     252    return false;
     253}
     254
     255bool CSSFontFace::setStretch(CSSValue& stretch)
     256{
     257    if (auto parsedStretch = calculateStretch(stretch)) {
     258        m_stretch = FontSelectionRange(parsedStretch.value(), parsedStretch.value());
     259
     260        if (m_cssConnection)
     261            m_cssConnection->mutableProperties().setProperty(CSSPropertyFontStretch, &stretch);
    204262
    205263        iterateClients(m_clients, [&](Client& client) {
  • trunk/Source/WebCore/css/CSSFontFace.h

    r208985 r213436  
    2727
    2828#include "CSSFontFaceRule.h"
     29#include "FontSelectionAlgorithm.h"
    2930#include "FontTaggedSettings.h"
    3031#include "TextFlags.h"
     
    6667    bool setStyle(CSSValue&);
    6768    bool setWeight(CSSValue&);
     69    bool setStretch(CSSValue&);
    6870    bool setUnicodeRange(CSSValue&);
    6971    bool setVariantLigatures(CSSValue&);
     
    7981    const CSSValueList* families() const { return m_families.get(); }
    8082    FontTraitsMask traitsMask() const { return m_traitsMask; }
     83    FontSelectionRange stretch() const { return m_stretch; }
    8184    const Vector<UnicodeRange>& ranges() const { return m_ranges; }
    8285    const FontFeatureSettings& featureSettings() const { return m_featureSettings; }
     
    8487    void setVariantSettings(const FontVariantSettings& variantSettings) { m_variantSettings = variantSettings; }
    8588    void setTraitsMask(FontTraitsMask traitsMask) { m_traitsMask = traitsMask; }
     89    void setStretch(FontSelectionRange stretch) { m_stretch = stretch; }
    8690    bool isLocalFallback() const { return m_isLocalFallback; }
    8791    Status status() const { return m_status; }
    8892    StyleRuleFontFace* cssConnection() const { return m_cssConnection.get(); }
     93    FontSelectionCapabilities fontSelectionCapabilities() const { return fontSelectionCapabilitiesForTraitsMask(m_traitsMask, m_stretch); }
    8994
    9095    static std::optional<FontTraitsMask> calculateStyleMask(CSSValue& style);
    9196    static std::optional<FontTraitsMask> calculateWeightMask(CSSValue& weight);
     97    static std::optional<FontSelectionValue> calculateStretch(CSSValue& stretch);
    9298
    9399    class Client;
     
    174180    WeakPtr<FontFace> m_wrapper;
    175181    Status m_status { Status::Pending };
     182    FontSelectionRange m_stretch { FontSelectionValue(100), FontSelectionValue(100) };
    176183    bool m_isLocalFallback { false };
    177184    bool m_sourcesPopulated { false };
  • trunk/Source/WebCore/css/CSSFontFaceSet.cpp

    r212985 r213436  
    102102        return;
    103103
    104     Vector<FontTraitsMask> traitsMasks = FontCache::singleton().getTraitsInFamily(familyName);
    105     if (traitsMasks.isEmpty())
     104    Vector<FontCache::TraitsAndStretch> traitsAndStretch = FontCache::singleton().getTraitsAndStretchInFamily(familyName);
     105    if (traitsAndStretch.isEmpty())
    106106        return;
    107107
    108108    Vector<Ref<CSSFontFace>> faces;
    109     for (auto mask : traitsMasks) {
     109    for (auto item : traitsAndStretch) {
    110110        Ref<CSSFontFace> face = CSSFontFace::create(nullptr, nullptr, nullptr, true);
    111111       
     
    113113        familyList->append(CSSValuePool::singleton().createFontFamilyValue(familyName));
    114114        face->setFamilies(familyList.get());
    115         face->setTraitsMask(mask);
     115        face->setTraitsMask(item.traits);
     116        face->setStretch(item.stretch);
    116117        face->adoptSource(std::make_unique<CSSFontFaceSource>(face.get(), familyName));
    117118        ASSERT(!face->allSourcesFailed());
     
    312313}
    313314
     315static std::optional<FontSelectionValue> computeFontStretch(MutableStyleProperties& style)
     316{
     317    RefPtr<CSSValue> stretchValue = style.getPropertyCSSValue(CSSPropertyFontStretch).get();
     318    if (!stretchValue)
     319        stretchValue = CSSValuePool::singleton().createIdentifierValue(CSSValueNormal).ptr();
     320
     321    if (auto stretchOptional = CSSFontFace::calculateStretch(*stretchValue))
     322        return stretchOptional.value();
     323    return std::nullopt;
     324}
     325
    314326static HashSet<UChar32> codePointsFromString(StringView stringView)
    315327{
     
    341353        return Exception { SYNTAX_ERR };
    342354
     355    FontSelectionValue stretch;
     356    if (auto stretchOptional = computeFontStretch(style.get()))
     357        stretch = stretchOptional.value();
     358    else
     359        return Exception { SYNTAX_ERR };
     360
    343361    auto family = style->getPropertyCSSValue(CSSPropertyFontFamily);
    344362    if (!is<CSSValueList>(family.get()))
     
    360378        bool found = false;
    361379        for (auto& family : familyOrder) {
    362             auto* faces = fontFace(fontTraitsMask, family);
     380            auto* faces = fontFace(fontTraitsMask, stretch, family);
    363381            if (!faces)
    364382                continue;
     
    395413}
    396414
    397 static bool fontFaceComparator(FontTraitsMask desiredTraitsMaskForComparison, const CSSFontFace& first, const CSSFontFace& second)
    398 {
    399     FontTraitsMask firstTraitsMask = first.traitsMask();
    400     FontTraitsMask secondTraitsMask = second.traitsMask();
    401 
    402     bool firstHasDesiredStyle = firstTraitsMask & desiredTraitsMaskForComparison & FontStyleMask;
    403     bool secondHasDesiredStyle = secondTraitsMask & desiredTraitsMaskForComparison & FontStyleMask;
    404 
    405     if (firstHasDesiredStyle != secondHasDesiredStyle)
    406         return firstHasDesiredStyle;
    407 
    408     if ((desiredTraitsMaskForComparison & FontStyleItalicMask) && !first.isLocalFallback() && !second.isLocalFallback()) {
    409         // Prefer a font that has indicated that it can only support italics to a font that claims to support
    410         // all styles. The specialized font is more likely to be the one the author wants used.
    411         bool firstRequiresItalics = (firstTraitsMask & FontStyleItalicMask) && !(firstTraitsMask & FontStyleNormalMask);
    412         bool secondRequiresItalics = (secondTraitsMask & FontStyleItalicMask) && !(secondTraitsMask & FontStyleNormalMask);
    413         if (firstRequiresItalics != secondRequiresItalics)
    414             return firstRequiresItalics;
    415     }
    416 
    417     if (secondTraitsMask & desiredTraitsMaskForComparison & FontWeightMask)
    418         return false;
    419     if (firstTraitsMask & desiredTraitsMaskForComparison & FontWeightMask)
    420         return true;
    421 
    422     // http://www.w3.org/TR/2011/WD-css3-fonts-20111004/#font-matching-algorithm says :
    423     //   - 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.
    424     //   - 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.
    425     //   - If the desired weight is 400, 500 is checked first and then the rule for desired weights less than 400 is used.
    426     //   - If the desired weight is 500, 400 is checked first and then the rule for desired weights less than 400 is used.
    427 
    428     static const unsigned fallbackRuleSets = 9;
    429     static const unsigned rulesPerSet = 8;
    430     static const FontTraitsMask weightFallbackRuleSets[fallbackRuleSets][rulesPerSet] = {
    431         { FontWeight200Mask, FontWeight300Mask, FontWeight400Mask, FontWeight500Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
    432         { FontWeight100Mask, FontWeight300Mask, FontWeight400Mask, FontWeight500Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
    433         { FontWeight200Mask, FontWeight100Mask, FontWeight400Mask, FontWeight500Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
    434         { FontWeight500Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
    435         { FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
    436         { FontWeight700Mask, FontWeight800Mask, FontWeight900Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask },
    437         { FontWeight800Mask, FontWeight900Mask, FontWeight600Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask },
    438         { FontWeight900Mask, FontWeight700Mask, FontWeight600Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask },
    439         { FontWeight800Mask, FontWeight700Mask, FontWeight600Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask }
    440     };
    441 
    442     unsigned ruleSetIndex = 0;
    443     for (; !(desiredTraitsMaskForComparison & (1 << (FontWeight100Bit + ruleSetIndex))); ruleSetIndex++) { }
    444 
    445     const FontTraitsMask* weightFallbackRule = weightFallbackRuleSets[ruleSetIndex];
    446     for (unsigned i = 0; i < rulesPerSet; ++i) {
    447         if (secondTraitsMask & weightFallbackRule[i])
    448             return false;
    449         if (firstTraitsMask & weightFallbackRule[i])
    450             return true;
    451     }
    452 
    453     return false;
    454 }
    455 
    456 CSSSegmentedFontFace* CSSFontFaceSet::fontFace(FontTraitsMask traitsMask, const AtomicString& family)
     415CSSSegmentedFontFace* CSSFontFaceSet::fontFace(FontTraitsMask traitsMask, FontSelectionValue stretch, const AtomicString& family)
    457416{
    458417    auto iterator = m_facesLookupTable.find(family);
     
    488447    }
    489448
    490     std::stable_sort(candidateFontFaces.begin(), candidateFontFaces.end(), [traitsMask](const CSSFontFace& first, const CSSFontFace& second) {
    491         return fontFaceComparator(traitsMask, first, second);
    492     });
    493     for (auto& candidate : candidateFontFaces)
    494         face->appendFontFace(candidate.get());
     449    if (!candidateFontFaces.isEmpty()) {
     450        Vector<FontSelectionCapabilities> capabilities;
     451        capabilities.reserveInitialCapacity(candidateFontFaces.size());
     452        for (auto& face : candidateFontFaces)
     453            capabilities.uncheckedAppend(face.get().fontSelectionCapabilities());
     454        FontSelectionAlgorithm fontSelectionAlgorithm(fontSelectionRequestForTraitsMask(traitsMask, stretch), capabilities);
     455        std::stable_sort(candidateFontFaces.begin(), candidateFontFaces.end(), [&fontSelectionAlgorithm](const CSSFontFace& first, const CSSFontFace& second) {
     456            auto firstCapabilities = first.fontSelectionCapabilities();
     457            auto secondCapabilities = second.fontSelectionCapabilities();
     458
     459            auto stretchDistanceFirst = fontSelectionAlgorithm.stretchDistance(firstCapabilities).distance;
     460            auto stretchDistanceSecond = fontSelectionAlgorithm.stretchDistance(secondCapabilities).distance;
     461            if (stretchDistanceFirst < stretchDistanceSecond)
     462                return true;
     463            if (stretchDistanceFirst > stretchDistanceSecond)
     464                return false;
     465
     466            auto styleDistanceFirst = fontSelectionAlgorithm.styleDistance(firstCapabilities).distance;
     467            auto styleDistanceSecond = fontSelectionAlgorithm.styleDistance(secondCapabilities).distance;
     468            if (styleDistanceFirst < styleDistanceSecond)
     469                return true;
     470            if (styleDistanceFirst > styleDistanceSecond)
     471                return false;
     472
     473            auto weightDistanceFirst = fontSelectionAlgorithm.weightDistance(firstCapabilities).distance;
     474            auto weightDistanceSecond = fontSelectionAlgorithm.weightDistance(secondCapabilities).distance;
     475            if (weightDistanceFirst < weightDistanceSecond)
     476                return true;
     477            return false;
     478        });
     479        for (auto& candidate : candidateFontFaces)
     480            face->appendFontFace(candidate.get());
     481    }
    495482
    496483    return face.get();
  • trunk/Source/WebCore/css/CSSFontFaceSet.h

    r207396 r213436  
    6868    ExceptionOr<bool> check(const String& font, const String& text);
    6969
    70     CSSSegmentedFontFace* fontFace(FontTraitsMask, const AtomicString& family);
     70    CSSSegmentedFontFace* fontFace(FontTraitsMask, FontSelectionValue stretch, const AtomicString& family);
    7171
    7272    enum class Status { Loading, Loaded };
  • trunk/Source/WebCore/css/CSSFontSelector.cpp

    r212449 r213436  
    146146    RefPtr<CSSValue> fontStyle = style.getPropertyCSSValue(CSSPropertyFontStyle);
    147147    RefPtr<CSSValue> fontWeight = style.getPropertyCSSValue(CSSPropertyFontWeight);
     148    RefPtr<CSSValue> fontStretch = style.getPropertyCSSValue(CSSPropertyFontStretch);
    148149    RefPtr<CSSValue> src = style.getPropertyCSSValue(CSSPropertySrc);
    149150    RefPtr<CSSValue> unicodeRange = style.getPropertyCSSValue(CSSPropertyUnicodeRange);
     
    168169        fontWeight = CSSValuePool::singleton().createIdentifierValue(CSSValueNormal);
    169170
     171    if (!fontStretch)
     172        fontStretch = CSSValuePool::singleton().createIdentifierValue(CSSValueNormal);
     173
    170174    CSSValueList* rangeList = downcast<CSSValueList>(unicodeRange.get());
    171175
     
    182186        return;
    183187    if (!fontFace->setWeight(*fontWeight))
     188        return;
     189    if (!fontFace->setStretch(*fontStretch))
    184190        return;
    185191    if (rangeList && !fontFace->setUnicodeRange(*rangeList))
     
    296302
    297303    AtomicString familyForLookup = resolveGenericFamilyFirst ? resolveGenericFamily(m_document, fontDescription, familyName) : familyName;
    298     auto* face = m_cssFontFaceSet->fontFace(fontDescription.traitsMask(), familyForLookup);
     304    auto* face = m_cssFontFaceSet->fontFace(fontDescription.traitsMask(), fontDescription.stretch(), familyForLookup);
    299305    if (!face) {
    300306        if (!resolveGenericFamilyFirst)
  • trunk/Source/WebCore/css/FontFace.cpp

    r212535 r213436  
    185185}
    186186
    187 ExceptionOr<void> FontFace::setStretch(const String&)
    188 {
    189     // We don't support font-stretch. Swallow the call.
     187ExceptionOr<void> FontFace::setStretch(const String& stretch)
     188{
     189    if (stretch.isEmpty())
     190        return Exception { SYNTAX_ERR };
     191
     192    bool success = false;
     193    if (auto value = parseString(stretch, CSSPropertyFontStretch))
     194        success = m_backing->setStretch(*value);
     195    if (!success)
     196        return Exception { SYNTAX_ERR };
    190197    return { };
    191198}
     
    319326String FontFace::stretch() const
    320327{
    321     return ASCIILiteral("normal");
     328    m_backing->updateStyleIfNeeded();
     329    auto stretch = m_backing->stretch();
     330
     331    auto rangeIsSingleValue = [](FontSelectionRange range, FontSelectionValue value) -> bool {
     332        return range.minimum == value && range.maximum == value;
     333    };
     334
     335    if (rangeIsSingleValue(stretch, FontSelectionValue(50)))
     336        return ASCIILiteral("ultra-condensed");
     337    if (rangeIsSingleValue(stretch, FontSelectionValue(62.5f)))
     338        return ASCIILiteral("extra-condensed");
     339    if (rangeIsSingleValue(stretch, FontSelectionValue(75)))
     340        return ASCIILiteral("condensed");
     341    if (rangeIsSingleValue(stretch, FontSelectionValue(87.5f)))
     342        return ASCIILiteral("semi-condensed");
     343    if (rangeIsSingleValue(stretch, FontSelectionValue(100)))
     344        return ASCIILiteral("normal");
     345    if (rangeIsSingleValue(stretch, FontSelectionValue(112.5f)))
     346        return ASCIILiteral("semi-expanded");
     347    if (rangeIsSingleValue(stretch, FontSelectionValue(125)))
     348        return ASCIILiteral("expanded");
     349    if (rangeIsSingleValue(stretch, FontSelectionValue(150)))
     350        return ASCIILiteral("extra-expanded");
     351    if (rangeIsSingleValue(stretch, FontSelectionValue(200)))
     352        return ASCIILiteral("ultra-expanded");
     353
     354    return String::format("%f-%f", static_cast<float>(stretch.minimum), static_cast<float>(stretch.maximum));
    322355}
    323356
  • trunk/Source/WebCore/css/parser/CSSPropertyParser.cpp

    r213305 r213436  
    41864186        break;
    41874187    case CSSPropertyFontStretch:
    4188         // FIXME: Implement this.
    4189         m_range.consumeIncludingWhitespace();
    4190         parsedValue = CSSValuePool::singleton().createIdentifierValue(CSSValueNormal);
     4188        parsedValue = consumeFontStretch(m_range);
    41914189        break;
    41924190    case CSSPropertyFontStyle: {
  • trunk/Source/WebCore/platform/graphics/FontCache.h

    r213341 r213436  
    204204    // This function exists so CSSFontSelector can have a unified notion of preinstalled fonts and @font-face.
    205205    // It comes into play when you create an @font-face which shares a family name as a preinstalled font.
    206     Vector<FontTraitsMask> getTraitsInFamily(const AtomicString&);
     206    struct TraitsAndStretch {
     207        FontTraitsMask traits;
     208        FontSelectionRange stretch;
     209    };
     210    Vector<TraitsAndStretch> getTraitsAndStretchInFamily(const AtomicString&);
    207211
    208212    WEBCORE_EXPORT RefPtr<Font> fontForFamily(const FontDescription&, const AtomicString&, const FontFeatureSettings* fontFaceFeatures = nullptr, const FontVariantSettings* fontFaceVariantSettings = nullptr, bool checkingAlternateName = false);
  • trunk/Source/WebCore/platform/graphics/FontDescription.h

    r213341 r213436  
    2727
    2828#include "CSSValueKeywords.h"
     29#include "FontSelectionAlgorithm.h"
    2930#include "FontTaggedSettings.h"
    3031#include "TextFlags.h"
  • trunk/Source/WebCore/platform/graphics/cocoa/FontCacheCoreText.cpp

    r213341 r213436  
    663663}
    664664
    665 Vector<FontTraitsMask> FontCache::getTraitsInFamily(const AtomicString& familyName)
     665static FontSelectionValue stretchFromCoreTextTraits(CFDictionaryRef traits)
     666{
     667    auto widthNumber = static_cast<CFNumberRef>(CFDictionaryGetValue(traits, kCTFontWidthTrait));
     668    if (widthNumber) {
     669        // FIXME: The normalization from Core Text's [-1, 1] range to CSS's [50%, 200%] range isn't perfect.
     670        float ctWidth;
     671        auto success = CFNumberGetValue(widthNumber, kCFNumberFloatType, &ctWidth);
     672        ASSERT_UNUSED(success, success);
     673        return FontSelectionValue(ctWidth < 0.5 ? ctWidth * 50 + 100 : ctWidth * 150 + 50);
     674    }
     675    return FontSelectionValue(100);
     676}
     677
     678auto FontCache::getTraitsAndStretchInFamily(const AtomicString& familyName) -> Vector<TraitsAndStretch>
    666679{
    667680    auto familyNameStr = familyName.string().createCFString();
     
    678691        return { };
    679692
    680     Vector<FontTraitsMask> traitsMasks;
    681     traitsMasks.reserveInitialCapacity(numMatches);
     693    Vector<TraitsAndStretch> result;
     694    result.reserveInitialCapacity(numMatches);
    682695    for (CFIndex i = 0; i < numMatches; ++i) {
    683696        auto traits = adoptCF((CFDictionaryRef)CTFontDescriptorCopyAttribute((CTFontDescriptorRef)CFArrayGetValueAtIndex(matchedDescriptors.get(), i), kCTFontTraitsAttribute));
     
    689702            CGFloat weight = 0;
    690703            CFNumberGetValue(weightRef, kCFNumberCGFloatType, &weight);
    691             traitsMasks.uncheckedAppend(toTraitsMask(symbolicTraits, weight));
    692         }
    693     }
    694     return traitsMasks;
     704            auto stretch = stretchFromCoreTextTraits(traits.get());
     705            result.uncheckedAppend({ toTraitsMask(symbolicTraits, weight), { stretch, stretch } });
     706        }
     707    }
     708    return result;
    695709}
    696710
     
    896910        FontSelectionValue weight;
    897911        if (traits) {
    898             auto widthNumber = static_cast<CFNumberRef>(CFDictionaryGetValue(traits.get(), kCTFontWidthTrait));
    899             if (widthNumber) {
    900                 // FIXME: The normalization from Core Text's [-1, 1] range to CSS's [50%, 200%] range isn't perfect.
    901                 float ctWidth;
    902                 auto success = CFNumberGetValue(widthNumber, kCFNumberFloatType, &ctWidth);
    903                 ASSERT_UNUSED(success, success);
    904                 width = FontSelectionValue(ctWidth < 0.5 ? ctWidth * 50 + 100 : ctWidth * 150 + 50);
    905             }
     912            width = stretchFromCoreTextTraits(traits.get());
    906913
    907914            auto symbolicTraitsNumber = static_cast<CFNumberRef>(CFDictionaryGetValue(traits.get(), kCTFontSymbolicTrait));
     
    927934    }
    928935
    929     static const FontSelectionValue stretchThreshold()
    930     {
    931         static NeverDestroyed<FontSelectionValue> threshold(100);
    932         return threshold.get();
    933     }
    934 
    935     static const FontSelectionValue italicThreshold()
    936     {
    937         static NeverDestroyed<FontSelectionValue> threshold(20);
    938         return threshold.get();
    939     }
    940 
    941     static const FontSelectionValue weightThreshold()
    942     {
    943         static NeverDestroyed<FontSelectionValue> threshold(500);
    944         return threshold.get();
    945     }
    946 
    947936private:
    948937    friend class NeverDestroyed<FontDatabase>;
     
    954943};
    955944
    956 template <typename T>
    957 using IterateActiveFontsWithReturnCallback = std::function<std::optional<T>(const FontDatabase::InstalledFont&, size_t)>;
    958 
    959 template <typename T>
    960 inline std::optional<T> iterateActiveFontsWithReturn(const FontDatabase::InstalledFontFamily& installedFonts, const std::unique_ptr<bool[]>& filter, IterateActiveFontsWithReturnCallback<T> callback)
    961 {
    962     for (size_t i = 0; i < installedFonts.size(); ++i) {
    963         if (!filter[i])
    964             continue;
    965         if (auto result = callback(installedFonts.installedFonts[i], i))
    966             return result;
    967     }
    968     return std::nullopt;
    969 }
    970 
    971 template <typename T>
    972 inline void iterateActiveFonts(const FontDatabase::InstalledFontFamily& installedFonts, const std::unique_ptr<bool[]>& filter, T callback)
    973 {
    974     iterateActiveFontsWithReturn<int>(installedFonts, filter, [&](const FontDatabase::InstalledFont& font, size_t i) -> std::optional<int> {
    975         callback(font, i);
    976         return std::nullopt;
    977     });
    978 }
    979 
    980 static inline std::optional<FontSelectionValue> findClosestStretch(FontSelectionValue targetStretch, const FontDatabase::InstalledFontFamily& installedFonts, const std::unique_ptr<bool[]>& filter)
    981 {
    982     std::function<float(const FontDatabase::InstalledFont&)> computeScore;
    983 
    984     if (targetStretch >= FontDatabase::stretchThreshold()) {
    985         auto threshold = std::max(targetStretch, installedFonts.capabilities.width.maximum);
    986         computeScore = [&, threshold](const FontDatabase::InstalledFont& font) -> float {
    987             auto width = font.capabilities.width;
    988             ASSERT(width.isValid());
    989             if (width.includes(targetStretch))
    990                 return 0;
    991             ASSERT(width.minimum > targetStretch || width.maximum < targetStretch);
    992             if (width.minimum > targetStretch)
    993                 return width.minimum - targetStretch;
    994             ASSERT(width.maximum < targetStretch);
    995             return threshold - width.maximum;
    996         };
    997     } else {
    998         ASSERT(targetStretch < FontDatabase::stretchThreshold());
    999         auto threshold = std::min(targetStretch, installedFonts.capabilities.width.minimum);
    1000         computeScore = [&, threshold](const FontDatabase::InstalledFont& font) -> float {
    1001             auto width = font.capabilities.width;
    1002             if (width.includes(targetStretch))
    1003                 return 0;
    1004             ASSERT(width.minimum > targetStretch || width.maximum < targetStretch);
    1005             if (width.maximum < targetStretch)
    1006                 return targetStretch - width.maximum;
    1007             ASSERT(width.minimum > targetStretch);
    1008             return width.minimum - threshold;
    1009         };
    1010     }
    1011 
    1012     size_t closestIndex = 0;
    1013     std::optional<float> minimumScore;
    1014     iterateActiveFonts(installedFonts, filter, [&](auto& installedFont, size_t i) {
    1015         auto score = computeScore(installedFont);
    1016         if (!minimumScore || score < minimumScore.value()) {
    1017             minimumScore = score;
    1018             closestIndex = i;
    1019         }
    1020     });
    1021 
    1022     if (!minimumScore)
    1023         return std::nullopt;
    1024     auto& winner = installedFonts.installedFonts[closestIndex];
    1025     auto width = winner.capabilities.width;
    1026     if (width.includes(targetStretch))
    1027         return targetStretch;
    1028     if (width.minimum > targetStretch)
    1029         return width.minimum;
    1030     ASSERT(width.maximum < targetStretch);
    1031     return width.maximum;
    1032 }
    1033 
    1034 static inline void filterStretch(FontSelectionValue target, const FontDatabase::InstalledFontFamily& installedFonts, std::unique_ptr<bool[]>& filter)
    1035 {
    1036     iterateActiveFonts(installedFonts, filter, [&](auto& installedFont, size_t i) {
    1037         if (!installedFont.capabilities.width.includes(target))
    1038             filter[i] = false;
    1039     });
    1040 }
    1041 
    1042 static inline std::optional<FontSelectionValue> findClosestStyle(FontSelectionValue targetStyle, const FontDatabase::InstalledFontFamily& installedFonts, const std::unique_ptr<bool[]>& filter)
    1043 {
    1044     std::function<float(const FontDatabase::InstalledFont&)> computeScore;
    1045 
    1046     if (targetStyle >= FontDatabase::italicThreshold()) {
    1047         auto threshold = std::max(targetStyle, installedFonts.capabilities.slope.maximum);
    1048         computeScore = [&, threshold](const FontDatabase::InstalledFont& font) -> float {
    1049             auto slope = font.capabilities.slope;
    1050             ASSERT(slope.isValid());
    1051             if (slope.includes(targetStyle))
    1052                 return 0;
    1053             ASSERT(slope.minimum > targetStyle || slope.maximum < targetStyle);
    1054             if (slope.minimum > targetStyle)
    1055                 return slope.minimum - targetStyle;
    1056             ASSERT(targetStyle > slope.maximum);
    1057             return threshold - slope.maximum;
    1058         };
    1059     } else if (targetStyle >= FontSelectionValue()) {
    1060         auto threshold = std::max(targetStyle, installedFonts.capabilities.slope.maximum);
    1061         computeScore = [&, threshold](const FontDatabase::InstalledFont& font) -> float {
    1062             auto slope = font.capabilities.slope;
    1063             ASSERT(slope.isValid());
    1064             if (slope.includes(targetStyle))
    1065                 return 0;
    1066             ASSERT(slope.minimum > targetStyle || slope.maximum < targetStyle);
    1067             if (slope.maximum >= FontSelectionValue() && slope.maximum < targetStyle)
    1068                 return targetStyle - slope.maximum;
    1069             if (slope.minimum > targetStyle)
    1070                 return slope.minimum;
    1071             ASSERT(slope.maximum < FontSelectionValue());
    1072             return threshold - slope.maximum;
    1073         };
    1074     } else if (targetStyle > -FontDatabase::italicThreshold()) {
    1075         auto threshold = std::min(targetStyle, installedFonts.capabilities.slope.minimum);
    1076         computeScore = [&, threshold](const FontDatabase::InstalledFont& font) -> float {
    1077             auto slope = font.capabilities.slope;
    1078             ASSERT(slope.isValid());
    1079             if (slope.includes(targetStyle))
    1080                 return 0;
    1081             ASSERT(slope.minimum > targetStyle || slope.maximum < targetStyle);
    1082             if (slope.minimum > targetStyle && slope.minimum <= FontSelectionValue())
    1083                 return slope.minimum - targetStyle;
    1084             if (slope.maximum < targetStyle)
    1085                 return -slope.maximum;
    1086             ASSERT(slope.minimum > FontSelectionValue());
    1087             return slope.minimum - threshold;
    1088         };
    1089     } else {
    1090         ASSERT(targetStyle <= -FontDatabase::italicThreshold());
    1091         auto threshold = std::min(targetStyle, installedFonts.capabilities.slope.minimum);
    1092         computeScore = [&, threshold](const FontDatabase::InstalledFont& font) -> float {
    1093             auto slope = font.capabilities.slope;
    1094             ASSERT(slope.isValid());
    1095             if (slope.includes(targetStyle))
    1096                 return 0;
    1097             ASSERT(slope.minimum > targetStyle || slope.maximum < targetStyle);
    1098             if (slope.maximum < targetStyle)
    1099                 return targetStyle - slope.maximum;
    1100             ASSERT(slope.minimum > targetStyle);
    1101             return slope.minimum - threshold;
    1102         };
    1103     }
    1104 
    1105     size_t closestIndex = 0;
    1106     std::optional<float> minimumScore;
    1107     iterateActiveFonts(installedFonts, filter, [&](auto& installedFont, size_t i) {
    1108         auto score = computeScore(installedFont);
    1109         if (!minimumScore || score < minimumScore.value()) {
    1110             minimumScore = score;
    1111             closestIndex = i;
    1112         }
    1113     });
    1114 
    1115     if (!minimumScore)
    1116         return std::nullopt;
    1117     auto& winner = installedFonts.installedFonts[closestIndex];
    1118     auto slope = winner.capabilities.slope;
    1119     if (slope.includes(targetStyle))
    1120         return targetStyle;
    1121     if (slope.minimum > targetStyle)
    1122         return slope.minimum;
    1123     ASSERT(slope.maximum < targetStyle);
    1124     return slope.maximum;
    1125 }
    1126 
    1127 static inline void filterStyle(FontSelectionValue target, const FontDatabase::InstalledFontFamily& installedFonts, std::unique_ptr<bool[]>& filter)
    1128 {
    1129     iterateActiveFonts(installedFonts, filter, [&](auto& installedFont, size_t i) {
    1130         if (!installedFont.capabilities.slope.includes(target))
    1131             filter[i] = false;
    1132     });
    1133 }
    1134 
    1135 static inline std::optional<FontSelectionValue> findClosestWeight(FontSelectionValue targetWeight, const FontDatabase::InstalledFontFamily& installedFonts, const std::unique_ptr<bool[]>& filter)
    1136 {
    1137     {
    1138         // The spec states: "If the desired weight is 400, 500 is checked first ... If the desired weight is 500, 400 is checked first"
    1139         IterateActiveFontsWithReturnCallback<FontSelectionValue> searchFor400 = [&](const FontDatabase::InstalledFont& font, size_t) -> std::optional<FontSelectionValue> {
    1140             if (font.capabilities.weight.includes(FontSelectionValue(400)))
    1141                 return FontSelectionValue(400);
    1142             return std::nullopt;
    1143         };
    1144         IterateActiveFontsWithReturnCallback<FontSelectionValue> searchFor500 = [&](const FontDatabase::InstalledFont& font, size_t) -> std::optional<FontSelectionValue> {
    1145             if (font.capabilities.weight.includes(FontSelectionValue(500)))
    1146                 return FontSelectionValue(500);
    1147             return std::nullopt;
    1148         };
    1149         if (targetWeight == FontSelectionValue(400)) {
    1150             if (auto result = iterateActiveFontsWithReturn(installedFonts, filter, searchFor400))
    1151                 return result;
    1152             if (auto result = iterateActiveFontsWithReturn(installedFonts, filter, searchFor500))
    1153                 return result;
    1154         } else if (targetWeight == FontSelectionValue(500)) {
    1155             if (auto result = iterateActiveFontsWithReturn(installedFonts, filter, searchFor500))
    1156                 return result;
    1157             if (auto result = iterateActiveFontsWithReturn(installedFonts, filter, searchFor400))
    1158                 return result;
    1159         }
    1160     }
    1161 
    1162     std::function<float(const FontDatabase::InstalledFont&)> computeScore;
    1163     if (targetWeight <= FontDatabase::weightThreshold()) {
    1164         auto threshold = std::min(targetWeight, installedFonts.capabilities.weight.minimum);
    1165         computeScore = [&, threshold](const FontDatabase::InstalledFont& font) -> FontSelectionValue {
    1166             auto weight = font.capabilities.weight;
    1167             if (weight.includes(targetWeight))
    1168                 return FontSelectionValue();
    1169             ASSERT(weight.minimum > targetWeight || weight.maximum < targetWeight);
    1170             if (weight.maximum < targetWeight)
    1171                 return targetWeight - weight.maximum;
    1172             ASSERT(weight.minimum > targetWeight);
    1173             return weight.minimum - threshold;
    1174         };
    1175     } else {
    1176         ASSERT(targetWeight > FontDatabase::weightThreshold());
    1177         auto threshold = std::max(targetWeight, installedFonts.capabilities.weight.maximum);
    1178         computeScore = [&, threshold](const FontDatabase::InstalledFont& font) -> FontSelectionValue {
    1179             auto weight = font.capabilities.weight;
    1180             if (weight.includes(targetWeight))
    1181                 return FontSelectionValue();
    1182             ASSERT(weight.minimum > targetWeight || weight.maximum < targetWeight);
    1183             if (weight.minimum > targetWeight)
    1184                 return weight.minimum - targetWeight;
    1185             ASSERT(weight.maximum < targetWeight);
    1186             return threshold - weight.maximum;
    1187         };
    1188     }
    1189 
    1190     size_t closestIndex = 0;
    1191     std::optional<float> minimumScore;
    1192     iterateActiveFonts(installedFonts, filter, [&](auto& installedFont, size_t i) {
    1193         auto score = computeScore(installedFont);
    1194         if (!minimumScore || score < minimumScore.value()) {
    1195             minimumScore = score;
    1196             closestIndex = i;
    1197         }
    1198     });
    1199 
    1200     if (!minimumScore)
    1201         return std::nullopt;
    1202     auto& winner = installedFonts.installedFonts[closestIndex];
    1203     auto weight = winner.capabilities.weight;
    1204     if (weight.includes(targetWeight))
    1205         return targetWeight;
    1206     if (weight.minimum > targetWeight)
    1207         return weight.minimum;
    1208     ASSERT(weight.maximum < targetWeight);
    1209     return weight.maximum;
    1210 }
    1211 
    1212 static inline void filterWeight(FontSelectionValue target, const FontDatabase::InstalledFontFamily& installedFonts, std::unique_ptr<bool[]>& filter)
    1213 {
    1214     iterateActiveFonts(installedFonts, filter, [&](auto& installedFont, size_t i) {
    1215         if (!installedFont.capabilities.weight.includes(target))
    1216             filter[i] = false;
    1217     });
    1218 }
    1219 
    1220 static inline FontSelectionValue computeTargetWeight(FontWeight weight)
    1221 {
     945static const FontDatabase::InstalledFont* findClosestFont(const FontDatabase::InstalledFontFamily& familyFonts, FontSelectionRequest fontSelectionRequest)
     946{
     947    Vector<FontSelectionCapabilities> capabilities;
     948    capabilities.reserveInitialCapacity(familyFonts.size());
     949    for (auto& font : familyFonts.installedFonts)
     950        capabilities.uncheckedAppend(font.capabilities);
     951    FontSelectionAlgorithm fontSelectionAlgorithm(fontSelectionRequest, capabilities, familyFonts.capabilities);
     952    return &familyFonts.installedFonts[fontSelectionAlgorithm.indexOfBestCapabilities()];
     953}
     954
     955static FontSelectionRequest calculateFontSelectionRequest(CTFontSymbolicTraits requestedTraits, FontWeight weight, FontSelectionValue stretch)
     956{
     957    FontSelectionRequest result;
    1222958    switch (weight) {
    1223959    case FontWeight100:
    1224         return FontSelectionValue(100);
     960        result.weight = FontSelectionValue(100);
     961        break;
    1225962    case FontWeight200:
    1226         return FontSelectionValue(200);
     963        result.weight = FontSelectionValue(200);
     964        break;
    1227965    case FontWeight300:
    1228         return FontSelectionValue(300);
     966        result.weight = FontSelectionValue(300);
     967        break;
    1229968    case FontWeight400:
    1230         return FontSelectionValue(400);
     969        result.weight = FontSelectionValue(400);
     970        break;
    1231971    case FontWeight500:
    1232         return FontSelectionValue(500);
     972        result.weight = FontSelectionValue(500);
     973        break;
    1233974    case FontWeight600:
    1234         return FontSelectionValue(600);
     975        result.weight = FontSelectionValue(600);
     976        break;
    1235977    case FontWeight700:
    1236         return FontSelectionValue(700);
     978        result.weight = FontSelectionValue(700);
     979        break;
    1237980    case FontWeight800:
    1238         return FontSelectionValue(800);
     981        result.weight = FontSelectionValue(800);
     982        break;
    1239983    case FontWeight900:
    1240         return FontSelectionValue(900);
    1241     default:
    1242         ASSERT_NOT_REACHED();
    1243         return FontSelectionValue(400);
    1244     }
    1245 }
    1246 
    1247 static const FontDatabase::InstalledFont* findClosestFont(const FontDatabase::InstalledFontFamily& familyFonts, CTFontSymbolicTraits requestedTraits, FontWeight weight, FontSelectionValue stretch)
    1248 {
    1249     ASSERT(!familyFonts.isEmpty());
    1250 
    1251     // Parallel to familyFonts.
    1252     std::unique_ptr<bool[]> filter { new bool[familyFonts.size()] };
    1253     for (size_t i = 0; i < familyFonts.size(); ++i)
    1254         filter[i] = true;
    1255 
    1256     if (auto closestStretch = findClosestStretch(stretch, familyFonts, filter))
    1257         filterStretch(closestStretch.value(), familyFonts, filter);
     984        result.weight = FontSelectionValue(900);
     985        break;
     986    default:
     987        ASSERT_NOT_REACHED();
     988        result.weight = FontSelectionValue(400);
     989        break;
     990    }
     991
     992    result.width = stretch;
     993
     994    if (requestedTraits & kCTFontTraitItalic)
     995        result.slope = italicThreshold();
    1258996    else
    1259         return nullptr;
    1260 
    1261     FontSelectionValue targetStyle = requestedTraits & kCTFontTraitItalic ? FontDatabase::italicThreshold() : FontSelectionValue();
    1262     if (auto closestStyle = findClosestStyle(targetStyle, familyFonts, filter))
    1263         filterStyle(closestStyle.value(), familyFonts, filter);
    1264     else
    1265         return nullptr;
    1266 
    1267     FontSelectionValue targetWeight = computeTargetWeight(weight);
    1268     if (auto closestWeight = findClosestWeight(targetWeight, familyFonts, filter))
    1269         filterWeight(closestWeight.value(), familyFonts, filter);
    1270     else
    1271         return nullptr;
    1272 
    1273     return iterateActiveFontsWithReturn<const FontDatabase::InstalledFont*>(familyFonts, filter, [](const FontDatabase::InstalledFont& font, size_t) {
    1274         return &font;
    1275     }).value_or(nullptr);
     997        result.slope = FontSelectionValue();
     998
     999    return result;
    12761000}
    12771001
     
    12831007    if (!isSystemFont(family) && whitelist.size() && !whitelist.contains(family))
    12841008        return nullptr;
    1285 
    12861009
    12871010#if SHOULD_USE_CORE_TEXT_FONT_LOOKUP
     
    13011024        if (!postScriptFont.fontDescriptor)
    13021025            return nullptr;
    1303         if (((requestedTraits & kCTFontTraitItalic) && postScriptFont.capabilities.slope.maximum < FontDatabase::italicThreshold())
    1304             || (weight >= FontWeight600 && postScriptFont.capabilities.weight.maximum < FontSelectionValue(600))) {
     1026        if (((requestedTraits & kCTFontTraitItalic) && postScriptFont.capabilities.slope.maximum < italicThreshold())
     1027            || (weight >= FontWeight600 && postScriptFont.capabilities.weight.maximum < boldThreshold())) {
    13051028            auto postScriptFamilyName = adoptCF(static_cast<CFStringRef>(CTFontDescriptorCopyAttribute(postScriptFont.fontDescriptor.get(), kCTFontFamilyNameAttribute)));
    13061029            if (!postScriptFamilyName)
     
    13091032            if (familyFonts.isEmpty())
    13101033                return nullptr;
    1311             if (const auto* installedFont = findClosestFont(familyFonts, requestedTraits, weight, stretch)) {
     1034            if (const auto* installedFont = findClosestFont(familyFonts, calculateFontSelectionRequest(requestedTraits, weight, stretch))) {
    13121035                if (!installedFont->fontDescriptor)
    13131036                    return nullptr;
     
    13191042    }
    13201043
    1321     if (const auto* installedFont = findClosestFont(familyFonts, requestedTraits, weight, stretch))
     1044    if (const auto* installedFont = findClosestFont(familyFonts, calculateFontSelectionRequest(requestedTraits, weight, stretch)))
    13221045        return adoptCF(CTFontCreateWithFontDescriptor(installedFont->fontDescriptor.get(), size, nullptr));
    13231046
  • trunk/Source/WebCore/platform/graphics/freetype/FontCacheFreeType.cpp

    r212513 r213436  
    139139}
    140140
    141 Vector<FontTraitsMask> FontCache::getTraitsInFamily(const AtomicString&)
     141auto FontCache::getTraitsAndStretchInFamily(const AtomicString& familyName) -> Vector<TraitsAndStretch>
    142142{
    143143    return { };
  • trunk/Source/WebCore/platform/graphics/win/FontCacheWin.cpp

    r212513 r213436  
    560560}
    561561
    562 Vector<FontTraitsMask> FontCache::getTraitsInFamily(const AtomicString& familyName)
     562auto FontCache::getTraitsAndStretchInFamily(const AtomicString& familyName) -> Vector<TraitsAndStretch>
    563563{
    564564    HWndDC hdc(0);
     
    573573    TraitsInFamilyProcData procData(familyName);
    574574    EnumFontFamiliesEx(hdc, &logFont, traitsInFamilyEnumProc, reinterpret_cast<LPARAM>(&procData), 0);
    575     Vector<FontTraitsMask> result;
     575    Vector<TraitsAndStretch> result;
    576576    result.reserveInitialCapacity(procData.m_traitsMasks.size());
    577577    for (unsigned mask : procData.m_traitsMasks)
    578         result.uncheckedAppend(static_cast<FontTraitsMask>(mask));
     578        result.uncheckedAppend({ static_cast<FontTraitsMask>(mask), FontSelectionRange(FontSelectionValue(), FontSelectionValue()) });
    579579    return result;
    580580}
  • trunk/Source/WebCore/platform/text/TextFlags.h

    r213341 r213436  
    2525
    2626#pragma once
    27 
    28 #include <wtf/NeverDestroyed.h>
    2927
    3028namespace WebCore {
     
    399397};
    400398
    401 // Unclamped, unchecked, signed fixed-point number representing a value used for font variations.
    402 // Sixteen bits in total, one sign bit, two fractional bits, means the smallest positive representable value is 0.25,
    403 // the maximum representable value is 8191.75, and the minimum representable value is -8192.
    404 class FontSelectionValue {
    405 public:
    406     FontSelectionValue() = default;
    407 
    408     // Explicit because it is lossy.
    409     explicit FontSelectionValue(int x)
    410         : m_backing(x * fractionalEntropy)
    411     {
    412     }
    413 
    414     // Explicit because it is lossy.
    415     explicit FontSelectionValue(float x)
    416         : m_backing(x * fractionalEntropy)
    417     {
    418     }
    419 
    420     operator float() const
    421     {
    422         // floats have 23 fractional bits, but only 14 fractional bits are necessary, so every value can be represented losslessly.
    423         return m_backing / static_cast<float>(fractionalEntropy);
    424     }
    425 
    426     FontSelectionValue operator+(const FontSelectionValue other) const
    427     {
    428         return FontSelectionValue(m_backing + other.m_backing, RawTag::RawTag);
    429     }
    430 
    431     FontSelectionValue operator-(const FontSelectionValue other) const
    432     {
    433         return FontSelectionValue(m_backing - other.m_backing, RawTag::RawTag);
    434     }
    435 
    436     FontSelectionValue operator*(const FontSelectionValue other) const
    437     {
    438         return FontSelectionValue(static_cast<int32_t>(m_backing) * other.m_backing / fractionalEntropy, RawTag::RawTag);
    439     }
    440 
    441     FontSelectionValue operator/(const FontSelectionValue other) const
    442     {
    443         return FontSelectionValue(static_cast<int32_t>(m_backing) / other.m_backing * fractionalEntropy, RawTag::RawTag);
    444     }
    445 
    446     FontSelectionValue operator-() const
    447     {
    448         return FontSelectionValue(-m_backing, RawTag::RawTag);
    449     }
    450 
    451     bool operator==(const FontSelectionValue other) const
    452     {
    453         return m_backing == other.m_backing;
    454     }
    455 
    456     bool operator!=(const FontSelectionValue other) const
    457     {
    458         return !operator==(other);
    459     }
    460 
    461     bool operator<(const FontSelectionValue other) const
    462     {
    463         return m_backing < other.m_backing;
    464     }
    465 
    466     bool operator<=(const FontSelectionValue other) const
    467     {
    468         return m_backing <= other.m_backing;
    469     }
    470 
    471     bool operator>(const FontSelectionValue other) const
    472     {
    473         return m_backing > other.m_backing;
    474     }
    475 
    476     bool operator>=(const FontSelectionValue other) const
    477     {
    478         return m_backing >= other.m_backing;
    479     }
    480 
    481     int16_t rawValue() const
    482     {
    483         return m_backing;
    484     }
    485 
    486     static FontSelectionValue maximumValue()
    487     {
    488         static NeverDestroyed<FontSelectionValue> result = FontSelectionValue(std::numeric_limits<int16_t>::max(), RawTag::RawTag);
    489         return result.get();
    490     }
    491 
    492     static FontSelectionValue minimumValue()
    493     {
    494         static NeverDestroyed<FontSelectionValue> result = FontSelectionValue(std::numeric_limits<int16_t>::min(), RawTag::RawTag);
    495         return result.get();
    496     }
    497 
    498 private:
    499     enum class RawTag { RawTag };
    500 
    501     FontSelectionValue(int16_t rawValue, RawTag)
    502         : m_backing(rawValue)
    503     {
    504     }
    505 
    506     static constexpr int fractionalEntropy = 4;
    507     int16_t m_backing { 0 };
    508 };
    509 
    510 // [Inclusive, Inclusive]
    511 struct FontSelectionRange {
    512     bool isValid() const
    513     {
    514         return minimum <= maximum;
    515     }
    516 
    517     void expand(const FontSelectionRange& other)
    518     {
    519         ASSERT(other.isValid());
    520         if (!isValid())
    521             *this = other;
    522         else {
    523             minimum = std::min(minimum, other.minimum);
    524             maximum = std::max(maximum, other.maximum);
    525         }
    526         ASSERT(isValid());
    527     }
    528 
    529     bool includes(FontSelectionValue target) const
    530     {
    531         return target >= minimum && target <= maximum;
    532     }
    533 
    534     FontSelectionValue minimum { FontSelectionValue(1) };
    535     FontSelectionValue maximum { FontSelectionValue(0) };
    536 };
    537 
    538 struct FontSelectionRequest {
    539     FontSelectionValue weight;
    540     FontSelectionValue width;
    541     FontSelectionValue slope;
    542 };
    543 
    544 struct FontSelectionCapabilities {
    545     void expand(const FontSelectionCapabilities& capabilities)
    546     {
    547         weight.expand(capabilities.weight);
    548         width.expand(capabilities.width);
    549         slope.expand(capabilities.slope);
    550     }
    551 
    552     FontSelectionRange weight;
    553     FontSelectionRange width;
    554     FontSelectionRange slope;
    555 };
    556 
    557399enum class Kerning {
    558400    Auto,
Note: See TracChangeset for help on using the changeset viewer.