[FreeType] Add initial implementation of variation fonts
authorcarlosgc@webkit.org <carlosgc@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 12 Dec 2018 09:07:19 +0000 (09:07 +0000)
committercarlosgc@webkit.org <carlosgc@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 12 Dec 2018 09:07:19 +0000 (09:07 +0000)
https://bugs.webkit.org/show_bug.cgi?id=192151

Reviewed by Michael Catanzaro.

.:

Enable variation fonts in GTK+ port when required dependencies are available.

* Source/cmake/OptionsGTK.cmake:

Source/WebCore:

* css/CSSFontFaceSource.cpp:
(WebCore::CSSFontFaceSource::font): Remove platform ifdefs.
* loader/cache/CachedFont.cpp:
(WebCore::CachedFont::platformDataFromCustomData): Ditto.
* platform/graphics/FontPlatformData.h:
(WebCore::FontPlatformData::isFixedWidth const):
* platform/graphics/cairo/FontCustomPlatformData.h: Use RefPtr for cairo_font_face_t.
* platform/graphics/freetype/FontCacheFreeType.cpp:
(WebCore::getFontPropertiesFromPattern): Helper function to get several font properties from the fontconfig
pattern.
(WebCore::FontCache::systemFallbackForCharacters): Use getFontPropertiesFromPattern().
(WebCore::FontCache::createFontPlatformData): Pass FC_VARIABLE to the pattern and call buildVariationSettings()
before creating the FontPlatformData to set FC_FONT_VARIATIONS on the pattern.
(WebCore::defaultVariationValues): Parse font variations table.
(WebCore::buildVariationSettings): Build a font variations string from the settings that can be passed to cairo.
* platform/graphics/freetype/FontCacheFreeType.h: Added.
* platform/graphics/freetype/FontCustomPlatformDataFreeType.cpp:
(WebCore::FontCustomPlatformData::FontCustomPlatformData): Use RefPtr and make freeTypeFaceKey global.
(WebCore::FontCustomPlatformData::~FontCustomPlatformData): Remove explicit destroy.
(WebCore::defaultFontconfigOptions): Moved here from FontCacheFreeType.
(WebCore::FontCustomPlatformData::fontPlatformData): Call buildVariationSettings() before creating the
FontPlatformData to set FC_FONT_VARIATIONS on the pattern.
(WebCore::FontCustomPlatformData::supportsFormat): Add variation formats.
* platform/graphics/freetype/FontPlatformDataFreeType.cpp:
(WebCore::setCairoFontOptionsFromFontConfigPattern): Call cairo_font_options_set_variations() with the
FC_FONT_VARIATIONS value from the pattern.
(WebCore::FontPlatformData::FontPlatformData): Use a single constructor that always receives a valid fontconfig
pattern.
(WebCore::FontPlatformData::fcPattern const): Return the fontconfig pattern.
(WebCore::FontPlatformData::platformIsEqual const): Update the condition now that m_pattern can't be nullptr.
(WebCore::FontPlatformData::buildScaledFont): Use m_pattern unconditionally.
* platform/graphics/freetype/SimpleFontDataFreeType.cpp:
(WebCore::Font::platformCreateScaledFont const): Update it to use the new FontPlatformData constructor.
* platform/graphics/harfbuzz/HarfBuzzFaceCairo.cpp:
(WebCore::HarfBuzzFace::createFont): Pass variations to HarfBuzz.
* platform/graphics/win/FontCustomPlatformData.cpp:
(WebCore::FontCustomPlatformData::fontPlatformData):
* platform/graphics/win/FontCustomPlatformData.h:

Tools:

Add cairo patch to avoid crashes.

* gtk/jhbuild.modules:
* gtk/patches/cairo-ft-Use-FT_Done_MM_Var-instead-of-free-when-available.patch: Added.

LayoutTests:

Unskip variation fonts tests that are now passing in GTK+ port.

* platform/gtk/TestExpectations:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@239100 268f45cc-cd09-0410-ab3c-d52691b4dbfc

20 files changed:
ChangeLog
LayoutTests/ChangeLog
LayoutTests/platform/gtk/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/css/CSSFontFaceSource.cpp
Source/WebCore/loader/cache/CachedFont.cpp
Source/WebCore/platform/graphics/FontPlatformData.h
Source/WebCore/platform/graphics/cairo/FontCustomPlatformData.h
Source/WebCore/platform/graphics/freetype/FontCacheFreeType.cpp
Source/WebCore/platform/graphics/freetype/FontCacheFreeType.h [new file with mode: 0644]
Source/WebCore/platform/graphics/freetype/FontCustomPlatformDataFreeType.cpp
Source/WebCore/platform/graphics/freetype/FontPlatformDataFreeType.cpp
Source/WebCore/platform/graphics/freetype/SimpleFontDataFreeType.cpp
Source/WebCore/platform/graphics/harfbuzz/HarfBuzzFaceCairo.cpp
Source/WebCore/platform/graphics/win/FontCustomPlatformData.cpp
Source/WebCore/platform/graphics/win/FontCustomPlatformData.h
Source/cmake/OptionsGTK.cmake
Tools/ChangeLog
Tools/gtk/jhbuild.modules
Tools/gtk/patches/cairo-ft-Use-FT_Done_MM_Var-instead-of-free-when-available.patch [new file with mode: 0644]

index 68693ba..4b999e3 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2018-12-12  Carlos Garcia Campos  <cgarcia@igalia.com>
+
+        [FreeType] Add initial implementation of variation fonts
+        https://bugs.webkit.org/show_bug.cgi?id=192151
+
+        Reviewed by Michael Catanzaro.
+
+        Enable variation fonts in GTK+ port when required dependencies are available.
+
+        * Source/cmake/OptionsGTK.cmake:
+
 2018-12-10  Don Olmstead  <don.olmstead@sony.com>
 
         [CMake] Add ENABLE_RESOURCE_LOAD_STATISTICS to WebKitFeatures.cmake
index 5462e48..ef33f8e 100644 (file)
@@ -1,3 +1,14 @@
+2018-12-12  Carlos Garcia Campos  <cgarcia@igalia.com>
+
+        [FreeType] Add initial implementation of variation fonts
+        https://bugs.webkit.org/show_bug.cgi?id=192151
+
+        Reviewed by Michael Catanzaro.
+
+        Unskip variation fonts tests that are now passing in GTK+ port.
+
+        * platform/gtk/TestExpectations:
+
 2018-12-10  Ryosuke Niwa  <rniwa@webkit.org>
 
         connectedCallback is invoked during the removal of the element inside another element's connectedCallback
index 46aede2..2aad6a7 100644 (file)
@@ -1046,11 +1046,6 @@ webkit.org/b/161586 css3/font-feature-settings-font-face-rendering.html [ ImageO
 
 webkit.org/b/161962 fast/forms/implicit-submission.html [ Failure ]
 
-# Font variations build flag is turned off
-webkit.org/b/162815 fast/text/variations/ [ Skip ]
-webkit.org/b/162815 animations/font-variations/ [ Skip ]
-webkit.org/b/162815 legacy-animation-engine/animations/font-variations/ [ Skip ]
-
 webkit.org/b/169531 fast/text/font-selection-font-face-parse.html [ Skip ]
 
 # CSS image-orientation is not yet enabled.
@@ -3642,6 +3637,10 @@ webkit.org/b/192434 fast/inline/inline-content-with-float-and-margin.html [ Fail
 webkit.org/b/166536 fast/canvas/webgl/hide-some-renderer-info.html [ Failure ]
 webkit.org/b/192435 fast/css-custom-paint/leaks.html [ Failure ]
 
+webkit.org/b/162815 fast/text/variations/font-face-clamp.html [ ImageOnlyFailure ]
+webkit.org/b/162815 fast/text/variations/font-selection-properties.html [ ImageOnlyFailure ]
+webkit.org/b/162815 fast/text/variations/skia-postscript-name.html [ ImageOnlyFailure ]
+
 #////////////////////////////////////////////////////////////////////////////////////////
 # End of non-crashing, non-flaky tests failing
 #////////////////////////////////////////////////////////////////////////////////////////
index 74894db..25a51ea 100644 (file)
@@ -1,3 +1,49 @@
+2018-12-12  Carlos Garcia Campos  <cgarcia@igalia.com>
+
+        [FreeType] Add initial implementation of variation fonts
+        https://bugs.webkit.org/show_bug.cgi?id=192151
+
+        Reviewed by Michael Catanzaro.
+
+        * css/CSSFontFaceSource.cpp:
+        (WebCore::CSSFontFaceSource::font): Remove platform ifdefs.
+        * loader/cache/CachedFont.cpp:
+        (WebCore::CachedFont::platformDataFromCustomData): Ditto.
+        * platform/graphics/FontPlatformData.h:
+        (WebCore::FontPlatformData::isFixedWidth const):
+        * platform/graphics/cairo/FontCustomPlatformData.h: Use RefPtr for cairo_font_face_t.
+        * platform/graphics/freetype/FontCacheFreeType.cpp:
+        (WebCore::getFontPropertiesFromPattern): Helper function to get several font properties from the fontconfig
+        pattern.
+        (WebCore::FontCache::systemFallbackForCharacters): Use getFontPropertiesFromPattern().
+        (WebCore::FontCache::createFontPlatformData): Pass FC_VARIABLE to the pattern and call buildVariationSettings()
+        before creating the FontPlatformData to set FC_FONT_VARIATIONS on the pattern.
+        (WebCore::defaultVariationValues): Parse font variations table.
+        (WebCore::buildVariationSettings): Build a font variations string from the settings that can be passed to cairo.
+        * platform/graphics/freetype/FontCacheFreeType.h: Added.
+        * platform/graphics/freetype/FontCustomPlatformDataFreeType.cpp:
+        (WebCore::FontCustomPlatformData::FontCustomPlatformData): Use RefPtr and make freeTypeFaceKey global.
+        (WebCore::FontCustomPlatformData::~FontCustomPlatformData): Remove explicit destroy.
+        (WebCore::defaultFontconfigOptions): Moved here from FontCacheFreeType.
+        (WebCore::FontCustomPlatformData::fontPlatformData): Call buildVariationSettings() before creating the
+        FontPlatformData to set FC_FONT_VARIATIONS on the pattern.
+        (WebCore::FontCustomPlatformData::supportsFormat): Add variation formats.
+        * platform/graphics/freetype/FontPlatformDataFreeType.cpp:
+        (WebCore::setCairoFontOptionsFromFontConfigPattern): Call cairo_font_options_set_variations() with the
+        FC_FONT_VARIATIONS value from the pattern.
+        (WebCore::FontPlatformData::FontPlatformData): Use a single constructor that always receives a valid fontconfig
+        pattern.
+        (WebCore::FontPlatformData::fcPattern const): Return the fontconfig pattern.
+        (WebCore::FontPlatformData::platformIsEqual const): Update the condition now that m_pattern can't be nullptr.
+        (WebCore::FontPlatformData::buildScaledFont): Use m_pattern unconditionally.
+        * platform/graphics/freetype/SimpleFontDataFreeType.cpp:
+        (WebCore::Font::platformCreateScaledFont const): Update it to use the new FontPlatformData constructor.
+        * platform/graphics/harfbuzz/HarfBuzzFaceCairo.cpp:
+        (WebCore::HarfBuzzFace::createFont): Pass variations to HarfBuzz.
+        * platform/graphics/win/FontCustomPlatformData.cpp:
+        (WebCore::FontCustomPlatformData::fontPlatformData):
+        * platform/graphics/win/FontCustomPlatformData.h:
+
 2018-12-11  Justin Michaud  <justin_michaud@apple.com>
 
         Implement feature flag for CSS Typed OM
index dc2bd7e..33a8589 100644 (file)
@@ -232,11 +232,7 @@ RefPtr<Font> CSSFontFaceSource::font(const FontDescription& fontDescription, boo
         return nullptr;
     if (!m_inDocumentCustomPlatformData)
         return nullptr;
-#if PLATFORM(COCOA)
     return Font::create(m_inDocumentCustomPlatformData->fontPlatformData(fontDescription, syntheticBold, syntheticItalic, fontFaceFeatures, fontFaceVariantSettings, fontFaceCapabilities), Font::Origin::Remote);
-#else
-    return Font::create(m_inDocumentCustomPlatformData->fontPlatformData(fontDescription, syntheticBold, syntheticItalic), Font::Origin::Remote);
-#endif
 #endif
 
     ASSERT_NOT_REACHED();
index f60a38c..8d47754 100644 (file)
@@ -141,14 +141,7 @@ FontPlatformData CachedFont::platformDataFromCustomData(const FontDescription& f
 
 FontPlatformData CachedFont::platformDataFromCustomData(FontCustomPlatformData& fontCustomPlatformData, const FontDescription& fontDescription, bool bold, bool italic, const FontFeatureSettings& fontFaceFeatures, const FontVariantSettings& fontFaceVariantSettings, FontSelectionSpecifiedCapabilities fontFaceCapabilities)
 {
-#if PLATFORM(COCOA)
     return fontCustomPlatformData.fontPlatformData(fontDescription, bold, italic, fontFaceFeatures, fontFaceVariantSettings, fontFaceCapabilities);
-#else
-    UNUSED_PARAM(fontFaceFeatures);
-    UNUSED_PARAM(fontFaceVariantSettings);
-    UNUSED_PARAM(fontFaceCapabilities);
-    return fontCustomPlatformData.fontPlatformData(fontDescription, bold, italic);
-#endif
 }
 
 void CachedFont::allClientsRemoved()
index 0ecf886..bf623ad 100644 (file)
@@ -112,8 +112,7 @@ public:
 #endif
 
 #if USE(FREETYPE)
-    FontPlatformData(FcPattern*, const FontDescription&);
-    FontPlatformData(cairo_font_face_t*, const FontDescription&, bool syntheticBold, bool syntheticOblique);
+    FontPlatformData(cairo_font_face_t*, FcPattern*, float size, bool fixedWidth, bool syntheticBold, bool syntheticOblique, FontOrientation);
     FontPlatformData(const FontPlatformData&);
     FontPlatformData(FontPlatformData&&) = default;
     FontPlatformData& operator=(const FontPlatformData&);
@@ -171,6 +170,8 @@ public:
 #if USE(FREETYPE)
     HarfBuzzFace& harfBuzzFace() const;
     bool hasCompatibleCharmap() const;
+    FcPattern* fcPattern() const;
+    bool isFixedWidth() const { return m_fixedWidth; }
 #endif
 
     unsigned hash() const;
index c0d72ec..c31cc6e 100644 (file)
  *
  */
 
-#ifndef FontCustomPlatformData_h
-#define FontCustomPlatformData_h
+#pragma once
 
 #if USE(CAIRO)
 
+#include "RefPtrCairo.h"
 #include <wtf/Forward.h>
 #include <wtf/Noncopyable.h>
 
 typedef struct FT_FaceRec_*  FT_Face;
-typedef struct _cairo_font_face cairo_font_face_t;
 
 namespace WebCore {
 
 class FontDescription;
 class FontPlatformData;
 class SharedBuffer;
+struct FontSelectionSpecifiedCapabilities;
+struct FontVariantSettings;
+
+template <typename T> class FontTaggedSettings;
+typedef FontTaggedSettings<int> FontFeatureSettings;
 
 struct FontCustomPlatformData {
     WTF_MAKE_NONCOPYABLE(FontCustomPlatformData);
 public:
     FontCustomPlatformData(FT_Face, SharedBuffer&);
-    ~FontCustomPlatformData();
-    FontPlatformData fontPlatformData(const FontDescription&, bool bold, bool italic);
+    ~FontCustomPlatformData() = default;
+    FontPlatformData fontPlatformData(const FontDescription&, bool bold, bool italic, const FontFeatureSettings&, const FontVariantSettings&, FontSelectionSpecifiedCapabilities);
     static bool supportsFormat(const String&);
 
 private:
-    cairo_font_face_t* m_fontFace;
+    RefPtr<cairo_font_face_t> m_fontFace;
 };
 
 std::unique_ptr<FontCustomPlatformData> createFontCustomPlatformData(SharedBuffer&, const String&);
@@ -53,5 +57,3 @@ std::unique_ptr<FontCustomPlatformData> createFontCustomPlatformData(SharedBuffe
 } // namespace WebCore
 
 #endif // USE(CAIRO)
-
-#endif // FontCustomPlatformData_h
index 860fc6b..c446edb 100644 (file)
 #include "config.h"
 #include "FontCache.h"
 
+#include "CairoUniquePtr.h"
 #include "CairoUtilities.h"
 #include "FcUniquePtr.h"
+#include "FloatConversion.h"
 #include "Font.h"
+#include "FontDescription.h"
+#include "FontCacheFreeType.h"
 #include "RefPtrCairo.h"
 #include "RefPtrFontconfig.h"
 #include "UTF16UChar32Iterator.h"
 #include "GtkUtilities.h"
 #endif
 
+#if ENABLE(VARIATION_FONTS)
+#include FT_MULTIPLE_MASTERS_H
+#endif
+
 namespace WebCore {
 
 void FontCache::platformInit()
@@ -79,6 +87,37 @@ static bool configurePatternForFontDescription(FcPattern* pattern, const FontDes
     return true;
 }
 
+static void getFontPropertiesFromPattern(FcPattern* pattern, const FontDescription& fontDescription, bool& fixedWidth, bool& syntheticBold, bool& syntheticOblique)
+{
+    fixedWidth = false;
+    int spacing;
+    if (FcPatternGetInteger(pattern, FC_SPACING, 0, &spacing) == FcResultMatch && spacing == FC_MONO)
+        fixedWidth = true;
+
+    syntheticBold = false;
+    bool descriptionAllowsSyntheticBold = fontDescription.fontSynthesis() & FontSynthesisWeight;
+    if (descriptionAllowsSyntheticBold && isFontWeightBold(fontDescription.weight())) {
+        // The FC_EMBOLDEN property instructs us to fake the boldness of the font.
+        FcBool fontConfigEmbolden = FcFalse;
+        if (FcPatternGetBool(pattern, FC_EMBOLDEN, 0, &fontConfigEmbolden) == FcResultMatch)
+            syntheticBold = fontConfigEmbolden;
+
+        // Fallback fonts may not have FC_EMBOLDEN activated even though it's necessary.
+        int weight = 0;
+        if (!syntheticBold && FcPatternGetInteger(pattern, FC_WEIGHT, 0, &weight) == FcResultMatch)
+            syntheticBold = syntheticBold || weight < FC_WEIGHT_DEMIBOLD;
+    }
+
+    // We requested an italic font, but Fontconfig gave us one that was neither oblique nor italic.
+    syntheticOblique = false;
+    int actualFontSlant;
+    bool descriptionAllowsSyntheticOblique = fontDescription.fontSynthesis() & FontSynthesisStyle;
+    if (descriptionAllowsSyntheticOblique && fontDescription.italic()
+        && FcPatternGetInteger(pattern, FC_SLANT, 0, &actualFontSlant) == FcResultMatch) {
+        syntheticOblique = actualFontSlant == FC_SLANT_ROMAN;
+    }
+}
+
 RefPtr<Font> FontCache::systemFallbackForCharacters(const FontDescription& description, const Font*, bool, const UChar* characters, unsigned length)
 {
     FcUniquePtr<FcCharSet> fontConfigCharSet(FcCharSetCreate());
@@ -106,7 +145,11 @@ RefPtr<Font> FontCache::systemFallbackForCharacters(const FontDescription& descr
     if (!resultPattern)
         return nullptr;
 
-    FontPlatformData alternateFontData(resultPattern.get(), description);
+    bool fixedWidth, syntheticBold, syntheticOblique;
+    getFontPropertiesFromPattern(resultPattern.get(), description, fixedWidth, syntheticBold, syntheticOblique);
+
+    RefPtr<cairo_font_face_t> fontFace = adoptRef(cairo_ft_font_face_create_for_pattern(resultPattern.get()));
+    FontPlatformData alternateFontData(fontFace.get(), resultPattern.get(), description.computedPixelSize(), fixedWidth, syntheticBold, syntheticOblique, description.orientation());
     return fontForPlatformData(alternateFontData);
 }
 
@@ -332,6 +375,9 @@ std::unique_ptr<FontPlatformData> FontCache::createFontPlatformData(const FontDe
     RefPtr<FcPattern> pattern = adoptRef(FcPatternCreate());
     // Never choose unscalable fonts, as they pixelate when displayed at different sizes.
     FcPatternAddBool(pattern.get(), FC_SCALABLE, FcTrue);
+#if ENABLE(VARIATION_FONTS)
+    FcPatternAddBool(pattern.get(), FC_VARIABLE, FcDontCare);
+#endif
     String familyNameString(getFamilyNameStringFromFamily(family));
     if (!FcPatternAddString(pattern.get(), FC_FAMILY, reinterpret_cast<const FcChar8*>(familyNameString.utf8().data())))
         return nullptr;
@@ -382,10 +428,28 @@ std::unique_ptr<FontPlatformData> FontCache::createFontPlatformData(const FontDe
     if (!matchedFontFamily)
         return nullptr;
 
+    bool fixedWidth, syntheticBold, syntheticOblique;
+    getFontPropertiesFromPattern(pattern.get(), fontDescription, fixedWidth, syntheticBold, syntheticOblique);
+
+    RefPtr<cairo_font_face_t> fontFace = adoptRef(cairo_ft_font_face_create_for_pattern(resultPattern.get()));
+#if ENABLE(VARIATION_FONTS)
+    // Cairo doesn't have API to get the FT_Face of an unscaled font, so we need to
+    // create a temporary scaled font to get the FT_Face.
+    CairoUniquePtr<cairo_font_options_t> options(cairo_font_options_copy(getDefaultCairoFontOptions()));
+    cairo_matrix_t matrix;
+    cairo_matrix_init_identity(&matrix);
+    RefPtr<cairo_scaled_font_t> scaledFont = adoptRef(cairo_scaled_font_create(fontFace.get(), &matrix, &matrix, options.get()));
+    CairoFtFaceLocker cairoFtFaceLocker(scaledFont.get());
+    if (FT_Face freeTypeFace = cairoFtFaceLocker.ftFace()) {
+        auto variants = buildVariationSettings(freeTypeFace, fontDescription);
+        if (!variants.isEmpty())
+            FcPatternAddString(resultPattern.get(), FC_FONT_VARIATIONS, reinterpret_cast<const FcChar8*>(variants.utf8().data()));
+    }
+#endif
+    auto platformData = std::make_unique<FontPlatformData>(fontFace.get(), resultPattern.get(), fontDescription.computedPixelSize(), fixedWidth, syntheticBold, syntheticOblique, fontDescription.orientation());
     // Verify that this font has an encoding compatible with Fontconfig. Fontconfig currently
     // supports three encodings in FcFreeTypeCharIndex: Unicode, Symbol and AppleRoman.
     // If this font doesn't have one of these three encodings, don't select it.
-    auto platformData = std::make_unique<FontPlatformData>(resultPattern.get(), fontDescription);
     if (!platformData->hasCompatibleCharmap())
         return nullptr;
 
@@ -397,4 +461,67 @@ const AtomicString& FontCache::platformAlternateFamilyName(const AtomicString&)
     return nullAtom();
 }
 
+#if ENABLE(VARIATION_FONTS)
+struct VariationDefaults {
+    float defaultValue;
+    float minimumValue;
+    float maximumValue;
+};
+
+typedef HashMap<FontTag, VariationDefaults, FourCharacterTagHash, FourCharacterTagHashTraits> VariationDefaultsMap;
+typedef HashMap<FontTag, float, FourCharacterTagHash, FourCharacterTagHashTraits> VariationsMap;
+
+static VariationDefaultsMap defaultVariationValues(FT_Face face)
+{
+    VariationDefaultsMap result;
+    FT_MM_Var* ftMMVar;
+    if (FT_Get_MM_Var(face, &ftMMVar))
+        return result;
+
+    for (unsigned i = 0; i < ftMMVar->num_axis; ++i) {
+        auto tag = ftMMVar->axis[i].tag;
+        auto b1 = 0xFF & (tag >> 24);
+        auto b2 = 0xFF & (tag >> 16);
+        auto b3 = 0xFF & (tag >> 8);
+        auto b4 = 0xFF & (tag >> 0);
+        FontTag resultKey = {{ static_cast<char>(b1), static_cast<char>(b2), static_cast<char>(b3), static_cast<char>(b4) }};
+        VariationDefaults resultValues = { narrowPrecisionToFloat(ftMMVar->axis[i].def / 65536.), narrowPrecisionToFloat(ftMMVar->axis[i].minimum / 65536.), narrowPrecisionToFloat(ftMMVar->axis[i].maximum / 65536.) };
+        result.set(resultKey, resultValues);
+    }
+    FT_Done_MM_Var(face->glyph->library, ftMMVar);
+    return result;
+}
+
+String buildVariationSettings(FT_Face face, const FontDescription& fontDescription)
+{
+    auto defaultValues = defaultVariationValues(face);
+    const auto& variations = fontDescription.variationSettings();
+
+    VariationsMap variationsToBeApplied;
+    auto applyVariation = [&](const FontTag& tag, float value) {
+        auto iterator = defaultValues.find(tag);
+        if (iterator == defaultValues.end())
+            return;
+        float valueToApply = clampTo(value, iterator->value.minimumValue, iterator->value.maximumValue);
+        variationsToBeApplied.set(tag, valueToApply);
+    };
+
+    for (auto& variation : variations)
+        applyVariation(variation.tag(), variation.value());
+
+    StringBuilder builder;
+    for (auto& variation : variationsToBeApplied) {
+        if (!builder.isEmpty())
+            builder.append(',');
+        builder.append(variation.key[0]);
+        builder.append(variation.key[1]);
+        builder.append(variation.key[2]);
+        builder.append(variation.key[3]);
+        builder.append('=');
+        builder.appendNumber(variation.value);
+    }
+    return builder.toString();
+}
+#endif // ENABLE(VARIATION_FONTS)
+
 }
diff --git a/Source/WebCore/platform/graphics/freetype/FontCacheFreeType.h b/Source/WebCore/platform/graphics/freetype/FontCacheFreeType.h
new file mode 100644 (file)
index 0000000..81e823c
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+typedef struct FT_FaceRec_* FT_Face;
+
+namespace WebCore {
+class FontDescription;
+
+#if ENABLE(VARIATION_FONTS)
+String buildVariationSettings(FT_Face, const FontDescription&);
+#endif
+};
index 53625df..8821d42 100644 (file)
@@ -22,6 +22,9 @@
 #include "config.h"
 #include "FontCustomPlatformData.h"
 
+#include "CairoUtilities.h"
+#include "FontCacheFreeType.h"
+#include "FontDescription.h"
 #include "FontPlatformData.h"
 #include "SharedBuffer.h"
 #include <cairo-ft.h>
@@ -36,29 +39,54 @@ static void releaseCustomFontData(void* data)
     static_cast<SharedBuffer*>(data)->deref();
 }
 
+static cairo_user_data_key_t freeTypeFaceKey;
+
 FontCustomPlatformData::FontCustomPlatformData(FT_Face freeTypeFace, SharedBuffer& buffer)
-    : m_fontFace(cairo_ft_font_face_create_for_ft_face(freeTypeFace, FT_LOAD_DEFAULT))
+    : m_fontFace(adoptRef(cairo_ft_font_face_create_for_ft_face(freeTypeFace, FT_LOAD_DEFAULT)))
 {
     buffer.ref(); // This is balanced by the buffer->deref() in releaseCustomFontData.
     static cairo_user_data_key_t bufferKey;
-    cairo_font_face_set_user_data(m_fontFace, &bufferKey, &buffer,
+    cairo_font_face_set_user_data(m_fontFace.get(), &bufferKey, &buffer,
          static_cast<cairo_destroy_func_t>(releaseCustomFontData));
 
     // Cairo doesn't do FreeType reference counting, so we need to ensure that when
     // this cairo_font_face_t is destroyed, it cleans up the FreeType face as well.
-    static cairo_user_data_key_t freeTypeFaceKey;
-    cairo_font_face_set_user_data(m_fontFace, &freeTypeFaceKey, freeTypeFace,
+    cairo_font_face_set_user_data(m_fontFace.get(), &freeTypeFaceKey, freeTypeFace,
         reinterpret_cast<cairo_destroy_func_t>(reinterpret_cast<void(*)(void)>(FT_Done_Face)));
 }
 
-FontCustomPlatformData::~FontCustomPlatformData()
+static FcPattern* defaultFontconfigOptions()
 {
-    cairo_font_face_destroy(m_fontFace);
+    // Get some generic default settings from fontconfig for web fonts. Strategy
+    // from Behdad Esfahbod in https://code.google.com/p/chromium/issues/detail?id=173207#c35
+    // For web fonts, the hint style is overridden in FontCustomPlatformData::FontCustomPlatformData
+    // so Fontconfig will not affect the hint style, but it may disable hinting completely.
+    static FcPattern* pattern = nullptr;
+    static std::once_flag flag;
+    std::call_once(flag, [](FcPattern*) {
+        pattern = FcPatternCreate();
+        FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
+        cairo_ft_font_options_substitute(getDefaultCairoFontOptions(), pattern);
+        FcDefaultSubstitute(pattern);
+        FcPatternDel(pattern, FC_FAMILY);
+        FcConfigSubstitute(nullptr, pattern, FcMatchFont);
+    }, pattern);
+    return pattern;
 }
 
-FontPlatformData FontCustomPlatformData::fontPlatformData(const FontDescription& description, bool bold, bool italic)
+FontPlatformData FontCustomPlatformData::fontPlatformData(const FontDescription& description, bool bold, bool italic, const FontFeatureSettings&, const FontVariantSettings&, FontSelectionSpecifiedCapabilities)
 {
-    return FontPlatformData(m_fontFace, description, bold, italic);
+    auto* freeTypeFace = static_cast<FT_Face>(cairo_font_face_get_user_data(m_fontFace.get(), &freeTypeFaceKey));
+    ASSERT(freeTypeFace);
+    RefPtr<FcPattern> pattern = defaultFontconfigOptions();
+#if ENABLE(VARIATION_FONTS)
+    auto variants = buildVariationSettings(freeTypeFace, description);
+    if (!variants.isEmpty()) {
+        pattern = adoptRef(FcPatternDuplicate(pattern.get()));
+        FcPatternAddString(pattern.get(), FC_FONT_VARIATIONS, reinterpret_cast<const FcChar8*>(variants.utf8().data()));
+    }
+#endif
+    return FontPlatformData(m_fontFace.get(), pattern.get(), description.computedPixelSize(), freeTypeFace->face_flags & FT_FACE_FLAG_FIXED_WIDTH, bold, italic, description.orientation());
 }
 
 static bool initializeFreeTypeLibrary(FT_Library& library)
@@ -110,6 +138,14 @@ bool FontCustomPlatformData::supportsFormat(const String& format)
         || equalLettersIgnoringASCIICase(format, "opentype")
 #if USE(WOFF2)
         || equalLettersIgnoringASCIICase(format, "woff2")
+#if ENABLE(VARIATION_FONTS)
+        || equalLettersIgnoringASCIICase(format, "woff2-variations")
+#endif
+#endif
+#if ENABLE(VARIATION_FONTS)
+        || equalLettersIgnoringASCIICase(format, "woff-variations")
+        || equalLettersIgnoringASCIICase(format, "truetype-variations")
+        || equalLettersIgnoringASCIICase(format, "opentype-variations")
 #endif
         || equalLettersIgnoringASCIICase(format, "woff");
 }
index f77d6a4..c5e1489 100644 (file)
@@ -28,7 +28,6 @@
 #include "CairoUniquePtr.h"
 #include "CairoUtilities.h"
 #include "FontCache.h"
-#include "FontDescription.h"
 #include "SharedBuffer.h"
 #include <cairo-ft.h>
 #include <fontconfig/fcfreetype.h>
@@ -100,74 +99,22 @@ static void setCairoFontOptionsFromFontConfigPattern(cairo_font_options_t* optio
         cairo_font_options_set_hint_style(options, convertFontConfigHintStyle(integerResult));
     if (FcPatternGetBool(pattern, FC_HINTING, 0, &booleanResult) == FcResultMatch && !booleanResult)
         cairo_font_options_set_hint_style(options, CAIRO_HINT_STYLE_NONE);
-}
 
-static FcPattern* getDefaultFontconfigOptions()
-{
-    // Get some generic default settings from fontconfig for web fonts. Strategy
-    // from Behdad Esfahbod in https://code.google.com/p/chromium/issues/detail?id=173207#c35
-    // For web fonts, the hint style is overridden in FontCustomPlatformData::FontCustomPlatformData
-    // so Fontconfig will not affect the hint style, but it may disable hinting completely.
-    static FcPattern* pattern = nullptr;
-    static std::once_flag flag;
-    std::call_once(flag, [](FcPattern*) {
-        pattern = FcPatternCreate();
-        FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
-        cairo_ft_font_options_substitute(getDefaultCairoFontOptions(), pattern);
-        FcDefaultSubstitute(pattern);
-        FcPatternDel(pattern, FC_FAMILY);
-        FcConfigSubstitute(nullptr, pattern, FcMatchFont);
-    }, pattern);
-    return pattern;
-}
-
-FontPlatformData::FontPlatformData(FcPattern* pattern, const FontDescription& fontDescription)
-    : m_pattern(pattern)
-    , m_size(fontDescription.computedPixelSize())
-    , m_orientation(fontDescription.orientation())
-{
-    ASSERT(m_pattern);
-    RefPtr<cairo_font_face_t> fontFace = adoptRef(cairo_ft_font_face_create_for_pattern(m_pattern.get()));
-
-    int spacing;
-    if (FcPatternGetInteger(pattern, FC_SPACING, 0, &spacing) == FcResultMatch && spacing == FC_MONO)
-        m_fixedWidth = true;
-
-    bool descriptionAllowsSyntheticBold = fontDescription.fontSynthesis() & FontSynthesisWeight;
-    if (descriptionAllowsSyntheticBold && isFontWeightBold(fontDescription.weight())) {
-        // The FC_EMBOLDEN property instructs us to fake the boldness of the font.
-        FcBool fontConfigEmbolden = FcFalse;
-        if (FcPatternGetBool(pattern, FC_EMBOLDEN, 0, &fontConfigEmbolden) == FcResultMatch)
-            m_syntheticBold = fontConfigEmbolden;
-
-        // Fallback fonts may not have FC_EMBOLDEN activated even though it's necessary.
-        int weight = 0;
-        if (!m_syntheticBold && FcPatternGetInteger(pattern, FC_WEIGHT, 0, &weight) == FcResultMatch)
-            m_syntheticBold = m_syntheticBold || weight < FC_WEIGHT_DEMIBOLD;
-    }
-
-    // We requested an italic font, but Fontconfig gave us one that was neither oblique nor italic.
-    int actualFontSlant;
-    bool descriptionAllowsSyntheticOblique = fontDescription.fontSynthesis() & FontSynthesisStyle;
-    if (descriptionAllowsSyntheticOblique && fontDescription.italic()
-        && FcPatternGetInteger(pattern, FC_SLANT, 0, &actualFontSlant) == FcResultMatch) {
-        m_syntheticOblique = actualFontSlant == FC_SLANT_ROMAN;
+#if ENABLE(VARIATION_FONTS)
+    FcChar8* variations;
+    if (FcPatternGetString(pattern, FC_FONT_VARIATIONS, 0, &variations) == FcResultMatch) {
+        cairo_font_options_set_variations(options, reinterpret_cast<char*>(variations));
     }
-
-    buildScaledFont(fontFace.get());
+#endif
 }
 
-FontPlatformData::FontPlatformData(cairo_font_face_t* fontFace, const FontDescription& description, bool bold, bool italic)
-    : m_size(description.computedPixelSize())
-    , m_orientation(description.orientation())
-    , m_syntheticBold(bold)
-    , m_syntheticOblique(italic)
+FontPlatformData::FontPlatformData(cairo_font_face_t* fontFace, FcPattern* pattern, float size, bool fixedWidth, bool syntheticBold, bool syntheticOblique, FontOrientation orientation)
+    : FontPlatformData(size, syntheticBold, syntheticOblique, orientation)
 {
-    buildScaledFont(fontFace);
+    m_pattern = pattern;
+    m_fixedWidth = fixedWidth;
 
-    CairoFtFaceLocker cairoFtFaceLocker(m_scaledFont.get());
-    if (FT_Face fontConfigFace = cairoFtFaceLocker.ftFace())
-        m_fixedWidth = fontConfigFace->face_flags & FT_FACE_FLAG_FIXED_WIDTH;
+    buildScaledFont(fontFace);
 }
 
 FontPlatformData::FontPlatformData(const FontPlatformData& other)
@@ -244,6 +191,11 @@ HarfBuzzFace& FontPlatformData::harfBuzzFace() const
     return *m_harfBuzzFace;
 }
 
+FcPattern* FontPlatformData::fcPattern() const
+{
+    return m_pattern.get();
+}
+
 bool FontPlatformData::isFixedPitch() const
 {
     return m_fixedWidth;
@@ -256,10 +208,7 @@ unsigned FontPlatformData::hash() const
 
 bool FontPlatformData::platformIsEqual(const FontPlatformData& other) const
 {
-    // FcPatternEqual does not support null pointers as arguments.
-    if ((m_pattern && !other.m_pattern)
-        || (!m_pattern && other.m_pattern)
-        || (m_pattern != other.m_pattern && !FcPatternEqual(m_pattern.get(), other.m_pattern.get())))
+    if (m_pattern != other.m_pattern && !FcPatternEqual(m_pattern.get(), other.m_pattern.get()))
         return false;
 
     return m_scaledFont == other.m_scaledFont;
@@ -274,9 +223,9 @@ String FontPlatformData::description() const
 
 void FontPlatformData::buildScaledFont(cairo_font_face_t* fontFace)
 {
+    ASSERT(m_pattern);
     CairoUniquePtr<cairo_font_options_t> options(cairo_font_options_copy(getDefaultCairoFontOptions()));
-    FcPattern* optionsPattern = m_pattern ? m_pattern.get() : getDefaultFontconfigOptions();
-    setCairoFontOptionsFromFontConfigPattern(options.get(), optionsPattern);
+    setCairoFontOptionsFromFontConfigPattern(options.get(), m_pattern.get());
 
     cairo_matrix_t ctm;
     cairo_matrix_init_identity(&ctm);
@@ -288,7 +237,7 @@ void FontPlatformData::buildScaledFont(cairo_font_face_t* fontFace)
     FcMatrixInit(&fontConfigMatrix);
 
     // These matrices may be stacked in the pattern, so it's our job to get them all and multiply them.
-    for (int i = 0; FcPatternGetMatrix(optionsPattern, FC_MATRIX, i, &tempFontConfigMatrix) == FcResultMatch; i++)
+    for (int i = 0; FcPatternGetMatrix(m_pattern.get(), FC_MATRIX, i, &tempFontConfigMatrix) == FcResultMatch; i++)
         FcMatrixMultiply(&fontConfigMatrix, &fontConfigMatrix, tempFontConfigMatrix);
 
     cairo_matrix_init(&fontMatrix, 1, -fontConfigMatrix.yx, -fontConfigMatrix.xy, 1, 0, 0);
index 622a3e6..4bff7cc 100644 (file)
@@ -143,12 +143,13 @@ void Font::platformCharWidthInit()
 RefPtr<Font> Font::platformCreateScaledFont(const FontDescription& fontDescription, float scaleFactor) const
 {
     ASSERT(m_platformData.scaledFont());
-    FontDescription scaledFontDescription = fontDescription;
-    scaledFontDescription.setComputedSize(scaleFactor * fontDescription.computedSize());
     return Font::create(FontPlatformData(cairo_scaled_font_get_font_face(m_platformData.scaledFont()),
-        scaledFontDescription,
+        m_platformData.fcPattern(),
+        scaleFactor * fontDescription.computedSize(),
+        m_platformData.isFixedWidth(),
         m_platformData.syntheticBold(),
-        m_platformData.syntheticOblique()),
+        m_platformData.syntheticOblique(),
+        fontDescription.orientation()),
         origin(), Interstitial::No);
 }
 
index b3fd8c0..d54b589 100644 (file)
 #include <wtf/text/CString.h>
 #include <wtf/text/StringView.h>
 
+#if ENABLE(VARIATION_FONTS)
+#include FT_MULTIPLE_MASTERS_H
+#endif
+
 namespace WebCore {
 
 struct HarfBuzzFontData {
@@ -213,6 +217,29 @@ hb_font_t* HarfBuzzFace::createFont()
         hb_font_set_ppem(font, size, size);
     int scale = floatToHarfBuzzPosition(size);
     hb_font_set_scale(font, scale, scale);
+
+#if ENABLE(VARIATION_FONTS)
+    {
+        CairoFtFaceLocker cairoFtFaceLocker(m_platformData.scaledFont());
+        if (FT_Face face = cairoFtFaceLocker.ftFace()) {
+            FT_MM_Var* ftMMVar;
+            if (!FT_Get_MM_Var(face, &ftMMVar)) {
+                Vector<FT_Fixed, 4> coords;
+                coords.resize(ftMMVar->num_axis);
+                if (!FT_Get_Var_Design_Coordinates(face, coords.size(), coords.data())) {
+                    Vector<hb_variation_t, 4> variations(coords.size());
+                    for (FT_UInt i = 0; i < ftMMVar->num_axis; ++i) {
+                        variations[i].tag = ftMMVar->axis[i].tag;
+                        variations[i].value = coords[i] / 65536.0;
+                    }
+                    hb_font_set_variations(font, variations.data(), variations.size());
+                }
+                FT_Done_MM_Var(face->glyph->library, ftMMVar);
+            }
+        }
+    }
+#endif
+
     hb_font_make_immutable(font);
     return font;
 }
index 1a62d33..387a239 100644 (file)
@@ -46,7 +46,7 @@ FontCustomPlatformData::~FontCustomPlatformData()
         RemoveFontMemResourceEx(m_fontReference);
 }
 
-FontPlatformData FontCustomPlatformData::fontPlatformData(const FontDescription& fontDescription, bool bold, bool italic)
+FontPlatformData FontCustomPlatformData::fontPlatformData(const FontDescription& fontDescription, bool bold, bool italic, const FontFeatureSettings&, const FontVariantSettings&, FontSelectionSpecifiedCapabilities)
 {
     int size = fontDescription.computedPixelSize();
     FontRenderingMode renderingMode = fontDescription.renderingMode();
index 28aaaad..1904490 100644 (file)
@@ -34,6 +34,11 @@ namespace WebCore {
 class FontDescription;
 class FontPlatformData;
 class SharedBuffer;
+struct FontSelectionSpecifiedCapabilities;
+struct FontVariantSettings;
+
+template <typename T> class FontTaggedSettings;
+typedef FontTaggedSettings<int> FontFeatureSettings;
 
 struct FontCustomPlatformData {
     WTF_MAKE_NONCOPYABLE(FontCustomPlatformData);
@@ -46,7 +51,7 @@ public:
 
     ~FontCustomPlatformData();
 
-    FontPlatformData fontPlatformData(const FontDescription&, bool bold, bool italic);
+    FontPlatformData fontPlatformData(const FontDescription&, bool bold, bool italic, const FontFeatureSettings&, const FontVariantSettings&, FontSelectionSpecifiedCapabilities);
 
     static bool supportsFormat(const String&);
 
index 58d00a0..91b0936 100644 (file)
@@ -125,6 +125,14 @@ else ()
     WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_BUBBLEWRAP_SANDBOX PRIVATE OFF)
 endif ()
 
+# Enable variation fonts when cairo >= 1.16, fontconfig >= 2.13.0, freetype >= 2.9.0 and harfbuzz >= 1.4.2.
+if ("${PC_CAIRO_VERSION}" VERSION_GREATER_EQUAL "1.16.0"
+    AND "${PC_FONTCONFIG_VERSION}" VERSION_GREATER_EQUAL "2.13.0"
+    AND "${FREETYPE_VERSION_STRING}" VERSION_GREATER_EQUAL "2.9.0"
+    AND "${PC_HARFBUZZ_VERSION}" VERSION_GREATER_EQUAL "1.4.2")
+    WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_VARIATION_FONTS PRIVATE ON)
+endif ()
+
 # Public options shared with other WebKit ports. Do not add any options here
 # without approval from a GTK+ reviewer. There must be strong reason to support
 # changing the value of the option.
index 367c36c..bb1ab79 100644 (file)
@@ -1,3 +1,15 @@
+2018-12-12  Carlos Garcia Campos  <cgarcia@igalia.com>
+
+        [FreeType] Add initial implementation of variation fonts
+        https://bugs.webkit.org/show_bug.cgi?id=192151
+
+        Reviewed by Michael Catanzaro.
+
+        Add cairo patch to avoid crashes.
+
+        * gtk/jhbuild.modules:
+        * gtk/patches/cairo-ft-Use-FT_Done_MM_Var-instead-of-free-when-available.patch: Added.
+
 2018-12-11  Justin Michaud  <justin_michaud@apple.com>
 
         Implement feature flag for CSS Typed OM
index 05746c2..d6a5a81 100644 (file)
     </dependencies>
     <branch module="releases/cairo-1.16.0.tar.xz" version="1.16.0"
             repo="cairographics.org"
-           hash="sha1:00e81842ae5e81bb0343108884eb5205be0eac14"/>
+           hash="sha1:00e81842ae5e81bb0343108884eb5205be0eac14">
+      <!-- See https://gitlab.freedesktop.org/cairo/cairo/merge_requests/5 -->
+      <patch file="cairo-ft-Use-FT_Done_MM_Var-instead-of-free-when-available.patch" strip="1"/>
+    </branch>
   </autotools>
 
   <!-- FIXME: Pixman 0.32.6 ARM iwMMXt fast path isn't buildable with GCC 4.9 and
diff --git a/Tools/gtk/patches/cairo-ft-Use-FT_Done_MM_Var-instead-of-free-when-available.patch b/Tools/gtk/patches/cairo-ft-Use-FT_Done_MM_Var-instead-of-free-when-available.patch
new file mode 100644 (file)
index 0000000..210d7d1
--- /dev/null
@@ -0,0 +1,30 @@
+From b84d0cfd25f82eb25e0b3a708005a9d5502069c7 Mon Sep 17 00:00:00 2001
+From: Carlos Garcia Campos <cgarcia@igalia.com>
+Date: Mon, 19 Nov 2018 12:33:07 +0100
+Subject: [PATCH 1/2] ft: Use FT_Done_MM_Var instead of free when available in
+ cairo_ft_apply_variations
+
+Fixes a crash when using freetype >= 2.9
+---
+ src/cairo-ft-font.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c
+index 325dd61b4..981973f78 100644
+--- a/src/cairo-ft-font.c
++++ b/src/cairo-ft-font.c
+@@ -2393,7 +2393,11 @@ skip:
+ done:
+         free (coords);
+         free (current_coords);
++#if HAVE_FT_DONE_MM_VAR
++        FT_Done_MM_Var (face->glyph->library, ft_mm_var);
++#else
+         free (ft_mm_var);
++#endif
+     }
+ }
+-- 
+2.19.2
+