Changeset 214572 in webkit
- Timestamp:
- Mar 29, 2017 4:12:08 PM (7 years ago)
- Location:
- trunk
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r214570 r214572 1 2017-03-29 Myles C. Maxfield <mmaxfield@apple.com> 2 3 Try to normalize variation ranges 4 https://bugs.webkit.org/show_bug.cgi?id=170119 5 6 Reviewed by Simon Fraser. 7 8 * fast/text/variations/font-selection-properties-expected.html: 9 1 10 2017-03-29 Ryan Haddad <ryanhaddad@apple.com> 2 11 -
trunk/LayoutTests/fast/text/variations/font-selection-properties-expected.html
r213505 r214572 11 11 <body> 12 12 This test makes sure that the font selection properties affect font variations. 13 <div style="font-family: Boxis; font-variation-settings: 'wdth' 900;">Hello</div>13 <div style="font-family: Boxis; font-variation-settings: 'wdth' 5.6666;">Hello</div> 14 14 <div style="font-family: Boxis; font-variation-settings: 'wdth' 100;">Hello</div> 15 15 </body> -
trunk/Source/WebCore/ChangeLog
r214571 r214572 1 2017-03-29 Myles C. Maxfield <mmaxfield@apple.com> 2 3 Try to normalize variation ranges 4 https://bugs.webkit.org/show_bug.cgi?id=170119 5 6 Reviewed by Simon Fraser. 7 8 TrueType GX-style variation fonts use one particular scale for values on their 9 weight/width/slope axes - usually the values lie between -1 and 1 on that scale. 10 However, OpenType 1.8-style fonts use the CSS scale for values on these axes. 11 For the purposes of font selection, these values need to lie on the same scale. 12 However, when font selection is completed and the variation values are actually 13 being applied to the fonts, values which lie on the font's actual scale need to 14 be applied. This patch adds normalize*() and denormalize*() functions to perform 15 both of these operations. 16 17 The conversion itself between the two scales isn't an exact mapping. Mapping 18 slope is just a linear relationship with 0deg <=> 0 and 20deg <=> 1 (as per the 19 CSS Fonts spec). Mapping widths is similar, it uses a 2-component piecewise 20 linear relationship which includes the values given in the Microsoft OpenType 21 spec for the OS/2 table's usWidthClass field. Weights are more difficult, so I 22 plotted the CSS weights and the GX-style weights for every style of San 23 Francisco, saw that the relationship appears to be linear, and ran a linear 24 regression to compute the line equation. 25 26 As for the actual discrimination of determining whether a font is a GX-style 27 font or not, we can use the presence of the 'STAT' table. This table didn't 28 exist when GX fonts were being created, and OpenType 1.8 variable fonts are 29 required to have this table. 30 31 Facebook uses the string ".SFNSText" in their @font-face blocks. This font is 32 a variation font, but uses the GX-style values. Facebook asks us to create 33 this font with a weight of 700, and because the values in the font are around 34 1.0, we were erroneously thinking that the font wasn't bold, so we were then 35 applying synthetic bold. This was causing text on facebook to look fuzzy and 36 ugly. 37 38 Test: fast/text/variations/font-selection-properties-expected.html 39 40 * platform/graphics/cocoa/FontCacheCoreText.cpp: 41 (WebCore::isGXVariableFont): 42 (WebCore::normalizeWeight): 43 (WebCore::normalizeSlope): 44 (WebCore::denormalizeWeight): 45 (WebCore::denormalizeWidth): 46 (WebCore::denormalizeSlope): 47 (WebCore::normalizeWidth): 48 (WebCore::preparePlatformFont): Instead of using FontSelectionValues for the 49 intermediate values, we should use floats instead. This is because 50 FontSelectionValues are fixed-point numbers with the denominator having 2 bits. 51 When using this data type to represent values on the GX scale, which are usually 52 between 0 and 1, you lose a lot of fidelity. Instead, our intermediate 53 calculations should be done with floats, and converted to FontSelectionValues at 54 the end when they are representative of values on the CSS scale. 55 (WebCore::stretchFromCoreTextTraits): 56 (WebCore::fontWeightFromCoreText): 57 (WebCore::extractVariationBounds): 58 (WebCore::variationCapabilitiesForFontDescriptor): 59 (WebCore::capabilitiesForFontDescriptor): 60 1 61 2017-03-29 Saam Barati <sbarati@apple.com> 2 62 -
trunk/Source/WebCore/platform/graphics/cocoa/FontCacheCoreText.cpp
r214364 r214572 429 429 return CFStringGetLength(name.get()) > 0 && CFStringGetCharacterAtIndex(name.get(), 0) == '.'; 430 430 } 431 #endif 431 432 static inline bool isGXVariableFont(CTFontRef font) 433 { 434 auto tables = adoptCF(CTFontCopyAvailableTables(font, kCTFontTableOptionNoOptions)); 435 if (!tables) 436 return false; 437 auto size = CFArrayGetCount(tables.get()); 438 for (CFIndex i = 0; i < size; ++i) { 439 // This is so yucky. 440 // https://developer.apple.com/reference/coretext/1510774-ctfontcopyavailabletables 441 // "The returned set will contain unboxed values, which can be extracted like so:" 442 // "CTFontTableTag tag = (CTFontTableTag)(uintptr_t)CFArrayGetValueAtIndex(tags, index);" 443 CTFontTableTag tableTag = static_cast<CTFontTableTag>(reinterpret_cast<uintptr_t>(CFArrayGetValueAtIndex(tables.get(), i))); 444 if (tableTag == 'stat') 445 return false; 446 } 447 return true; 448 } 449 450 // These values were calculated by performing a linear regression on the CSS weights/widths/slopes and Core Text weights/widths/slopes of San Francisco. 451 // FIXME: <rdar://problem/31312602> Get the real values from Core Text. 452 static inline float normalizeWeight(float value) 453 { 454 return 523.7 * value - 109.3; 455 } 456 457 static inline float normalizeSlope(float value) 458 { 459 return value * 300; 460 } 461 462 static inline float denormalizeWeight(float value) 463 { 464 return (value + 109.3) / 523.7; 465 } 466 467 static inline float denormalizeWidth(float value) 468 { 469 if (value < 125) 470 return (value - 100) / 50; 471 return (value - 50) / 150; 472 } 473 474 static inline float denormalizeSlope(float value) 475 { 476 return value / 300; 477 } 478 #endif 479 480 static inline float normalizeWidth(float value) 481 { 482 if (value < 0.5) 483 return value * 50 + 100; 484 return value * 150 + 50; 485 } 432 486 433 487 RetainPtr<CTFontRef> preparePlatformFont(CTFontRef originalFont, TextRenderingMode textRenderingMode, const FontFeatureSettings* fontFaceFeatures, const FontVariantSettings* fontFaceVariantSettings, const FontFeatureSettings& features, const FontVariantSettings& variantSettings, FontSelectionRequest fontSelectionRequest, const FontVariationSettings& variations, FontOpticalSizing fontOpticalSizing, float size) … … 484 538 VariationsMap variationsToBeApplied; 485 539 540 bool needsConversion = isGXVariableFont(originalFont); 486 541 auto applyVariationValue = [&](const FontTag& tag, float value, bool isDefaultValue) { 487 542 // FIXME: Remove when <rdar://problem/28707822> is fixed … … 508 563 // The system font is somewhat magical. Don't mess with its variations. 509 564 if (!fontIsSystemFont(originalFont)) { 510 applyVariation({{'w', 'g', 'h', 't'}}, static_cast<float>(fontSelectionRequest.weight)); 511 applyVariation({{'w', 'd', 't', 'h'}}, static_cast<float>(fontSelectionRequest.width)); 512 applyVariation({{'s', 'l', 'n', 't'}}, static_cast<float>(fontSelectionRequest.slope)); 565 float weight = fontSelectionRequest.weight; 566 float width = fontSelectionRequest.width; 567 float slope = fontSelectionRequest.slope; 568 if (needsConversion) { 569 weight = denormalizeWeight(weight); 570 width = denormalizeWidth(width); 571 slope = denormalizeSlope(slope); 572 } 573 applyVariation({{'w', 'g', 'h', 't'}}, weight); 574 applyVariation({{'w', 'd', 't', 'h'}}, width); 575 applyVariation({{'s', 'l', 'n', 't'}}, slope); 513 576 } 514 577 … … 602 665 } 603 666 604 static FontSelectionValuestretchFromCoreTextTraits(CFDictionaryRef traits)667 static float stretchFromCoreTextTraits(CFDictionaryRef traits) 605 668 { 606 669 auto widthNumber = static_cast<CFNumberRef>(CFDictionaryGetValue(traits, kCTFontWidthTrait)); 607 if (widthNumber) { 608 // FIXME: The normalization from Core Text's [-1, 1] range to CSS's [50%, 200%] range isn't perfect. 609 float ctWidth; 610 auto success = CFNumberGetValue(widthNumber, kCFNumberFloatType, &ctWidth); 611 ASSERT_UNUSED(success, success); 612 return FontSelectionValue(ctWidth < 0.5 ? ctWidth * 50 + 100 : ctWidth * 150 + 50); 613 } 614 return normalStretchValue(); 670 if (!widthNumber) 671 return normalStretchValue(); 672 673 float ctWidth; 674 auto success = CFNumberGetValue(widthNumber, kCFNumberFloatType, &ctWidth); 675 ASSERT_UNUSED(success, success); 676 return normalizeWidth(ctWidth); 615 677 } 616 678 … … 709 771 710 772 #if SHOULD_USE_CORE_TEXT_FONT_LOOKUP 711 static FontSelectionValuefontWeightFromCoreText(CGFloat weight)773 static float fontWeightFromCoreText(CGFloat weight) 712 774 { 713 775 if (weight < -0.6) 714 return FontSelectionValue(100);776 return 100; 715 777 if (weight < -0.365) 716 return FontSelectionValue(200);778 return 200; 717 779 if (weight < -0.115) 718 return FontSelectionValue(300);780 return 300; 719 781 if (weight < 0.130) 720 return FontSelectionValue(400);782 return 400; 721 783 if (weight < 0.235) 722 return FontSelectionValue(500);784 return 500; 723 785 if (weight < 0.350) 724 return FontSelectionValue(600);786 return 600; 725 787 if (weight < 0.500) 726 return FontSelectionValue(700);788 return 700; 727 789 if (weight < 0.700) 728 return FontSelectionValue(800);729 return FontSelectionValue(900);790 return 800; 791 return 900; 730 792 } 731 793 #endif … … 836 898 }; 837 899 900 // Because this struct holds intermediate values which may be in the compressed -1 - 1 GX range, we don't want to use the relatively large 901 // quantization of FontSelectionValue. Instead, do this logic with floats. 902 struct MinMax { 903 float minimum; 904 float maximum; 905 }; 906 838 907 struct VariationCapabilities { 839 std::optional< FontSelectionRange> weight;840 std::optional< FontSelectionRange> width;841 std::optional< FontSelectionRange> slope;908 std::optional<MinMax> weight; 909 std::optional<MinMax> width; 910 std::optional<MinMax> slope; 842 911 }; 843 912 844 913 #if ENABLE(VARIATION_FONTS) 845 static std::optional< FontSelectionRange> extractVariationBounds(CFDictionaryRef axis)914 static std::optional<MinMax> extractVariationBounds(CFDictionaryRef axis) 846 915 { 847 916 CFNumberRef minimumValue = static_cast<CFNumberRef>(CFDictionaryGetValue(axis, kCTFontVariationAxisMinimumValueKey)); … … 852 921 CFNumberGetValue(maximumValue, kCFNumberFloatType, &rawMaximumValue); 853 922 if (rawMinimumValue < rawMaximumValue) 854 return {{ FontSelectionValue(rawMinimumValue), FontSelectionValue(rawMaximumValue)}};923 return {{ rawMinimumValue, rawMaximumValue }}; 855 924 return std::nullopt; 856 925 } … … 865 934 return result; 866 935 867 auto variations = adoptCF(CTFontCopyVariationAxes(adoptCF(CTFontCreateWithFontDescriptor(fontDescriptor, 0, nullptr)).get())); 936 auto font = adoptCF(CTFontCreateWithFontDescriptor(fontDescriptor, 0, nullptr)); 937 auto variations = adoptCF(CTFontCopyVariationAxes(font.get())); 868 938 if (!variations) 869 939 return result; … … 886 956 result.slope = extractVariationBounds(axis); 887 957 } 958 959 if (isGXVariableFont(font.get())) { 960 if (result.weight) 961 result.weight = {{ normalizeWeight(result.weight.value().minimum), normalizeWeight(result.weight.value().maximum) }}; 962 if (result.width) 963 result.width = {{ normalizeWidth(result.width.value().minimum), normalizeWidth(result.width.value().maximum) }}; 964 if (result.slope) 965 result.slope = {{ normalizeSlope(result.slope.value().minimum), normalizeSlope(result.slope.value().maximum) }}; 966 } 888 967 #else 889 968 UNUSED_PARAM(fontDescriptor); … … 920 999 auto success = CFNumberGetValue(symbolicTraitsNumber, kCFNumberSInt32Type, &symbolicTraits); 921 1000 ASSERT_UNUSED(success, success); 922 auto slopeValue = s ymbolicTraits & kCTFontTraitItalic ? italicValue() : normalItalicValue();1001 auto slopeValue = static_cast<float>(symbolicTraits & kCTFontTraitItalic ? italicValue() : normalItalicValue()); 923 1002 variationCapabilities.slope = {{ slopeValue, slopeValue }}; 924 1003 } else 925 variationCapabilities.slope = {{ normalItalicValue(), normalItalicValue() }};1004 variationCapabilities.slope = {{ static_cast<float>(normalItalicValue()), static_cast<float>(normalItalicValue()) }}; 926 1005 } 927 1006 … … 936 1015 variationCapabilities.weight = {{ weightValue, weightValue }}; 937 1016 } else 938 variationCapabilities.weight = {{ normalWeightValue(), normalWeightValue() }};1017 variationCapabilities.weight = {{ static_cast<float>(normalWeightValue()), static_cast<float>(normalWeightValue()) }}; 939 1018 } 940 1019 #endif … … 949 1028 auto success = CFNumberGetValue(weightNumber.get(), kCFNumberFloatType, &cssWeight); 950 1029 ASSERT_UNUSED(success, success); 951 auto weightValue = FontSelectionValue(cssWeight); 952 variationCapabilities.weight = {{ weightValue, weightValue }}; 1030 variationCapabilities.weight = {{ cssWeight, cssWeight }}; 953 1031 } else 954 variationCapabilities.weight = {{ normalWeightValue(), normalWeightValue() }}; 955 } 956 #endif 957 958 return { variationCapabilities.weight.value(), variationCapabilities.width.value(), variationCapabilities.slope.value() }; 1032 variationCapabilities.weight = {{ static_cast<float>(normalWeightValue()), static_cast<float>(normalWeightValue()) }}; 1033 } 1034 #endif 1035 1036 return {{ FontSelectionValue(variationCapabilities.weight.value().minimum), FontSelectionValue(variationCapabilities.weight.value().maximum) }, 1037 { FontSelectionValue(variationCapabilities.width.value().minimum), FontSelectionValue(variationCapabilities.width.value().maximum) }, 1038 { FontSelectionValue(variationCapabilities.slope.value().minimum), FontSelectionValue(variationCapabilities.slope.value().maximum) }}; 959 1039 } 960 1040
Note: See TracChangeset
for help on using the changeset viewer.