Bug 1427641 - patch 3 - Add variation data to the cairo_ft_*_font objects, so they can properly track instances of the same font resource used with different variation parameters. r=lsalzman
authorJonathan Kew <jkew@mozilla.com>
Thu, 07 Dec 2017 13:22:49 +0000
changeset 449885 54896137200d457c307690840e1efa02b7567466
parent 449884 1070a455ef173c643fa398a15e0033ea491a0bdd
child 449886 d6e329f15f6ab2ca85c7daef5ce0370a1327cd00
push id8527
push userCallek@gmail.com
push dateThu, 11 Jan 2018 21:05:50 +0000
treeherdermozilla-beta@95342d212a7a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslsalzman
bugs1427641
milestone59.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1427641 - patch 3 - Add variation data to the cairo_ft_*_font objects, so they can properly track instances of the same font resource used with different variation parameters. r=lsalzman
gfx/2d/ScaledFontFontconfig.cpp
gfx/cairo/cairo/src/cairo-ft-font.c
gfx/cairo/cairo/src/cairo-ft.h
gfx/thebes/gfxFT2FontBase.cpp
gfx/thebes/gfxFT2FontBase.h
gfx/thebes/gfxFT2FontList.cpp
gfx/thebes/gfxFcPlatformFontList.cpp
gfx/thebes/gfxFcPlatformFontList.h
--- a/gfx/2d/ScaledFontFontconfig.cpp
+++ b/gfx/2d/ScaledFontFontconfig.cpp
@@ -392,17 +392,17 @@ ScaledFontFontconfig::CreateFromInstance
     FcPatternAddFTFace(pattern, FC_FT_FACE, face);
   } else {
     FcPatternAddString(pattern, FC_FILE, reinterpret_cast<const FcChar8*>(aUnscaledFont->GetFile()));
     FcPatternAddInteger(pattern, FC_INDEX, aUnscaledFont->GetIndex());
   }
   FcPatternAddDouble(pattern, FC_PIXEL_SIZE, aSize);
   aInstanceData.SetupPattern(pattern);
 
-  cairo_font_face_t* font = cairo_ft_font_face_create_for_pattern(pattern);
+  cairo_font_face_t* font = cairo_ft_font_face_create_for_pattern(pattern, nullptr, 0);
   if (cairo_font_face_status(font) != CAIRO_STATUS_SUCCESS) {
     gfxWarning() << "Failed creating Cairo font face for Fontconfig pattern";
     FcPatternDestroy(pattern);
     return nullptr;
   }
 
   if (aNativeFontResource) {
     // Bug 1362117 - Cairo may keep the font face alive after the owning NativeFontResource
--- a/gfx/cairo/cairo/src/cairo-ft-font.c
+++ b/gfx/cairo/cairo/src/cairo-ft-font.c
@@ -49,16 +49,17 @@
 #include "cairo-fontconfig-private.h"
 
 #include <ft2build.h>
 #include FT_FREETYPE_H
 #include FT_OUTLINE_H
 #include FT_IMAGE_H
 #include FT_BITMAP_H
 #include FT_TRUETYPE_TABLES_H
+#include FT_MULTIPLE_MASTERS_H
 #if HAVE_FT_GLYPHSLOT_EMBOLDEN
 #include FT_SYNTHESIS_H
 #endif
 
 #if HAVE_FT_LIBRARY_SETLCDFILTER
 #include FT_LCD_FILTER_H
 #endif
 
@@ -159,16 +160,20 @@ struct _cairo_ft_unscaled_font {
 
     cairo_bool_t from_face; /* was the FT_Face provided by user? */
     FT_Face face;	    /* provided or cached face */
 
     /* only set if from_face is false */
     char *filename;
     int id;
 
+    /* For variation fonts, the variation coordinates to apply to each axis. */
+    const FT_Fixed *var_coords;
+    int num_var_coords;
+
     /* We temporarily scale the unscaled font as needed */
     cairo_bool_t have_scale;
     cairo_matrix_t current_scale;
     double x_scale;		/* Extracted X scale factor */
     double y_scale;             /* Extracted Y scale factor */
     cairo_bool_t have_shape;	/* true if the current scale has a non-scale component*/
     cairo_matrix_t current_shape;
     FT_Matrix Current_Shape;
@@ -355,30 +360,36 @@ static void
     CAIRO_MUTEX_UNLOCK (_cairo_ft_unscaled_font_map_mutex);
 }
 
 static void
 _cairo_ft_unscaled_font_init_key (cairo_ft_unscaled_font_t *key,
 				  cairo_bool_t              from_face,
 				  char			   *filename,
 				  int			    id,
+                                  const FT_Fixed           *var_coords,
+                                  int                       num_var_coords,
 				  FT_Face		    face)
 {
     unsigned long hash;
 
     key->from_face = from_face;
     key->filename = filename;
     key->id = id;
     key->face = face;
+    key->var_coords = var_coords;
+    key->num_var_coords = num_var_coords;
 
     hash = _cairo_hash_string (filename);
     /* the constants are just arbitrary primes */
     hash += ((unsigned long) id) * 1607;
     hash += ((unsigned long) face) * 2137;
 
+    hash = _cairo_hash_bytes (hash, var_coords, num_var_coords * sizeof(FT_Fixed));
+
     key->base.hash_entry.hash = hash;
 }
 
 /**
  * _cairo_ft_unscaled_font_init:
  *
  * Initialize a #cairo_ft_unscaled_font_t.
  *
@@ -398,35 +409,37 @@ static void
  * parallel in the from_face case, (where the calling code would have
  * to do its own mapping to ensure similar sharing).
  **/
 static cairo_status_t
 _cairo_ft_unscaled_font_init (cairo_ft_unscaled_font_t *unscaled,
 			      cairo_bool_t              from_face,
 			      const char	       *filename,
 			      int			id,
+                              const FT_Fixed           *var_coords,
+                              int                       num_var_coords,
 			      FT_Face			face)
 {
     _cairo_unscaled_font_init (&unscaled->base,
 			       &cairo_ft_unscaled_font_backend);
 
     if (from_face) {
 	unscaled->from_face = TRUE;
-	_cairo_ft_unscaled_font_init_key (unscaled, TRUE, NULL, 0, face);
+	_cairo_ft_unscaled_font_init_key (unscaled, TRUE, NULL, 0, var_coords, num_var_coords, face);
     } else {
 	char *filename_copy;
 
 	unscaled->from_face = FALSE;
 	unscaled->face = NULL;
 
 	filename_copy = strdup (filename);
 	if (unlikely (filename_copy == NULL))
 	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
-	_cairo_ft_unscaled_font_init_key (unscaled, FALSE, filename_copy, id, NULL);
+	_cairo_ft_unscaled_font_init_key (unscaled, FALSE, filename_copy, id, var_coords, num_var_coords, NULL);
     }
 
     unscaled->have_scale = FALSE;
     CAIRO_MUTEX_INIT (unscaled->mutex);
     unscaled->lock_count = 0;
 
     unscaled->faces = NULL;
 
@@ -449,33 +462,43 @@ static void
 {
     assert (unscaled->face == NULL);
 
     if (unscaled->filename) {
 	free (unscaled->filename);
 	unscaled->filename = NULL;
     }
 
+    if (unscaled->var_coords) {
+        free (unscaled->var_coords);
+        unscaled->var_coords = NULL;
+    }
+
     CAIRO_MUTEX_FINI (unscaled->mutex);
 }
 
 static int
 _cairo_ft_unscaled_font_keys_equal (const void *key_a,
 				    const void *key_b)
 {
     const cairo_ft_unscaled_font_t *unscaled_a = key_a;
     const cairo_ft_unscaled_font_t *unscaled_b = key_b;
 
     if (unscaled_a->id == unscaled_b->id &&
-	unscaled_a->from_face == unscaled_b->from_face)
+	unscaled_a->from_face == unscaled_b->from_face &&
+        unscaled_a->num_var_coords == unscaled_b->num_var_coords)
     {
         if (unscaled_a->from_face)
 	    return unscaled_a->face == unscaled_b->face;
 
-	if (unscaled_a->filename == NULL && unscaled_b->filename == NULL)
+        if (unscaled_a->num_var_coords > 0 &&
+            (memcmp (unscaled_a->var_coords, unscaled_b->var_coords,
+                     unscaled_a->num_var_coords * sizeof(FT_Fixed)) != 0))
+            return FALSE;
+	else if (unscaled_a->filename == NULL && unscaled_b->filename == NULL)
 	    return TRUE;
 	else if (unscaled_a->filename == NULL || unscaled_b->filename == NULL)
 	    return FALSE;
 	else
 	    return (strcmp (unscaled_a->filename, unscaled_b->filename) == 0);
     }
 
     return FALSE;
@@ -484,44 +507,57 @@ static int
 /* Finds or creates a #cairo_ft_unscaled_font_t for the filename/id from
  * pattern.  Returns a new reference to the unscaled font.
  */
 static cairo_status_t
 _cairo_ft_unscaled_font_create_internal (cairo_bool_t from_face,
 					 char *filename,
 					 int id,
 					 FT_Face font_face,
+                                         const FT_Fixed *var_coords,
+                                         int num_var_coords,
 					 cairo_ft_unscaled_font_t **out)
 {
     cairo_ft_unscaled_font_t key, *unscaled;
     cairo_ft_unscaled_font_map_t *font_map;
+    FT_Fixed* new_var_coords = NULL;
     cairo_status_t status;
 
     font_map = _cairo_ft_unscaled_font_map_lock ();
     if (unlikely (font_map == NULL))
 	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
-    _cairo_ft_unscaled_font_init_key (&key, from_face, filename, id, font_face);
+    _cairo_ft_unscaled_font_init_key (&key, from_face, filename, id, var_coords, num_var_coords, font_face);
 
     /* Return existing unscaled font if it exists in the hash table. */
     unscaled = _cairo_hash_table_lookup (font_map->hash_table,
 					 &key.base.hash_entry);
     if (unscaled != NULL) {
 	_cairo_unscaled_font_reference (&unscaled->base);
 	goto DONE;
     }
 
     /* Otherwise create it and insert into hash table. */
     unscaled = malloc (sizeof (cairo_ft_unscaled_font_t));
     if (unlikely (unscaled == NULL)) {
 	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
 	goto UNWIND_FONT_MAP_LOCK;
     }
 
-    status = _cairo_ft_unscaled_font_init (unscaled, from_face, filename, id, font_face);
+    /* If we have variation coordinate data, make a copy to save in the unscaled_font */
+    if (var_coords && num_var_coords) {
+        new_var_coords = malloc (num_var_coords * sizeof(FT_Fixed));
+        if (unlikely (!new_var_coords)) {
+            status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+            goto UNWIND_VAR_COORDS;
+        }
+        memcpy (new_var_coords, var_coords, num_var_coords * sizeof(FT_Fixed));
+    }
+
+    status = _cairo_ft_unscaled_font_init (unscaled, from_face, filename, id, new_var_coords, num_var_coords, font_face);
     if (unlikely (status))
 	goto UNWIND_UNSCALED_MALLOC;
 
     assert (unscaled->base.hash_entry.hash == key.base.hash_entry.hash);
     status = _cairo_hash_table_insert (font_map->hash_table,
 				       &unscaled->base.hash_entry);
     if (unlikely (status))
 	goto UNWIND_UNSCALED_FONT_INIT;
@@ -530,25 +566,28 @@ DONE:
     _cairo_ft_unscaled_font_map_unlock ();
     *out = unscaled;
     return CAIRO_STATUS_SUCCESS;
 
 UNWIND_UNSCALED_FONT_INIT:
     _cairo_ft_unscaled_font_fini (unscaled);
 UNWIND_UNSCALED_MALLOC:
     free (unscaled);
+UNWIND_VAR_COORDS:
+    free (new_var_coords);
 UNWIND_FONT_MAP_LOCK:
     _cairo_ft_unscaled_font_map_unlock ();
     return status;
 }
 
 
 #if CAIRO_HAS_FC_FONT
 static cairo_status_t
 _cairo_ft_unscaled_font_create_for_pattern (FcPattern *pattern,
+                                            const FT_Fixed *var_coords, int num_var_coords,
 					    cairo_ft_unscaled_font_t **out)
 {
     FT_Face font_face = NULL;
     char *filename = NULL;
     int id = 0;
     FcResult ret;
 
     ret = FcPatternGetFTFace (pattern, FC_FT_FACE, 0, &font_face);
@@ -571,25 +610,27 @@ static cairo_status_t
 
     /* The pattern contains neither a face nor a filename, resolve it later. */
     *out = NULL;
     return CAIRO_STATUS_SUCCESS;
 
 DONE:
     return _cairo_ft_unscaled_font_create_internal (font_face != NULL,
 						    filename, id, font_face,
+                                                    var_coords, num_var_coords,
 						    out);
 }
 #endif
 
 static cairo_status_t
-_cairo_ft_unscaled_font_create_from_face (FT_Face face,
+_cairo_ft_unscaled_font_create_from_face (FT_Face face, const FT_Fixed *var_coords, int num_var_coords,
 					  cairo_ft_unscaled_font_t **out)
 {
-    return _cairo_ft_unscaled_font_create_internal (TRUE, NULL, 0, face, out);
+    return _cairo_ft_unscaled_font_create_internal (TRUE, NULL, 0, face,
+                                                    var_coords, num_var_coords, out);
 }
 
 static void
 _cairo_ft_unscaled_font_destroy (void *abstract_font)
 {
     cairo_ft_unscaled_font_t *unscaled  = abstract_font;
     cairo_ft_unscaled_font_map_t *font_map;
 
@@ -679,16 +720,29 @@ cairo_warn FT_Face
     if (!face)
     {
 	unscaled->lock_count--;
 	CAIRO_MUTEX_UNLOCK (unscaled->mutex);
 	_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
 	return NULL;
     }
 
+    if (unscaled->var_coords) {
+        typedef FT_UInt (*SetCoordsFunc)(FT_Face, FT_UInt, FT_Fixed*);
+        static SetCoordsFunc setCoords;
+        static cairo_bool_t firstTime = TRUE;
+        if (firstTime) {
+            firstTime = FALSE;
+            (SetCoordsFunc)dlsym(RTLD_DEFAULT, "FT_Set_Var_Design_Coordinates");
+        }
+        if (setCoords) {
+            (*setCoords)(face, unscaled->num_var_coords, unscaled->var_coords);
+        }
+    }
+
     unscaled->face = face;
 
     font_map->num_open_faces++;
 
     return face;
 }
 
 
@@ -3061,17 +3115,17 @@ static cairo_font_face_t *
 	/* We failed to find any font. Substitute twin so that the user can
 	 * see something (and hopefully recognise that the font is missing)
 	 * and not just receive a NO_MEMORY error during rendering.
 	 */
 	font_face = _cairo_font_face_twin_create_fallback ();
 	goto FREE_PATTERN;
     }
 
-    status = _cairo_ft_unscaled_font_create_for_pattern (resolved, &unscaled);
+    status = _cairo_ft_unscaled_font_create_for_pattern (resolved, NULL, 0, &unscaled);
     if (unlikely (status || unscaled == NULL)) {
 	font_face = (cairo_font_face_t *)&_cairo_font_face_nil;
 	goto FREE_RESOLVED;
     }
 
     _get_pattern_ft_options (resolved, &ft_options);
     font_face = _cairo_ft_font_face_create (unscaled, &ft_options);
     _cairo_unscaled_font_destroy (&unscaled->base);
@@ -3118,24 +3172,27 @@ FREE_PATTERN:
  * time of the returned #cairo_font_face_t.  See
  * cairo_ft_font_face_create_for_ft_face() for an exmaple of how to couple
  * the life time of the FT_Face to that of the cairo font-face.
  *
  * Return value: a newly created #cairo_font_face_t. Free with
  *  cairo_font_face_destroy() when you are done using it.
  **/
 cairo_font_face_t *
-cairo_ft_font_face_create_for_pattern (FcPattern *pattern)
+cairo_ft_font_face_create_for_pattern (FcPattern *pattern,
+                                       const FT_Fixed *var_coords, int num_var_coords)
 {
     cairo_ft_unscaled_font_t *unscaled;
     cairo_font_face_t *font_face;
     cairo_ft_options_t ft_options;
     cairo_status_t status;
 
-    status = _cairo_ft_unscaled_font_create_for_pattern (pattern, &unscaled);
+    status = _cairo_ft_unscaled_font_create_for_pattern (pattern,
+                                                         var_coords, num_var_coords,
+                                                         &unscaled);
     if (unlikely (status))
 	return (cairo_font_face_t *) &_cairo_font_face_nil;
     if (unlikely (unscaled == NULL)) {
 	/* Store the pattern.  We will resolve it and create unscaled
 	 * font when creating scaled fonts */
 	status = _cairo_ft_font_face_create_for_pattern (pattern,
 							 &font_face);
 	if (unlikely (status))
@@ -3195,24 +3252,28 @@ cairo_ft_font_face_create_for_pattern (F
  * }
  * </programlisting></informalexample>
  *
  * Return value: a newly created #cairo_font_face_t. Free with
  *  cairo_font_face_destroy() when you are done using it.
  **/
 cairo_font_face_t *
 cairo_ft_font_face_create_for_ft_face (FT_Face         face,
-				       int             load_flags)
+				       int             load_flags,
+                                       const FT_Fixed *var_coords,
+                                       int             num_var_coords)
 {
     cairo_ft_unscaled_font_t *unscaled;
     cairo_font_face_t *font_face;
     cairo_ft_options_t ft_options;
     cairo_status_t status;
 
-    status = _cairo_ft_unscaled_font_create_from_face (face, &unscaled);
+    status = _cairo_ft_unscaled_font_create_from_face (face,
+                                                       var_coords, num_var_coords,
+                                                       &unscaled);
     if (unlikely (status))
 	return (cairo_font_face_t *)&_cairo_font_face_nil;
 
     ft_options.load_flags = load_flags;
     ft_options.extra_flags = 0;
     _cairo_font_options_init_default (&ft_options.base);
 
     font_face = _cairo_ft_font_face_create (unscaled, &ft_options);
--- a/gfx/cairo/cairo/src/cairo-ft.h
+++ b/gfx/cairo/cairo/src/cairo-ft.h
@@ -49,28 +49,32 @@
 #if CAIRO_HAS_FC_FONT
 #include <fontconfig/fontconfig.h>
 #endif
 
 CAIRO_BEGIN_DECLS
 
 cairo_public cairo_font_face_t *
 cairo_ft_font_face_create_for_ft_face (FT_Face         face,
-				       int             load_flags);
+				       int             load_flags,
+                                       const FT_Fixed *var_coords,
+                                       int             num_var_coords);
 
 cairo_public FT_Face
 cairo_ft_scaled_font_lock_face (cairo_scaled_font_t *scaled_font);
 
 cairo_public void
 cairo_ft_scaled_font_unlock_face (cairo_scaled_font_t *scaled_font);
 
 #if CAIRO_HAS_FC_FONT
 
 cairo_public cairo_font_face_t *
-cairo_ft_font_face_create_for_pattern (FcPattern *pattern);
+cairo_ft_font_face_create_for_pattern (FcPattern      *pattern,
+                                       const FT_Fixed *var_coords,
+                                       int             num_var_coords);
 
 cairo_public void
 cairo_ft_font_options_substitute (const cairo_font_options_t *options,
 				  FcPattern                  *pattern);
 
 #endif
 
 CAIRO_END_DECLS
--- a/gfx/thebes/gfxFT2FontBase.cpp
+++ b/gfx/thebes/gfxFT2FontBase.cpp
@@ -202,42 +202,28 @@ gfxFT2FontBase::InitMetrics()
         mMetrics.underlineOffset = -underlineSize;
         mMetrics.strikeoutOffset = 0.25 * emHeight;
         mMetrics.strikeoutSize = underlineSize;
 
         SanitizeMetrics(&mMetrics, false);
         return;
     }
 
-    // For variation fonts, figure out the variation coordinates to be applied
-    // for each axis, in freetype's order (which may not match the order of
-    // axes in mStyle.variationSettings, so we need to search by axis tag).
-    if (face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) {
-        typedef FT_UInt (*GetVarFunc)(FT_Face, FT_MM_Var**);
-        typedef FT_UInt (*SetCoordsFunc)(FT_Face, FT_UInt, FT_Fixed*);
-        static GetVarFunc getVar =
-            (GetVarFunc)dlsym(RTLD_DEFAULT, "FT_Get_MM_Var");
-        static SetCoordsFunc setCoords =
-            (SetCoordsFunc)dlsym(RTLD_DEFAULT, "FT_Set_Var_Design_Coordinates");
-        FT_MM_Var* ftVar;
-        if (getVar && setCoords && FT_Err_Ok == (*getVar)(face, &ftVar)) {
-            for (unsigned i = 0; i < ftVar->num_axis; ++i) {
-                mCoords.AppendElement(ftVar->axis[i].def);
-                for (const auto& v : mStyle.variationSettings) {
-                    if (ftVar->axis[i].tag == v.mTag) {
-                        FT_Fixed val = v.mValue * 0x10000;
-                        val = std::min(val, ftVar->axis[i].maximum);
-                        val = std::max(val, ftVar->axis[i].minimum);
-                        mCoords[i] = val;
-                        break;
-                    }
-                }
+    if (!mStyle.variationSettings.IsEmpty()) {
+        SetupVarCoords(face, mStyle.variationSettings, &mCoords);
+        if (!mCoords.IsEmpty()) {
+            typedef FT_UInt (*SetCoordsFunc)(FT_Face, FT_UInt, FT_Fixed*);
+            static SetCoordsFunc setCoords;
+            static bool firstTime = true;
+            if (firstTime) {
+                firstTime = false;
+                setCoords = (SetCoordsFunc)
+                    dlsym(RTLD_DEFAULT, "FT_Set_Var_Design_Coordinates");
             }
-            free(ftVar);
-            if (!mCoords.IsEmpty()) {
+            if (setCoords) {
                 (*setCoords)(face, mCoords.Length(), mCoords.Elements());
             }
         }
     }
 
     const FT_Size_Metrics& ftMetrics = face->size->metrics;
 
     mMetrics.maxAscent = FLOAT_FROM_26_6(ftMetrics.ascender);
@@ -576,8 +562,45 @@ gfxFT2FontBase::SetupCairoFont(DrawTarge
     //
     // I can't see any significant difference in printing, irrespective of
     // what is set here.  It's too late to change things here as measuring has
     // already taken place.  We should really be measuring with a different
     // font for pdf and ps surfaces (bug 403513).
     cairo_set_scaled_font(gfxFont::RefCairo(aDrawTarget), cairoFont);
     return true;
 }
+
+// For variation fonts, figure out the variation coordinates to be applied
+// for each axis, in freetype's order (which may not match the order of
+// axes in mStyle.variationSettings, so we need to search by axis tag).
+/*static*/
+void
+gfxFT2FontBase::SetupVarCoords(FT_Face aFace,
+                               const nsTArray<gfxFontVariation>& aVariations,
+                               nsTArray<FT_Fixed>* aCoords)
+{
+    aCoords->TruncateLength(0);
+    if (aFace->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) {
+        typedef FT_UInt (*GetVarFunc)(FT_Face, FT_MM_Var**);
+        static GetVarFunc getVar;
+        static bool firstTime = true;
+        if (firstTime) {
+            firstTime = false;
+            getVar = (GetVarFunc)dlsym(RTLD_DEFAULT, "FT_Get_MM_Var");
+        }
+        FT_MM_Var* ftVar;
+        if (getVar && FT_Err_Ok == (*getVar)(aFace, &ftVar)) {
+            for (unsigned i = 0; i < ftVar->num_axis; ++i) {
+                aCoords->AppendElement(ftVar->axis[i].def);
+                for (const auto& v : aVariations) {
+                    if (ftVar->axis[i].tag == v.mTag) {
+                        FT_Fixed val = v.mValue * 0x10000;
+                        val = std::min(val, ftVar->axis[i].maximum);
+                        val = std::max(val, ftVar->axis[i].minimum);
+                        (*aCoords)[i] = val;
+                        break;
+                    }
+                }
+            }
+            free(ftVar);
+        }
+    }
+}
--- a/gfx/thebes/gfxFT2FontBase.h
+++ b/gfx/thebes/gfxFT2FontBase.h
@@ -33,16 +33,20 @@ public:
     virtual bool ProvidesGlyphWidths() const override { return true; }
     virtual int32_t GetGlyphWidth(DrawTarget& aDrawTarget,
                                   uint16_t aGID) override;
 
     virtual bool SetupCairoFont(DrawTarget* aDrawTarget) override;
 
     virtual FontType GetType() const override { return FONT_TYPE_FT2; }
 
+    static void SetupVarCoords(FT_Face aFace,
+                               const nsTArray<gfxFontVariation>& aVariations,
+                               nsTArray<FT_Fixed>* aCoords);
+
 private:
     uint32_t GetCharExtents(char aChar, cairo_text_extents_t* aExtents);
     void InitMetrics();
 
 protected:
     virtual const Metrics& GetHorizontalMetrics() override;
 
     uint32_t mSpaceGlyph;
--- a/gfx/thebes/gfxFT2FontList.cpp
+++ b/gfx/thebes/gfxFT2FontList.cpp
@@ -385,17 +385,17 @@ FT2FontEntry::CreateFontEntry(FT_Face aF
     fe->mFilename = aFilename;
     fe->mFTFontIndex = aIndex;
 
     if (aFontData) {
         fe->mFTFace = aFace;
         int flags = gfxPlatform::GetPlatform()->FontHintingEnabled() ?
                     FT_LOAD_DEFAULT :
                     (FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING);
-        fe->mFontFace = cairo_ft_font_face_create_for_ft_face(aFace, flags);
+        fe->mFontFace = cairo_ft_font_face_create_for_ft_face(aFace, flags, nullptr, 0);
         FTUserFontData *userFontData = new FTUserFontData(aFace, aFontData);
         cairo_font_face_set_user_data(fe->mFontFace, &sFTUserFontDataKey,
                                       userFontData, FTFontDestroyFunc);
     }
 
     return fe;
 }
 
@@ -429,17 +429,17 @@ FT2FontEntry::CairoFontFace()
         AutoFTFace face(this);
         if (!face) {
             return nullptr;
         }
         mFTFace = face.forget();
         int flags = gfxPlatform::GetPlatform()->FontHintingEnabled() ?
                     FT_LOAD_DEFAULT :
                     (FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING);
-        mFontFace = cairo_ft_font_face_create_for_ft_face(face, flags);
+        mFontFace = cairo_ft_font_face_create_for_ft_face(face, flags, nullptr, 0);
         FTUserFontData *userFontData = new FTUserFontData(face, face.FontData());
         cairo_font_face_set_user_data(mFontFace, &sFTUserFontDataKey,
                                       userFontData, FTFontDestroyFunc);
     }
     return mFontFace;
 }
 
 // Copied/modified from similar code in gfxMacPlatformFontList.mm:
--- a/gfx/thebes/gfxFcPlatformFontList.cpp
+++ b/gfx/thebes/gfxFcPlatformFontList.cpp
@@ -689,18 +689,29 @@ gfxFontconfigFontEntry::CreateScaledFont
                         aStyle->allowSyntheticStyle;
 
     if (needsOblique) {
         // disable embedded bitmaps (mimics behavior in 90-synthetic.conf)
         FcPatternDel(aRenderPattern, FC_EMBEDDED_BITMAP);
         FcPatternAddBool(aRenderPattern, FC_EMBEDDED_BITMAP, FcFalse);
     }
 
+    AutoTArray<FT_Fixed,8> coords;
+    if (!aStyle->variationSettings.IsEmpty()) {
+        FT_Face ftFace = GetFTFace();
+        if (ftFace) {
+            gfxFT2FontBase::SetupVarCoords(ftFace, aStyle->variationSettings,
+                                           &coords);
+        }
+    }
+
     cairo_font_face_t *face =
-        cairo_ft_font_face_create_for_pattern(aRenderPattern);
+        cairo_ft_font_face_create_for_pattern(aRenderPattern,
+                                              coords.Elements(),
+                                              coords.Length());
 
     if (mFontData) {
         // for data fonts, add the face/data pointer to the cairo font face
         // so that it gets deleted whenever cairo decides
         NS_ASSERTION(mFTFace, "FT_Face is null when setting user data");
         NS_ASSERTION(mUserFontData, "user font data is null when setting user data");
         mUserFontData.get()->AddRef();
         if (cairo_font_face_set_user_data(face,
@@ -923,51 +934,54 @@ gfxFontconfigFontEntry::CreateFontInstan
         new gfxFontconfigFont(unscaledFont, scaledFont,
                               renderPattern, size,
                               this, aFontStyle, aNeedsBold);
     cairo_scaled_font_destroy(scaledFont);
 
     return newFont;
 }
 
+FT_Face
+gfxFontconfigFontEntry::GetFTFace()
+{
+    if (!mFTFaceInitialized) {
+        mFTFaceInitialized = true;
+        FcChar8 *filename;
+        if (FcPatternGetString(mFontPattern, FC_FILE, 0, &filename) != FcResultMatch) {
+            return nullptr;
+        }
+        int index;
+        if (FcPatternGetInteger(mFontPattern, FC_INDEX, 0, &index) != FcResultMatch) {
+            index = 0; // default to 0 if not found in pattern
+        }
+        mFTFace = Factory::NewFTFace(nullptr, ToCharPtr(filename), index);
+    }
+    return mFTFace;
+}
+
 nsresult
 gfxFontconfigFontEntry::CopyFontTable(uint32_t aTableTag,
                                       nsTArray<uint8_t>& aBuffer)
 {
     NS_ASSERTION(!mIsDataUserFont,
                  "data fonts should be reading tables directly from memory");
 
-    if (!mFTFaceInitialized) {
-        mFTFaceInitialized = true;
-        FcChar8 *filename;
-        if (FcPatternGetString(mFontPattern, FC_FILE, 0, &filename) != FcResultMatch) {
-            return NS_ERROR_FAILURE;
-        }
-        int index;
-        if (FcPatternGetInteger(mFontPattern, FC_INDEX, 0, &index) != FcResultMatch) {
-            index = 0; // default to 0 if not found in pattern
-        }
-        mFTFace = Factory::NewFTFace(nullptr, ToCharPtr(filename), index);
-        if (!mFTFace) {
-            return NS_ERROR_FAILURE;
-        }
-    }
-
-    if (!mFTFace) {
+    FT_Face face = GetFTFace();
+    if (!face) {
         return NS_ERROR_NOT_AVAILABLE;
     }
 
     FT_ULong length = 0;
-    if (FT_Load_Sfnt_Table(mFTFace, aTableTag, 0, nullptr, &length) != 0) {
+    if (FT_Load_Sfnt_Table(face, aTableTag, 0, nullptr, &length) != 0) {
         return NS_ERROR_NOT_AVAILABLE;
     }
     if (!aBuffer.SetLength(length, fallible)) {
         return NS_ERROR_OUT_OF_MEMORY;
     }
-    if (FT_Load_Sfnt_Table(mFTFace, aTableTag, 0, aBuffer.Elements(), &length) != 0) {
+    if (FT_Load_Sfnt_Table(face, aTableTag, 0, aBuffer.Elements(), &length) != 0) {
         aBuffer.Clear();
         return NS_ERROR_FAILURE;
     }
 
     return NS_OK;
 }
 
 void
--- a/gfx/thebes/gfxFcPlatformFontList.h
+++ b/gfx/thebes/gfxFcPlatformFontList.h
@@ -107,16 +107,18 @@ public:
 
     gfxFontEntry* Clone() const override;
 
     FcPattern* GetPattern() { return mFontPattern; }
 
     nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr) override;
     bool TestCharacterMap(uint32_t aCh) override;
 
+    FT_Face GetFTFace();
+
     hb_blob_t* GetFontTable(uint32_t aTableTag) override;
 
     void ForgetHBFace() override;
     void ReleaseGrFace(gr_face* aFace) override;
 
     double GetAspect();
 
 protected: