Add typeface factory registry

Based on the registration pattern of SkCodec, allow typeface factories
to be registered. This replaces and expands the existing special casing
for the deserialization of SkUserTypefaces with SkCustomTypefaceBuilder.

When a typeface is to be serialized, allow it to specify a prefered
factory for deserialization in its font descriptor. On deserialization
prefer a typeface factory with the matching factory identifier.

As an implementation detail, this required the introduction of a
concrete SkTypeface_FreeType subclass for the FreeType factory
implementation to return. This change uses this implementation to
replace a number of similar subclasses which were previously unshared
between font manager implementations.

Bug: skia:3929, chromium:1271939
Change-Id: I63cc19ea62832e51db1f7c35547017db435e0faf
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/642517
Commit-Queue: Ben Wagner <bungeman@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index 6bea5a0..507c08b 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -539,6 +539,7 @@
 optional("fontmgr_mac_ct") {
   enabled = skia_use_fonthost_mac
 
+  public_defines = [ "SK_TYPEFACE_FACTORY_CORETEXT" ]
   public = [
     "include/ports/SkFontMgr_mac_ct.h",
     "include/ports/SkTypeface_mac.h",
@@ -580,6 +581,7 @@
 optional("fontmgr_win") {
   enabled = skia_enable_fontmgr_win
 
+  public_defines = [ "SK_TYPEFACE_FACTORY_DIRECTWRITE" ]
   public = [ "include/ports/SkTypeface_win.h" ]
   sources = [
     "include/ports/SkFontMgr_indirect.h",
@@ -1381,6 +1383,7 @@
 optional("typeface_freetype") {
   enabled = skia_use_freetype
 
+  public_defines = [ "SK_TYPEFACE_FACTORY_FREETYPE" ]
   deps = [ "//third_party/freetype2" ]
   sources = [
     "src/ports/SkFontHost_FreeType.cpp",
diff --git a/defines.bzl b/defines.bzl
index dd0b37b..63d217d 100644
--- a/defines.bzl
+++ b/defines.bzl
@@ -113,6 +113,13 @@
     },
 )
 
+TYPEFACE_DEFINES = select_multi(
+    {
+        "//src/ports:uses_freetype": ["SK_TYPEFACE_FACTORY_FREETYPE"],
+        #TODO: others when they become available
+    },
+)
+
 PLATFORM_DEFINES = select({
     "//bazel/common_config_settings:cpu_wasm": [
         # working around https://github.com/emscripten-core/emscripten/issues/10072
@@ -127,6 +134,6 @@
 # building the library but not exporting it for clients, so they can use whatever vulkan they want.
 GENERAL_LOCAL_DEFINES = ["SKIA_IMPLEMENTATION=1"]
 
-DEFAULT_DEFINES = GENERAL_DEFINES + GPU_DEFINES + CODEC_DEFINES + PLATFORM_DEFINES + EXTRA_DEFINES
+DEFAULT_DEFINES = GENERAL_DEFINES + GPU_DEFINES + CODEC_DEFINES + TYPEFACE_DEFINES + PLATFORM_DEFINES + EXTRA_DEFINES
 
 DEFAULT_LOCAL_DEFINES = GENERAL_LOCAL_DEFINES
diff --git a/include/core/SkTypeface.h b/include/core/SkTypeface.h
index 6273f21..b627136 100644
--- a/include/core/SkTypeface.h
+++ b/include/core/SkTypeface.h
@@ -360,6 +360,12 @@
         return this->onGetCTFontRef();
     }
 
+    /* Skia reserves all tags that begin with a lower case letter and 0 */
+    using FactoryId = SkFourByteTag;
+    static void Register(
+            FactoryId id,
+            sk_sp<SkTypeface> (*make)(std::unique_ptr<SkStreamAsset>, const SkFontArguments&));
+
 protected:
     explicit SkTypeface(const SkFontStyle& style, bool isFixedPitch = false);
     ~SkTypeface() override;
diff --git a/include/utils/SkCustomTypeface.h b/include/utils/SkCustomTypeface.h
index afdf91e..d387fb2 100644
--- a/include/utils/SkCustomTypeface.h
+++ b/include/utils/SkCustomTypeface.h
@@ -14,12 +14,15 @@
 #include "include/core/SkPath.h"
 #include "include/core/SkRect.h"
 #include "include/core/SkRefCnt.h"
+#include "include/core/SkTypeface.h"
 #include "include/core/SkTypes.h"
 
+#include <memory>
 #include <vector>
 
 class SkStream;
-class SkTypeface;
+class SkStreamAsset;
+struct SkFontArguments;
 
 class SK_API SkCustomTypefaceBuilder {
 public:
@@ -33,6 +36,9 @@
 
     sk_sp<SkTypeface> detach();
 
+    static constexpr SkTypeface::FactoryId FactoryId = SkSetFourByteTag('u','s','e','r');
+    static sk_sp<SkTypeface> MakeFromStream(std::unique_ptr<SkStreamAsset>, const SkFontArguments&);
+
 private:
     struct GlyphRec {
         // logical union
diff --git a/src/core/SkFontDescriptor.cpp b/src/core/SkFontDescriptor.cpp
index 28ed16a..7c82e19 100644
--- a/src/core/SkFontDescriptor.cpp
+++ b/src/core/SkFontDescriptor.cpp
@@ -28,6 +28,7 @@
     kFontVariation  = 0xFA, // int count, (u32, scalar)[count]
 
     // Related to font data.
+    kFactoryId      = 0xFC, // int
     kFontIndex      = 0xFD, // int
     kSentinel       = 0xFF, // no data
 };
@@ -80,6 +81,9 @@
 };
 
 bool SkFontDescriptor::Deserialize(SkStream* stream, SkFontDescriptor* result) {
+    size_t factoryId;
+    using FactoryIdType = decltype(result->fFactoryId);
+
     size_t coordinateCount;
     using CoordinateCountType = decltype(result->fCoordinateCount);
 
@@ -178,6 +182,11 @@
                     }
                 }
                 break;
+            case kFactoryId:
+                if (!stream->readPackedUInt(&factoryId)) { return false; }
+                if (!SkTFitsIn<FactoryIdType>(factoryId)) { return false; }
+                result->fFactoryId = SkTo<FactoryIdType>(factoryId);
+                break;
             default:
                 SkDEBUGFAIL("Unknown id used by a font descriptor");
                 return false;
@@ -248,6 +257,8 @@
         }
     }
 
+    write_uint(stream, fFactoryId, kFactoryId);
+
     stream->writePackedUInt(kSentinel);
 
     if (fStream) {
diff --git a/src/core/SkFontDescriptor.h b/src/core/SkFontDescriptor.h
index 7931f1a..b4437d7 100644
--- a/src/core/SkFontDescriptor.h
+++ b/src/core/SkFontDescriptor.h
@@ -104,9 +104,12 @@
         return fVariation.get();
     }
     int getPaletteEntryOverrideCount() const { return fPaletteEntryOverrideCount; }
-    const SkFontArguments::Palette::Override* getPaletteEntryOverrides() {
+    const SkFontArguments::Palette::Override* getPaletteEntryOverrides() const {
         return fPaletteEntryOverrides.get();
     }
+    SkTypeface::FactoryId getFactoryId() {
+        return fFactoryId;
+    }
 
     std::unique_ptr<SkStreamAsset> detachStream() { return std::move(fStream); }
     void setStream(std::unique_ptr<SkStreamAsset> stream) { fStream = std::move(stream); }
@@ -120,7 +123,18 @@
         fPaletteEntryOverrideCount = paletteEntryOverrideCount;
         return fPaletteEntryOverrides.reset(paletteEntryOverrideCount);
     }
+    void setFactoryId(SkTypeface::FactoryId factoryId) {
+        fFactoryId = factoryId;
+    }
 
+    SkFontArguments getFontArguments() const {
+        return SkFontArguments()
+            .setCollectionIndex(this->getCollectionIndex())
+            .setVariationDesignPosition({this->getVariation(),this->getVariationCoordinateCount()})
+            .setPalette({this->getPaletteIndex(),
+                         this->getPaletteEntryOverrides(),
+                         this->getPaletteEntryOverrideCount()});
+    }
     static SkFontStyle::Width SkFontStyleWidthForWidthAxisValue(SkScalar width);
 
 private:
@@ -138,6 +152,7 @@
     int fPaletteIndex = 0;
     int fPaletteEntryOverrideCount = 0;
     skia_private::AutoTMalloc<SkFontArguments::Palette::Override> fPaletteEntryOverrides;
+    SkTypeface::FactoryId fFactoryId = 0;
 };
 
 #endif // SkFontDescriptor_DEFINED
diff --git a/src/core/SkTypeface.cpp b/src/core/SkTypeface.cpp
index 9f0ad47..71d826f 100644
--- a/src/core/SkTypeface.cpp
+++ b/src/core/SkTypeface.cpp
@@ -22,6 +22,18 @@
 #include "src/core/SkTypefaceCache.h"
 #include "src/sfnt/SkOTTable_OS_2.h"
 
+#ifdef SK_TYPEFACE_FACTORY_FREETYPE
+#include "src/ports/SkFontHost_FreeType_common.h"
+#endif
+
+#ifdef SK_TYPEFACE_FACTORY_CORETEXT
+#include "src/ports/SkTypeface_mac_ct.h"
+#endif
+
+#ifdef SK_TYPEFACE_FACTORY_DIRECTWRITE
+#include "src/ports/SkTypeface_win_dw.h"
+#endif
+
 using namespace skia_private;
 
 SkTypeface::SkTypeface(const SkFontStyle& style, bool isFixedPitch)
@@ -36,6 +48,15 @@
 class SkEmptyTypeface : public SkTypeface {
 public:
     static sk_sp<SkTypeface> Make() { return sk_sp<SkTypeface>(new SkEmptyTypeface); }
+
+    static constexpr SkTypeface::FactoryId FactoryId = SkSetFourByteTag('e','m','t','y');
+    static sk_sp<SkTypeface> MakeFromStream(std::unique_ptr<SkStreamAsset> stream,
+                                            const SkFontArguments&) {
+        if (stream->getLength() == 0) {
+            return SkEmptyTypeface::Make();
+        }
+        return nullptr;
+    }
 protected:
     SkEmptyTypeface() : SkTypeface(SkFontStyle(), true) { }
 
@@ -53,7 +74,10 @@
     std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override {
         return nullptr;
     }
-    void onGetFontDescriptor(SkFontDescriptor*, bool*) const override { }
+    void onGetFontDescriptor(SkFontDescriptor* desc, bool* serialize) const override {
+        desc->setFactoryId(FactoryId);
+        *serialize = false;
+    }
     void onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const override {
         sk_bzero(glyphs, count * sizeof(glyphs[0]));
     }
@@ -133,6 +157,32 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+namespace {
+
+    struct DecoderProc {
+        SkFourByteTag id;
+        sk_sp<SkTypeface> (*makeFromStream)(std::unique_ptr<SkStreamAsset>, const SkFontArguments&);
+    };
+
+    std::vector<DecoderProc>* decoders() {
+        static auto* decoders = new std::vector<DecoderProc> {
+            { SkEmptyTypeface::FactoryId, SkEmptyTypeface::MakeFromStream },
+            { SkCustomTypefaceBuilder::FactoryId, SkCustomTypefaceBuilder::MakeFromStream },
+#ifdef SK_TYPEFACE_FACTORY_CORETEXT
+            { SkTypeface_Mac::FactoryId, SkTypeface_Mac::MakeFromStream },
+#endif
+#ifdef SK_TYPEFACE_FACTORY_DIRECTWRITE
+            { DWriteFontTypeface::FactoryId, DWriteFontTypeface::MakeFromStream },
+#endif
+#ifdef SK_TYPEFACE_FACTORY_FREETYPE
+            { SkTypeface_FreeType::FactoryId, SkTypeface_FreeType::MakeFromStream },
+#endif
+        };
+        return decoders;
+    }
+
+}  // namespace
+
 sk_sp<SkTypeface> SkTypeface::MakeFromName(const char name[],
                                            SkFontStyle fontStyle) {
     if (nullptr == name && (fontStyle.slant() == SkFontStyle::kItalic_Slant ||
@@ -152,6 +202,17 @@
     if (!stream) {
         return nullptr;
     }
+    // TODO: Enable this while updating tests (FontHostStream), expectations, and nonativeFonts.
+#if 0
+    SkFontArguments args;
+    args.setCollectionIndex(index);
+    for (const DecoderProc& proc : *decoders()) {
+        sk_sp<SkTypeface> typeface = proc.makeFromStream(stream->duplicate(), args);
+        if (typeface) {
+            return typeface;
+        }
+    }
+#endif
     return SkFontMgr::RefDefault()->makeFromStream(std::move(stream), index);
 }
 
@@ -172,10 +233,19 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+void SkTypeface::Register(
+            FactoryId id,
+            sk_sp<SkTypeface> (*make)(std::unique_ptr<SkStreamAsset>, const SkFontArguments&)) {
+    decoders()->push_back(DecoderProc{id, make});
+}
+
 void SkTypeface::serialize(SkWStream* wstream, SerializeBehavior behavior) const {
     bool isLocalData = false;
     SkFontDescriptor desc;
     this->onGetFontDescriptor(&desc, &isLocalData);
+    if (desc.getFactoryId() == 0) {
+        SkDEBUGF("Factory was not set for %s.\n", desc.getFamilyName());
+    }
 
     bool shouldSerializeData = false;
     switch (behavior) {
@@ -215,20 +285,20 @@
     }
 
     if (desc.hasStream()) {
-        if (auto tf = SkCustomTypefaceBuilder::Deserialize(desc.dupStream().get())) {
-            return tf;
+        for (const DecoderProc& proc : *decoders()) {
+            if (proc.id == desc.getFactoryId()) {
+                return proc.makeFromStream(desc.detachStream(), desc.getFontArguments());
+            }
         }
-    }
 
-    if (desc.hasStream()) {
-        SkFontArguments args;
-        args.setCollectionIndex(desc.getCollectionIndex());
-        args.setVariationDesignPosition({desc.getVariation(), desc.getVariationCoordinateCount()});
-        args.setPalette({desc.getPaletteIndex(),
-                         desc.getPaletteEntryOverrides(),
-                         desc.getPaletteEntryOverrideCount()});
+        SkDEBUGCODE(FactoryId id = desc.getFactoryId();)
+        SkDEBUGF("Could not find factory %c%c%c%c for %s.\n",
+                 (id >> 24) & 0xFF, (id >> 16) & 0xFF, (id >> 8) & 0xFF, (id >> 0) & 0xFF,
+                 desc.getFamilyName());
+
         sk_sp<SkFontMgr> defaultFm = SkFontMgr::RefDefault();
-        sk_sp<SkTypeface> typeface = defaultFm->makeFromStream(desc.detachStream(), args);
+        sk_sp<SkTypeface> typeface = defaultFm->makeFromStream(desc.detachStream(),
+                                                               desc.getFontArguments());
         if (typeface) {
             return typeface;
         }
diff --git a/src/ports/SkFontConfigTypeface.h b/src/ports/SkFontConfigTypeface.h
index 8ad4e02..7955049 100644
--- a/src/ports/SkFontConfigTypeface.h
+++ b/src/ports/SkFontConfigTypeface.h
@@ -20,7 +20,6 @@
     sk_sp<SkFontConfigInterface> fFCI;
     SkFontConfigInterface::FontIdentity fIdentity;
     SkString fFamilyName;
-    std::unique_ptr<SkFontData> fFontData;
 
 public:
     static SkTypeface_FCI* Create(sk_sp<SkFontConfigInterface> fci,
@@ -31,12 +30,6 @@
         return new SkTypeface_FCI(std::move(fci), fi, std::move(familyName), style);
     }
 
-    static SkTypeface_FCI* Create(std::unique_ptr<SkFontData> data,
-                                  SkString familyName, SkFontStyle style, bool isFixedPitch)
-    {
-        return new SkTypeface_FCI(std::move(data), std::move(familyName), style, isFixedPitch);
-    }
-
     const SkFontConfigInterface::FontIdentity& getIdentity() const {
         return fIdentity;
     }
@@ -46,10 +39,9 @@
         if (!data) {
             return nullptr;
         }
-        return sk_sp<SkTypeface>(new SkTypeface_FCI(std::move(data),
-                                                    fFamilyName,
-                                                    this->fontStyle(),
-                                                    this->isFixedPitch()));
+        return sk_sp<SkTypeface>(
+            new SkTypeface_FreeTypeStream(std::move(data), fFamilyName,
+                                          this->fontStyle(), this->isFixedPitch()));
     }
 
 protected:
@@ -60,18 +52,7 @@
             : INHERITED(style, false)
             , fFCI(std::move(fci))
             , fIdentity(fi)
-            , fFamilyName(std::move(familyName))
-            , fFontData(nullptr) {}
-
-    SkTypeface_FCI(std::unique_ptr<SkFontData> data,
-                   SkString familyName, SkFontStyle style, bool isFixedPitch)
-            : INHERITED(style, isFixedPitch)
-            , fFamilyName(std::move(familyName))
-            , fFontData(std::move(data))
-    {
-        SkASSERT(fFontData);
-        fIdentity.fTTCIndex = fFontData->getIndex();
-    }
+            , fFamilyName(std::move(familyName)) {}
 
     void onGetFamilyName(SkString* familyName) const override { *familyName = fFamilyName; }
     void onGetFontDescriptor(SkFontDescriptor*, bool*) const override;
diff --git a/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.cpp
index 06cbbee..5051310 100644
--- a/src/ports/SkFontHost_FreeType.cpp
+++ b/src/ports/SkFontHost_FreeType.cpp
@@ -2294,3 +2294,72 @@
         }
     )
 }
+
+
+SkTypeface_FreeTypeStream::SkTypeface_FreeTypeStream(std::unique_ptr<SkFontData> fontData,
+                                                     const SkString familyName,
+                                                     const SkFontStyle& style, bool isFixedPitch)
+    : SkTypeface_FreeType(style, isFixedPitch)
+    , fFamilyName(std::move(familyName))
+    , fData(std::move(fontData))
+{ }
+
+SkTypeface_FreeTypeStream::~SkTypeface_FreeTypeStream() {}
+
+void SkTypeface_FreeTypeStream::onGetFamilyName(SkString* familyName) const {
+    *familyName = fFamilyName;
+}
+
+std::unique_ptr<SkStreamAsset> SkTypeface_FreeTypeStream::onOpenStream(int* ttcIndex) const {
+    *ttcIndex = fData->getIndex();
+    return fData->getStream()->duplicate();
+}
+
+std::unique_ptr<SkFontData> SkTypeface_FreeTypeStream::onMakeFontData() const {
+    return std::make_unique<SkFontData>(*fData);
+}
+
+sk_sp<SkTypeface> SkTypeface_FreeTypeStream::onMakeClone(const SkFontArguments& args) const {
+    std::unique_ptr<SkFontData> data = this->cloneFontData(args);
+    if (!data) {
+        return nullptr;
+    }
+
+    SkString familyName;
+    this->getFamilyName(&familyName);
+
+    return sk_make_sp<SkTypeface_FreeTypeStream>(
+        std::move(data), familyName, this->fontStyle(), this->isFixedPitch());
+}
+
+void SkTypeface_FreeTypeStream::onGetFontDescriptor(SkFontDescriptor* desc, bool* serialize) const {
+    desc->setFamilyName(fFamilyName.c_str());
+    desc->setStyle(this->fontStyle());
+    desc->setFactoryId(SkTypeface_FreeType::FactoryId);
+    SkTypeface_FreeType::FontDataPaletteToDescriptorPalette(*fData, desc);
+    *serialize = true;
+}
+
+sk_sp<SkTypeface> SkTypeface_FreeType::MakeFromStream(std::unique_ptr<SkStreamAsset> stream,
+                                                      const SkFontArguments& args) {
+    using Scanner = SkTypeface_FreeType::Scanner;
+    static Scanner scanner;
+    bool isFixedPitch;
+    SkFontStyle style;
+    SkString name;
+    Scanner::AxisDefinitions axisDefinitions;
+    if (!scanner.scanFont(stream.get(), args.getCollectionIndex(),
+                          &name, &style, &isFixedPitch, &axisDefinitions)) {
+        return nullptr;
+    }
+
+    const SkFontArguments::VariationPosition position = args.getVariationDesignPosition();
+    AutoSTMalloc<4, SkFixed> axisValues(axisDefinitions.size());
+    Scanner::computeAxisValues(axisDefinitions, position, axisValues, name);
+
+    auto data = std::make_unique<SkFontData>(
+        std::move(stream), args.getCollectionIndex(), args.getPalette().index,
+        axisValues.get(), axisDefinitions.size(),
+        args.getPalette().overrides, args.getPalette().overrideCount);
+    return sk_make_sp<SkTypeface_FreeTypeStream>(std::move(data), name, style, isFixedPitch);
+}
diff --git a/src/ports/SkFontHost_FreeType_common.h b/src/ports/SkFontHost_FreeType_common.h
index 6b4978a..d45b16d 100644
--- a/src/ports/SkFontHost_FreeType_common.h
+++ b/src/ports/SkFontHost_FreeType_common.h
@@ -18,7 +18,9 @@
 #include "src/core/SkSharedMutex.h"
 #include "src/utils/SkCharToGlyphCache.h"
 
-#include "include/core/SkFontMgr.h"
+struct SkAdvancedTypefaceMetrics;
+class SkFontDescriptor;
+class SkFontData;
 
 // These are forward declared to avoid pimpl but also hide the FreeType implementation.
 typedef struct FT_LibraryRec_* FT_Library;
@@ -117,6 +119,9 @@
     class FaceRec;
     FaceRec* getFaceRec() const;
 
+    static constexpr SkTypeface::FactoryId FactoryId = SkSetFourByteTag('f','r','e','e');
+    static sk_sp<SkTypeface> MakeFromStream(std::unique_ptr<SkStreamAsset>, const SkFontArguments&);
+
 protected:
     SkTypeface_FreeType(const SkFontStyle& style, bool isFixedPitch);
     ~SkTypeface_FreeType() override;
@@ -164,4 +169,22 @@
     using INHERITED = SkTypeface;
 };
 
+class SkTypeface_FreeTypeStream : public SkTypeface_FreeType {
+public:
+    SkTypeface_FreeTypeStream(std::unique_ptr<SkFontData> fontData, const SkString familyName,
+                              const SkFontStyle& style, bool isFixedPitch);
+    ~SkTypeface_FreeTypeStream() override;
+
+protected:
+    void onGetFamilyName(SkString* familyName) const override;
+    void onGetFontDescriptor(SkFontDescriptor*, bool* serialize) const override;
+    std::unique_ptr<SkStreamAsset> onOpenStream(int* ttcIndex) const override;
+    std::unique_ptr<SkFontData> onMakeFontData() const override;
+    sk_sp<SkTypeface> onMakeClone(const SkFontArguments&) const override;
+
+private:
+    const SkString fFamilyName;
+    const std::unique_ptr<const SkFontData> fData;
+};
+
 #endif // SKFONTHOST_FREETYPE_COMMON_H_
diff --git a/src/ports/SkFontMgr_FontConfigInterface.cpp b/src/ports/SkFontMgr_FontConfigInterface.cpp
index ff94d71..89685e8 100644
--- a/src/ports/SkFontMgr_FontConfigInterface.cpp
+++ b/src/ports/SkFontMgr_FontConfigInterface.cpp
@@ -22,37 +22,22 @@
 
 std::unique_ptr<SkStreamAsset> SkTypeface_FCI::onOpenStream(int* ttcIndex) const {
     *ttcIndex =  this->getIdentity().fTTCIndex;
-
-    if (fFontData) {
-        SkStreamAsset* stream = fFontData->getStream();
-        if (!stream) {
-            return nullptr;
-        }
-        return stream->duplicate();
-    }
-
     return std::unique_ptr<SkStreamAsset>(fFCI->openStream(this->getIdentity()));
 }
 
 std::unique_ptr<SkFontData> SkTypeface_FCI::onMakeFontData() const {
-    if (fFontData) {
-        return std::make_unique<SkFontData>(*fFontData);
-    }
-
     const SkFontConfigInterface::FontIdentity& id = this->getIdentity();
     return std::make_unique<SkFontData>(std::unique_ptr<SkStreamAsset>(fFCI->openStream(id)),
                                         id.fTTCIndex, 0, nullptr, 0, nullptr, 0);
 }
 
-void SkTypeface_FCI::onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocalStream) const {
+void SkTypeface_FCI::onGetFontDescriptor(SkFontDescriptor* desc, bool* serialize) const {
     SkString name;
     this->getFamilyName(&name);
     desc->setFamilyName(name.c_str());
     desc->setStyle(this->fontStyle());
-    if (fFontData) {
-        SkTypeface_FreeType::FontDataPaletteToDescriptorPalette(*fFontData, desc);
-    }
-    *isLocalStream = SkToBool(fFontData);
+    desc->setFactoryId(SkTypeface_FreeType::FactoryId);
+    *serialize = true;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -227,32 +212,12 @@
 
     sk_sp<SkTypeface> onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset> stream,
                                             int ttcIndex) const override {
-        const size_t length = stream->getLength();
-        if (!length) {
-            return nullptr;
-        }
-        if (length >= 1024 * 1024 * 1024) {
-            return nullptr;  // don't accept too large fonts (>= 1GB) for safety.
-        }
-
-        // TODO should the caller give us the style or should we get it from freetype?
-        SkString name;
-        SkFontStyle style;
-        bool isFixedPitch = false;
-        if (!fScanner.scanFont(stream.get(), ttcIndex,
-                               &name, &style, &isFixedPitch, nullptr)) {
-            return nullptr;
-        }
-
-        auto fontData = std::make_unique<SkFontData>(std::move(stream), ttcIndex, 0,
-                                                     nullptr, 0, nullptr, 0);
-        return sk_sp<SkTypeface>(SkTypeface_FCI::Create(std::move(fontData), std::move(name),
-                                                        style, isFixedPitch));
+        return this->makeFromStream(std::move(stream),
+                                    SkFontArguments().setCollectionIndex(ttcIndex));
     }
 
     sk_sp<SkTypeface> onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset> stream,
                                            const SkFontArguments& args) const override {
-        using Scanner = SkTypeface_FreeType::Scanner;
         const size_t length = stream->getLength();
         if (!length) {
             return nullptr;
@@ -261,29 +226,7 @@
             return nullptr;  // don't accept too large fonts (>= 1GB) for safety.
         }
 
-        SkString name;
-        SkFontStyle style;
-        bool isFixedPitch = false;
-        Scanner::AxisDefinitions axisDefinitions;
-        if (!fScanner.scanFont(stream.get(), args.getCollectionIndex(),
-                               &name, &style, &isFixedPitch, &axisDefinitions))
-        {
-            return nullptr;
-        }
-
-        AutoSTMalloc<4, SkFixed> axisValues(axisDefinitions.size());
-        Scanner::computeAxisValues(axisDefinitions, args.getVariationDesignPosition(),
-                                   axisValues, name);
-
-        auto fontData = std::make_unique<SkFontData>(std::move(stream),
-                                                     args.getCollectionIndex(),
-                                                     args.getPalette().index,
-                                                     axisValues.get(),
-                                                     axisDefinitions.size(),
-                                                     args.getPalette().overrides,
-                                                     args.getPalette().overrideCount);
-        return sk_sp<SkTypeface>(SkTypeface_FCI::Create(std::move(fontData), std::move(name),
-                                                        style, isFixedPitch));
+        return SkTypeface_FreeType::MakeFromStream(std::move(stream), args);
     }
 
     sk_sp<SkTypeface> onMakeFromFile(const char path[], int ttcIndex) const override {
diff --git a/src/ports/SkFontMgr_android.cpp b/src/ports/SkFontMgr_android.cpp
index 9667061..58a26e2 100644
--- a/src/ports/SkFontMgr_android.cpp
+++ b/src/ports/SkFontMgr_android.cpp
@@ -89,6 +89,7 @@
         SkASSERT(serialize);
         desc->setFamilyName(fFamilyName.c_str());
         desc->setStyle(this->fontStyle());
+        desc->setFactoryId(SkTypeface_FreeType::FactoryId);
         *serialize = false;
     }
     std::unique_ptr<SkStreamAsset> onOpenStream(int* ttcIndex) const override {
@@ -126,49 +127,6 @@
     using INHERITED = SkTypeface_Android;
 };
 
-class SkTypeface_AndroidStream : public SkTypeface_Android {
-public:
-    SkTypeface_AndroidStream(std::unique_ptr<SkFontData> data,
-                             const SkFontStyle& style,
-                             bool isFixedPitch,
-                             const SkString& familyName)
-        : INHERITED(style, isFixedPitch, familyName)
-        , fData(std::move(data))
-    { }
-
-    void onGetFontDescriptor(SkFontDescriptor* desc, bool* serialize) const override {
-        SkASSERT(desc);
-        SkASSERT(serialize);
-        desc->setFamilyName(fFamilyName.c_str());
-        SkTypeface_FreeType::FontDataPaletteToDescriptorPalette(*fData, desc);
-        *serialize = true;
-    }
-
-    std::unique_ptr<SkStreamAsset> onOpenStream(int* ttcIndex) const override {
-        *ttcIndex = fData->getIndex();
-        return fData->getStream()->duplicate();
-    }
-
-    std::unique_ptr<SkFontData> onMakeFontData() const override {
-        return std::make_unique<SkFontData>(*fData);
-    }
-
-    sk_sp<SkTypeface> onMakeClone(const SkFontArguments& args) const override {
-        std::unique_ptr<SkFontData> data = this->cloneFontData(args);
-        if (!data) {
-            return nullptr;
-        }
-        return sk_make_sp<SkTypeface_AndroidStream>(std::move(data),
-                                                    this->fontStyle(),
-                                                    this->isFixedPitch(),
-                                                    fFamilyName);
-    }
-
-private:
-    const std::unique_ptr<const SkFontData> fData;
-    using INHERITED = SkTypeface_Android;
-};
-
 class SkFontStyleSet_Android : public SkFontStyleSet {
     typedef SkTypeface_FreeType::Scanner Scanner;
 
@@ -448,41 +406,13 @@
 
     sk_sp<SkTypeface> onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset> stream,
                                             int ttcIndex) const override {
-        bool isFixedPitch;
-        SkFontStyle style;
-        SkString name;
-        if (!fScanner.scanFont(stream.get(), ttcIndex, &name, &style, &isFixedPitch, nullptr)) {
-            return nullptr;
-        }
-        auto data = std::make_unique<SkFontData>(std::move(stream), ttcIndex, 0,
-                                                 nullptr, 0, nullptr, 0);
-        return sk_sp<SkTypeface>(new SkTypeface_AndroidStream(std::move(data),
-                                                              style, isFixedPitch, name));
+        return this->makeFromStream(std::move(stream),
+                                    SkFontArguments().setCollectionIndex(ttcIndex));
     }
 
     sk_sp<SkTypeface> onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset> stream,
                                            const SkFontArguments& args) const override {
-        using Scanner = SkTypeface_FreeType::Scanner;
-        bool isFixedPitch;
-        SkFontStyle style;
-        SkString name;
-        Scanner::AxisDefinitions axisDefinitions;
-        if (!fScanner.scanFont(stream.get(), args.getCollectionIndex(),
-                               &name, &style, &isFixedPitch, &axisDefinitions))
-        {
-            return nullptr;
-        }
-
-        AutoSTMalloc<4, SkFixed> axisValues(axisDefinitions.size());
-        Scanner::computeAxisValues(axisDefinitions, args.getVariationDesignPosition(),
-                                   axisValues, name);
-
-        auto data = std::make_unique<SkFontData>(
-            std::move(stream), args.getCollectionIndex(), args.getPalette().index,
-            axisValues.get(), axisDefinitions.size(),
-            args.getPalette().overrides, args.getPalette().overrideCount);
-        return sk_sp<SkTypeface>(new SkTypeface_AndroidStream(std::move(data),
-                                                              style, isFixedPitch, name));
+        return SkTypeface_FreeType::MakeFromStream(std::move(stream), args);
     }
 
     sk_sp<SkTypeface> onLegacyMakeTypeface(const char familyName[], SkFontStyle style) const override {
diff --git a/src/ports/SkFontMgr_custom.cpp b/src/ports/SkFontMgr_custom.cpp
index 0827064..54ea68e 100644
--- a/src/ports/SkFontMgr_custom.cpp
+++ b/src/ports/SkFontMgr_custom.cpp
@@ -41,6 +41,7 @@
 void SkTypeface_Custom::onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal) const {
     desc->setFamilyName(fFamilyName.c_str());
     desc->setStyle(this->fontStyle());
+    desc->setFactoryId(SkTypeface_FreeType::FactoryId);
     *isLocal = !this->isSysFont();
 }
 
@@ -57,43 +58,6 @@
 
 std::unique_ptr<SkFontData> SkTypeface_Empty::onMakeFontData() const { return nullptr; }
 
-SkTypeface_Stream::SkTypeface_Stream(std::unique_ptr<SkFontData> fontData,
-                                     const SkFontStyle& style, bool isFixedPitch, bool sysFont,
-                                     const SkString familyName)
-    : INHERITED(style, isFixedPitch, sysFont, familyName, fontData->getIndex())
-    , fData(std::move(fontData))
-{ }
-
-std::unique_ptr<SkStreamAsset> SkTypeface_Stream::onOpenStream(int* ttcIndex) const {
-    *ttcIndex = fData->getIndex();
-    return fData->getStream()->duplicate();
-}
-
-std::unique_ptr<SkFontData> SkTypeface_Stream::onMakeFontData() const {
-    return std::make_unique<SkFontData>(*fData);
-}
-
-sk_sp<SkTypeface> SkTypeface_Stream::onMakeClone(const SkFontArguments& args) const {
-    std::unique_ptr<SkFontData> data = this->cloneFontData(args);
-    if (!data) {
-        return nullptr;
-    }
-
-    SkString familyName;
-    this->getFamilyName(&familyName);
-
-    return sk_make_sp<SkTypeface_Stream>(std::move(data),
-                                         this->fontStyle(),
-                                         this->isFixedPitch(),
-                                         this->isSysFont(),
-                                         familyName);
-}
-
-void SkTypeface_Stream::onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal) const {
-    this->SkTypeface_Custom::onGetFontDescriptor(desc, isLocal);
-    SkTypeface_FreeType::FontDataPaletteToDescriptorPalette(*fData, desc);
-}
-
 SkTypeface_File::SkTypeface_File(const SkFontStyle& style, bool isFixedPitch, bool sysFont,
                                  const SkString familyName, const char path[], int index)
     : INHERITED(style, isFixedPitch, sysFont, familyName, index)
@@ -114,11 +78,10 @@
     SkString familyName;
     this->getFamilyName(&familyName);
 
-    return sk_make_sp<SkTypeface_Stream>(std::move(data),
-                                         this->fontStyle(),
-                                         this->isFixedPitch(),
-                                         this->isSysFont(),
-                                         familyName);
+    return sk_make_sp<SkTypeface_FreeTypeStream>(std::move(data),
+                                                 familyName,
+                                                 this->fontStyle(),
+                                                 this->isFixedPitch());
 }
 
 std::unique_ptr<SkFontData> SkTypeface_File::onMakeFontData() const {
@@ -134,7 +97,7 @@
 
 SkFontStyleSet_Custom::SkFontStyleSet_Custom(const SkString familyName) : fFamilyName(familyName) {}
 
-void SkFontStyleSet_Custom::appendTypeface(sk_sp<SkTypeface_Custom> typeface) {
+void SkFontStyleSet_Custom::appendTypeface(sk_sp<SkTypeface> typeface) {
     fStyles.emplace_back(std::move(typeface));
 }
 
@@ -241,25 +204,7 @@
 
 sk_sp<SkTypeface> SkFontMgr_Custom::onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset> stream,
                                                          const SkFontArguments& args) const {
-    using Scanner = SkTypeface_FreeType::Scanner;
-    bool isFixedPitch;
-    SkFontStyle style;
-    SkString name;
-    Scanner::AxisDefinitions axisDefinitions;
-    if (!fScanner.scanFont(stream.get(), args.getCollectionIndex(),
-                           &name, &style, &isFixedPitch, &axisDefinitions)) {
-        return nullptr;
-    }
-
-    const SkFontArguments::VariationPosition position = args.getVariationDesignPosition();
-    AutoSTMalloc<4, SkFixed> axisValues(axisDefinitions.size());
-    Scanner::computeAxisValues(axisDefinitions, position, axisValues, name);
-
-    auto data = std::make_unique<SkFontData>(
-        std::move(stream), args.getCollectionIndex(), args.getPalette().index,
-        axisValues.get(), axisDefinitions.size(),
-        args.getPalette().overrides, args.getPalette().overrideCount);
-    return sk_sp<SkTypeface>(new SkTypeface_Stream(std::move(data), style, isFixedPitch, false, name));
+    return SkTypeface_FreeType::MakeFromStream(std::move(stream), args);
 }
 
 sk_sp<SkTypeface> SkFontMgr_Custom::onMakeFromFile(const char path[], int ttcIndex) const {
diff --git a/src/ports/SkFontMgr_custom.h b/src/ports/SkFontMgr_custom.h
index 3b4748b..d867f51 100644
--- a/src/ports/SkFontMgr_custom.h
+++ b/src/ports/SkFontMgr_custom.h
@@ -57,25 +57,6 @@
     using INHERITED = SkTypeface_Custom;
 };
 
-/** The stream SkTypeface implementation for the custom font manager. */
-class SkTypeface_Stream : public SkTypeface_Custom {
-public:
-    SkTypeface_Stream(std::unique_ptr<SkFontData> fontData,
-                      const SkFontStyle& style, bool isFixedPitch, bool sysFont,
-                      const SkString familyName);
-
-protected:
-    std::unique_ptr<SkStreamAsset> onOpenStream(int* ttcIndex) const override;
-    std::unique_ptr<SkFontData> onMakeFontData() const override;
-    sk_sp<SkTypeface> onMakeClone(const SkFontArguments& args) const override;
-    void onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal) const override;
-
-private:
-    const std::unique_ptr<const SkFontData> fData;
-
-    using INHERITED = SkTypeface_Custom;
-};
-
 /** The file SkTypeface implementation for the custom font manager. */
 class SkTypeface_File : public SkTypeface_Custom {
 public:
@@ -105,7 +86,7 @@
     explicit SkFontStyleSet_Custom(const SkString familyName);
 
     /** Should only be called during the initial build phase. */
-    void appendTypeface(sk_sp<SkTypeface_Custom> typeface);
+    void appendTypeface(sk_sp<SkTypeface> typeface);
     int count() override;
     void getStyle(int index, SkFontStyle* style, SkString* name) override;
     SkTypeface* createTypeface(int index) override;
@@ -113,7 +94,7 @@
     SkString getFamilyName();
 
 private:
-    SkTArray<sk_sp<SkTypeface_Custom>> fStyles;
+    SkTArray<sk_sp<SkTypeface>> fStyles;
     SkString fFamilyName;
 
     friend class SkFontMgr_Custom;
diff --git a/src/ports/SkFontMgr_custom_embedded.cpp b/src/ports/SkFontMgr_custom_embedded.cpp
index 3c26907..bd74c27 100644
--- a/src/ports/SkFontMgr_custom_embedded.cpp
+++ b/src/ports/SkFontMgr_custom_embedded.cpp
@@ -101,9 +101,8 @@
         }
         auto data = std::make_unique<SkFontData>(stream->duplicate(), faceIndex, 0,
                                                  nullptr, 0, nullptr, 0);
-        addTo->appendTypeface(sk_make_sp<SkTypeface_Stream>(std::move(data),
-                                                            style, isFixedPitch,
-                                                            true, realname));
+        addTo->appendTypeface(sk_make_sp<SkTypeface_FreeTypeStream>(
+            std::move(data), realname, style, isFixedPitch));
     }
 }
 
diff --git a/src/ports/SkFontMgr_fontconfig.cpp b/src/ports/SkFontMgr_fontconfig.cpp
index f53a171..89f1b01 100644
--- a/src/ports/SkFontMgr_fontconfig.cpp
+++ b/src/ports/SkFontMgr_fontconfig.cpp
@@ -398,51 +398,6 @@
     FcPatternAddInteger(pattern, FC_SLANT , slant);
 }
 
-class SkTypeface_stream : public SkTypeface_FreeType {
-public:
-    SkTypeface_stream(std::unique_ptr<SkFontData> data,
-                      SkString familyName, const SkFontStyle& style, bool fixedWidth)
-        : INHERITED(style, fixedWidth)
-        , fFamilyName(std::move(familyName))
-        , fData(std::move(data))
-    { }
-
-    void onGetFamilyName(SkString* familyName) const override {
-        *familyName = fFamilyName;
-    }
-
-    void onGetFontDescriptor(SkFontDescriptor* desc, bool* serialize) const override {
-        SkTypeface_FreeType::FontDataPaletteToDescriptorPalette(*fData, desc);
-        *serialize = true;
-    }
-
-    std::unique_ptr<SkStreamAsset> onOpenStream(int* ttcIndex) const override {
-        *ttcIndex = fData->getIndex();
-        return fData->getStream()->duplicate();
-    }
-
-    std::unique_ptr<SkFontData> onMakeFontData() const override {
-        return std::make_unique<SkFontData>(*fData);
-    }
-
-    sk_sp<SkTypeface> onMakeClone(const SkFontArguments& args) const override {
-        std::unique_ptr<SkFontData> data = this->cloneFontData(args);
-        if (!data) {
-            return nullptr;
-        }
-        return sk_make_sp<SkTypeface_stream>(std::move(data),
-                                             fFamilyName,
-                                             this->fontStyle(),
-                                             this->isFixedPitch());
-    }
-
-private:
-    SkString fFamilyName;
-    const std::unique_ptr<const SkFontData> fData;
-
-    using INHERITED = SkTypeface_FreeType;
-};
-
 class SkTypeface_fontconfig : public SkTypeface_FreeType {
 public:
     static sk_sp<SkTypeface_fontconfig> Make(SkAutoFcPattern pattern, SkString sysroot) {
@@ -457,11 +412,13 @@
     }
 
     void onGetFontDescriptor(SkFontDescriptor* desc, bool* serialize) const override {
+        // TODO: need to serialize FC_MATRIX and FC_EMBOLDEN
         FCLocker lock;
         desc->setFamilyName(get_string(fPattern, FC_FAMILY));
         desc->setFullName(get_string(fPattern, FC_FULLNAME));
         desc->setPostscriptName(get_string(fPattern, FC_POSTSCRIPT_NAME));
         desc->setStyle(this->fontStyle());
+        desc->setFactoryId(SkTypeface_FreeType::FactoryId);
         *serialize = false;
     }
 
@@ -528,13 +485,11 @@
             return nullptr;
         }
 
+        // TODO: need to clone FC_MATRIX and FC_EMBOLDEN
         SkString familyName;
         this->getFamilyName(&familyName);
-
-        return sk_make_sp<SkTypeface_stream>(std::move(data),
-                                             familyName,
-                                             this->fontStyle(),
-                                             this->isFixedPitch());
+        return sk_make_sp<SkTypeface_FreeTypeStream>(
+            std::move(data), familyName, this->fontStyle(), this->isFixedPitch());
     }
 
     std::unique_ptr<SkFontData> onMakeFontData() const override {
@@ -957,47 +912,17 @@
 
     sk_sp<SkTypeface> onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset> stream,
                                             int ttcIndex) const override {
-        const size_t length = stream->getLength();
-        if (length <= 0 || (1u << 30) < length) {
-            return nullptr;
-        }
-
-        SkString name;
-        SkFontStyle style;
-        bool isFixedWidth = false;
-        if (!fScanner.scanFont(stream.get(), ttcIndex, &name, &style, &isFixedWidth, nullptr)) {
-            return nullptr;
-        }
-
-        auto data = std::make_unique<SkFontData>(std::move(stream), ttcIndex, 0,
-                                                 nullptr, 0, nullptr, 0);
-        return sk_sp<SkTypeface>(new SkTypeface_stream(std::move(data), std::move(name),
-                                                       style, isFixedWidth));
+        return this->makeFromStream(std::move(stream),
+                                    SkFontArguments().setCollectionIndex(ttcIndex));
     }
 
     sk_sp<SkTypeface> onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset> stream,
                                            const SkFontArguments& args) const override {
-        using Scanner = SkTypeface_FreeType::Scanner;
-        bool isFixedPitch;
-        SkFontStyle style;
-        SkString name;
-        Scanner::AxisDefinitions axisDefinitions;
-        if (!fScanner.scanFont(stream.get(), args.getCollectionIndex(),
-                               &name, &style, &isFixedPitch, &axisDefinitions))
-        {
+        const size_t length = stream->getLength();
+        if (length <= 0 || (1u << 30) < length) {
             return nullptr;
         }
-
-        AutoSTMalloc<4, SkFixed> axisValues(axisDefinitions.size());
-        Scanner::computeAxisValues(axisDefinitions, args.getVariationDesignPosition(),
-                                   axisValues, name);
-
-        auto data = std::make_unique<SkFontData>(
-            std::move(stream), args.getCollectionIndex(), args.getPalette().index,
-            axisValues.get(), axisDefinitions.size(),
-            args.getPalette().overrides, args.getPalette().overrideCount);
-        return sk_sp<SkTypeface>(new SkTypeface_stream(std::move(data), std::move(name),
-                                                       style, isFixedPitch));
+        return SkTypeface_FreeType::MakeFromStream(std::move(stream), args);
     }
 
     sk_sp<SkTypeface> onMakeFromData(sk_sp<SkData> data, int ttcIndex) const override {
diff --git a/src/ports/SkFontMgr_fuchsia.cpp b/src/ports/SkFontMgr_fuchsia.cpp
index 4a52677..87a0f88 100644
--- a/src/ports/SkFontMgr_fuchsia.cpp
+++ b/src/ports/SkFontMgr_fuchsia.cpp
@@ -228,12 +228,11 @@
 
 constexpr kNullTypefaceId = {0xFFFFFFFF, 0xFFFFFFFF};
 
-class SkTypeface_Fuchsia : public SkTypeface_Stream {
+class SkTypeface_Fuchsia : public SkTypeface_FreeTypeStream {
 public:
     SkTypeface_Fuchsia(std::unique_ptr<SkFontData> fontData, const SkFontStyle& style,
                        bool isFixedPitch, const SkString familyName, TypefaceId id)
-            : SkTypeface_Stream(std::move(fontData), style, isFixedPitch,
-                                /*sys_font=*/true, familyName)
+            : SkTypeface_FreeTypeStream(std::move(fontData), familyName, style, isFixedPitch)
             , fId(id) {}
 
     TypefaceId id() { return fId; }
diff --git a/src/ports/SkFontMgr_mac_ct.cpp b/src/ports/SkFontMgr_mac_ct.cpp
index 7941c28..60e530e 100644
--- a/src/ports/SkFontMgr_mac_ct.cpp
+++ b/src/ports/SkFontMgr_mac_ct.cpp
@@ -228,56 +228,6 @@
 
 namespace {
 
-static sk_sp<SkData> skdata_from_skstreamasset(std::unique_ptr<SkStreamAsset> stream) {
-    size_t size = stream->getLength();
-    if (const void* base = stream->getMemoryBase()) {
-        return SkData::MakeWithProc(base, size,
-                                    [](const void*, void* ctx) -> void {
-                                        delete (SkStreamAsset*)ctx;
-                                    }, stream.release());
-    }
-    return SkData::MakeFromStream(stream.get(), size);
-}
-
-static SkUniqueCFRef<CFDataRef> cfdata_from_skdata(sk_sp<SkData> data) {
-    void const * const addr = data->data();
-    size_t const size = data->size();
-
-    CFAllocatorContext ctx = {
-        0, // CFIndex version
-        data.release(), // void* info
-        nullptr, // const void *(*retain)(const void *info);
-        nullptr, // void (*release)(const void *info);
-        nullptr, // CFStringRef (*copyDescription)(const void *info);
-        nullptr, // void * (*allocate)(CFIndex size, CFOptionFlags hint, void *info);
-        nullptr, // void*(*reallocate)(void* ptr,CFIndex newsize,CFOptionFlags hint,void* info);
-        [](void*,void* info) -> void { // void (*deallocate)(void *ptr, void *info);
-            SkASSERT(info);
-            ((SkData*)info)->unref();
-        },
-        nullptr, // CFIndex (*preferredSize)(CFIndex size, CFOptionFlags hint, void *info);
-    };
-    SkUniqueCFRef<CFAllocatorRef> alloc(CFAllocatorCreate(kCFAllocatorDefault, &ctx));
-    return SkUniqueCFRef<CFDataRef>(CFDataCreateWithBytesNoCopy(
-            kCFAllocatorDefault, (const UInt8 *)addr, size, alloc.get()));
-}
-
-static SkUniqueCFRef<CTFontRef> ctfont_from_skdata(sk_sp<SkData> data, int ttcIndex) {
-    // TODO: Use CTFontManagerCreateFontDescriptorsFromData when available.
-    if (ttcIndex != 0) {
-        return nullptr;
-    }
-
-    SkUniqueCFRef<CFDataRef> cfData(cfdata_from_skdata(std::move(data)));
-
-    SkUniqueCFRef<CTFontDescriptorRef> desc(
-            CTFontManagerCreateFontDescriptorFromData(cfData.get()));
-    if (!desc) {
-        return nullptr;
-    }
-    return SkUniqueCFRef<CTFontRef>(CTFontCreateWithFontDescriptor(desc.get(), 0, nullptr));
-}
-
 static bool find_desc_str(CTFontDescriptorRef desc, CFStringRef name, SkString* value) {
     SkUniqueCFRef<CFStringRef> ref((CFStringRef)CTFontDescriptorCopyAttribute(desc, name));
     if (!ref) {
@@ -516,84 +466,22 @@
     }
 
     sk_sp<SkTypeface> onMakeFromData(sk_sp<SkData> data, int ttcIndex) const override {
-        if (ttcIndex != 0) {
-            return nullptr;
-        }
-
-        SkUniqueCFRef<CTFontRef> ct = ctfont_from_skdata(data, ttcIndex);
-        if (!ct) {
-            return nullptr;
-        }
-
-        return SkTypeface_Mac::Make(std::move(ct), OpszVariation(),
-                                    SkMemoryStream::Make(std::move(data)));
+        return this->makeFromStream(
+                std::unique_ptr<SkStreamAsset>(new SkMemoryStream(std::move(data))), ttcIndex);
     }
 
     sk_sp<SkTypeface> onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset> stream,
                                             int ttcIndex) const override {
-        if (ttcIndex != 0) {
-            return nullptr;
-        }
-
-        sk_sp<SkData> data = skdata_from_skstreamasset(stream->duplicate());
-        if (!data) {
-            return nullptr;
-        }
-        SkUniqueCFRef<CTFontRef> ct = ctfont_from_skdata(std::move(data), ttcIndex);
-        if (!ct) {
-            return nullptr;
-        }
-
-        return SkTypeface_Mac::Make(std::move(ct), OpszVariation(), std::move(stream));
+        return this->makeFromStream(std::move(stream),
+                                    SkFontArguments().setCollectionIndex(ttcIndex));
     }
 
     sk_sp<SkTypeface> onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset> stream,
-                                           const SkFontArguments& args) const override
-    {
-        // TODO: Use CTFontManagerCreateFontDescriptorsFromData when available.
-        int ttcIndex = args.getCollectionIndex();
-        if (ttcIndex != 0) {
-            return nullptr;
-        }
-
-        sk_sp<SkData> data = skdata_from_skstreamasset(stream->duplicate());
-        if (!data) {
-            return nullptr;
-        }
-        SkUniqueCFRef<CTFontRef> ct = ctfont_from_skdata(std::move(data), ttcIndex);
-        if (!ct) {
-            return nullptr;
-        }
-
-        SkUniqueCFRef<CFArrayRef> axes(CTFontCopyVariationAxes(ct.get()));
-        CTFontVariation ctVariation = SkCTVariationFromSkFontArguments(ct.get(), axes.get(), args);
-
-        SkUniqueCFRef<CTFontRef> ctVariant;
-        if (ctVariation.variation) {
-            SkUniqueCFRef<CFMutableDictionaryRef> attributes(
-                    CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
-                                              &kCFTypeDictionaryKeyCallBacks,
-                                              &kCFTypeDictionaryValueCallBacks));
-            CFDictionaryAddValue(attributes.get(),
-                                 kCTFontVariationAttribute, ctVariation.variation.get());
-            SkUniqueCFRef<CTFontDescriptorRef> varDesc(
-                    CTFontDescriptorCreateWithAttributes(attributes.get()));
-            ctVariant.reset(CTFontCreateCopyWithAttributes(ct.get(), 0, nullptr, varDesc.get()));
-        } else {
-            ctVariant.reset(ct.release());
-        }
-        if (!ctVariant) {
-            return nullptr;
-        }
-
-        return SkTypeface_Mac::Make(std::move(ctVariant), ctVariation.opsz, std::move(stream));
+                                           const SkFontArguments& args) const override {
+        return SkTypeface_Mac::MakeFromStream(std::move(stream), args);
     }
 
     sk_sp<SkTypeface> onMakeFromFile(const char path[], int ttcIndex) const override {
-        if (ttcIndex != 0) {
-            return nullptr;
-        }
-
         sk_sp<SkData> data = SkData::MakeFromFileName(path);
         if (!data) {
             return nullptr;
diff --git a/src/ports/SkFontMgr_win_dw.cpp b/src/ports/SkFontMgr_win_dw.cpp
index e8f46d7..d930441 100644
--- a/src/ports/SkFontMgr_win_dw.cpp
+++ b/src/ports/SkFontMgr_win_dw.cpp
@@ -32,241 +32,6 @@
 
 using namespace skia_private;
 
-////////////////////////////////////////////////////////////////////////////////
-
-class StreamFontFileLoader : public IDWriteFontFileLoader {
-public:
-    // IUnknown methods
-    SK_STDMETHODIMP QueryInterface(REFIID iid, void** ppvObject) override;
-    SK_STDMETHODIMP_(ULONG) AddRef() override;
-    SK_STDMETHODIMP_(ULONG) Release() override;
-
-    // IDWriteFontFileLoader methods
-    SK_STDMETHODIMP CreateStreamFromKey(
-        void const* fontFileReferenceKey,
-        UINT32 fontFileReferenceKeySize,
-        IDWriteFontFileStream** fontFileStream) override;
-
-    // Takes ownership of stream.
-    static HRESULT Create(std::unique_ptr<SkStreamAsset> stream,
-                          StreamFontFileLoader** streamFontFileLoader) {
-        *streamFontFileLoader = new StreamFontFileLoader(std::move(stream));
-        if (nullptr == *streamFontFileLoader) {
-            return E_OUTOFMEMORY;
-        }
-        return S_OK;
-    }
-
-private:
-    StreamFontFileLoader(std::unique_ptr<SkStreamAsset> stream)
-        : fStream(std::move(stream)), fRefCount(1)
-    {}
-    virtual ~StreamFontFileLoader() { }
-
-    std::unique_ptr<SkStreamAsset> fStream;
-    ULONG fRefCount;
-};
-
-SK_STDMETHODIMP StreamFontFileLoader::QueryInterface(REFIID iid, void** ppvObject) {
-    if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileLoader)) {
-        *ppvObject = this;
-        AddRef();
-        return S_OK;
-    } else {
-        *ppvObject = nullptr;
-        return E_NOINTERFACE;
-    }
-}
-
-SK_STDMETHODIMP_(ULONG) StreamFontFileLoader::AddRef() {
-    return InterlockedIncrement(&fRefCount);
-}
-
-SK_STDMETHODIMP_(ULONG) StreamFontFileLoader::Release() {
-    ULONG newCount = InterlockedDecrement(&fRefCount);
-    if (0 == newCount) {
-        delete this;
-    }
-    return newCount;
-}
-
-SK_STDMETHODIMP StreamFontFileLoader::CreateStreamFromKey(
-    void const* fontFileReferenceKey,
-    UINT32 fontFileReferenceKeySize,
-    IDWriteFontFileStream** fontFileStream)
-{
-    SkTScopedComPtr<SkDWriteFontFileStreamWrapper> stream;
-    HR(SkDWriteFontFileStreamWrapper::Create(fStream->duplicate().release(), &stream));
-    *fontFileStream = stream.release();
-    return S_OK;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-class StreamFontFileEnumerator : public IDWriteFontFileEnumerator {
-public:
-    // IUnknown methods
-    SK_STDMETHODIMP QueryInterface(REFIID iid, void** ppvObject) override;
-    SK_STDMETHODIMP_(ULONG) AddRef() override;
-    SK_STDMETHODIMP_(ULONG) Release() override;
-
-    // IDWriteFontFileEnumerator methods
-    SK_STDMETHODIMP MoveNext(BOOL* hasCurrentFile) override;
-    SK_STDMETHODIMP GetCurrentFontFile(IDWriteFontFile** fontFile) override;
-
-    static HRESULT Create(IDWriteFactory* factory, IDWriteFontFileLoader* fontFileLoader,
-                          StreamFontFileEnumerator** streamFontFileEnumerator) {
-        *streamFontFileEnumerator = new StreamFontFileEnumerator(factory, fontFileLoader);
-        if (nullptr == *streamFontFileEnumerator) {
-            return E_OUTOFMEMORY;
-        }
-        return S_OK;
-    }
-private:
-    StreamFontFileEnumerator(IDWriteFactory* factory, IDWriteFontFileLoader* fontFileLoader);
-    virtual ~StreamFontFileEnumerator() { }
-
-    ULONG fRefCount;
-
-    SkTScopedComPtr<IDWriteFactory> fFactory;
-    SkTScopedComPtr<IDWriteFontFile> fCurrentFile;
-    SkTScopedComPtr<IDWriteFontFileLoader> fFontFileLoader;
-    bool fHasNext;
-};
-
-StreamFontFileEnumerator::StreamFontFileEnumerator(IDWriteFactory* factory,
-                                                   IDWriteFontFileLoader* fontFileLoader)
-    : fRefCount(1)
-    , fFactory(SkRefComPtr(factory))
-    , fCurrentFile()
-    , fFontFileLoader(SkRefComPtr(fontFileLoader))
-    , fHasNext(true)
-{ }
-
-SK_STDMETHODIMP StreamFontFileEnumerator::QueryInterface(REFIID iid, void** ppvObject) {
-    if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileEnumerator)) {
-        *ppvObject = this;
-        AddRef();
-        return S_OK;
-    } else {
-        *ppvObject = nullptr;
-        return E_NOINTERFACE;
-    }
-}
-
-SK_STDMETHODIMP_(ULONG) StreamFontFileEnumerator::AddRef() {
-    return InterlockedIncrement(&fRefCount);
-}
-
-SK_STDMETHODIMP_(ULONG) StreamFontFileEnumerator::Release() {
-    ULONG newCount = InterlockedDecrement(&fRefCount);
-    if (0 == newCount) {
-        delete this;
-    }
-    return newCount;
-}
-
-SK_STDMETHODIMP StreamFontFileEnumerator::MoveNext(BOOL* hasCurrentFile) {
-    *hasCurrentFile = FALSE;
-
-    if (!fHasNext) {
-        return S_OK;
-    }
-    fHasNext = false;
-
-    UINT32 fontFileReferenceKey = 0;
-    HR(fFactory->CreateCustomFontFileReference(
-            &fontFileReferenceKey, //cannot be nullptr
-            sizeof(fontFileReferenceKey), //even if this is 0
-            fFontFileLoader.get(),
-            &fCurrentFile));
-
-    *hasCurrentFile = TRUE;
-    return S_OK;
-}
-
-SK_STDMETHODIMP StreamFontFileEnumerator::GetCurrentFontFile(IDWriteFontFile** fontFile) {
-    if (fCurrentFile.get() == nullptr) {
-        *fontFile = nullptr;
-        return E_FAIL;
-    }
-
-    *fontFile = SkRefComPtr(fCurrentFile.get());
-    return  S_OK;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-class StreamFontCollectionLoader : public IDWriteFontCollectionLoader {
-public:
-    // IUnknown methods
-    SK_STDMETHODIMP QueryInterface(REFIID iid, void** ppvObject) override;
-    SK_STDMETHODIMP_(ULONG) AddRef() override;
-    SK_STDMETHODIMP_(ULONG) Release() override;
-
-    // IDWriteFontCollectionLoader methods
-    SK_STDMETHODIMP CreateEnumeratorFromKey(
-        IDWriteFactory* factory,
-        void const* collectionKey,
-        UINT32 collectionKeySize,
-        IDWriteFontFileEnumerator** fontFileEnumerator) override;
-
-    static HRESULT Create(IDWriteFontFileLoader* fontFileLoader,
-                          StreamFontCollectionLoader** streamFontCollectionLoader) {
-        *streamFontCollectionLoader = new StreamFontCollectionLoader(fontFileLoader);
-        if (nullptr == *streamFontCollectionLoader) {
-            return E_OUTOFMEMORY;
-        }
-        return S_OK;
-    }
-private:
-    StreamFontCollectionLoader(IDWriteFontFileLoader* fontFileLoader)
-        : fRefCount(1)
-        , fFontFileLoader(SkRefComPtr(fontFileLoader))
-    { }
-    virtual ~StreamFontCollectionLoader() { }
-
-    ULONG fRefCount;
-    SkTScopedComPtr<IDWriteFontFileLoader> fFontFileLoader;
-};
-
-SK_STDMETHODIMP StreamFontCollectionLoader::QueryInterface(REFIID iid, void** ppvObject) {
-    if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontCollectionLoader)) {
-        *ppvObject = this;
-        AddRef();
-        return S_OK;
-    } else {
-        *ppvObject = nullptr;
-        return E_NOINTERFACE;
-    }
-}
-
-SK_STDMETHODIMP_(ULONG) StreamFontCollectionLoader::AddRef() {
-    return InterlockedIncrement(&fRefCount);
-}
-
-SK_STDMETHODIMP_(ULONG) StreamFontCollectionLoader::Release() {
-    ULONG newCount = InterlockedDecrement(&fRefCount);
-    if (0 == newCount) {
-        delete this;
-    }
-    return newCount;
-}
-
-SK_STDMETHODIMP StreamFontCollectionLoader::CreateEnumeratorFromKey(
-    IDWriteFactory* factory,
-    void const* collectionKey,
-    UINT32 collectionKeySize,
-    IDWriteFontFileEnumerator** fontFileEnumerator)
-{
-    SkTScopedComPtr<StreamFontFileEnumerator> enumerator;
-    HR(StreamFontFileEnumerator::Create(factory, fFontFileLoader.get(), &enumerator));
-    *fontFileEnumerator = enumerator.release();
-    return S_OK;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
 namespace {
 
 // Korean fonts Gulim, Dotum, Batang, Gungsuh have bitmap strikes that get
@@ -985,41 +750,6 @@
     return returnTypeface;
 }
 
-template <typename T> class SkAutoIDWriteUnregister {
-public:
-    SkAutoIDWriteUnregister(IDWriteFactory* factory, T* unregister)
-        : fFactory(factory), fUnregister(unregister)
-    { }
-    SkAutoIDWriteUnregister(const SkAutoIDWriteUnregister&) = delete;
-    SkAutoIDWriteUnregister& operator=(const SkAutoIDWriteUnregister&) = delete;
-    SkAutoIDWriteUnregister(SkAutoIDWriteUnregister&&) = delete;
-    SkAutoIDWriteUnregister& operator=(SkAutoIDWriteUnregister&&) = delete;
-
-    ~SkAutoIDWriteUnregister() {
-        if (fUnregister) {
-            unregister(fFactory, fUnregister);
-        }
-    }
-
-    T* detatch() {
-        T* old = fUnregister;
-        fUnregister = nullptr;
-        return old;
-    }
-
-private:
-    HRESULT unregister(IDWriteFactory* factory, IDWriteFontFileLoader* unregister) {
-        return factory->UnregisterFontFileLoader(unregister);
-    }
-
-    HRESULT unregister(IDWriteFactory* factory, IDWriteFontCollectionLoader* unregister) {
-        return factory->UnregisterFontCollectionLoader(unregister);
-    }
-
-    IDWriteFactory* fFactory;
-    T* fUnregister;
-};
-
 sk_sp<SkTypeface> SkFontMgr_DirectWrite::onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset> stream,
                                                                int ttcIndex) const {
     SkFontArguments args;
@@ -1027,101 +757,9 @@
     return this->onMakeFromStreamArgs(std::move(stream), args);
 }
 
-static HRESULT apply_fontargument_variation(SkTScopedComPtr<IDWriteFontFace>& fontFace,
-                                            const SkFontArguments& args)
-{
-#if defined(NTDDI_WIN10_RS3) && NTDDI_VERSION >= NTDDI_WIN10_RS3
-
-    SkTScopedComPtr<IDWriteFontFace5> fontFace5;
-    if (FAILED(fontFace->QueryInterface(&fontFace5)) || !fontFace5->HasVariations()) {
-        return S_OK;
-    }
-
-    UINT32 fontAxisCount = fontFace5->GetFontAxisValueCount();
-    UINT32 argsCoordCount = args.getVariationDesignPosition().coordinateCount;
-    AutoSTMalloc<8, DWRITE_FONT_AXIS_VALUE> variation(fontAxisCount);
-    SkTScopedComPtr<IDWriteFontResource> fontResource;
-    HR(fontFace5->GetFontResource(&fontResource));
-    HR(fontResource->GetDefaultFontAxisValues(variation, fontAxisCount));
-
-    for (UINT32 fontAxisIndex = 0; fontAxisIndex < fontAxisCount; ++fontAxisIndex) {
-        DWRITE_FONT_AXIS_VALUE& fontCoordinate = variation[fontAxisIndex];
-
-        for (UINT32 argsCoordIndex = argsCoordCount; argsCoordIndex --> 0;) {
-            const SkFontArguments::VariationPosition::Coordinate& argsCoordinate =
-                args.getVariationDesignPosition().coordinates[argsCoordIndex];
-            if (SkEndian_SwapBE32(fontCoordinate.axisTag) == argsCoordinate.axis) {
-                fontCoordinate.value = argsCoordinate.value;
-                break;
-            }
-        }
-    }
-
-    SkTScopedComPtr<IDWriteFontFace5> fontFace5_Out;
-    HR(fontResource->CreateFontFace(DWRITE_FONT_SIMULATIONS_NONE,
-                                    variation.get(), fontAxisCount,
-                                    &fontFace5_Out));
-    fontFace.reset();
-    HR(fontFace5_Out->QueryInterface(&fontFace));
-#endif
-    return S_OK;
-}
 sk_sp<SkTypeface> SkFontMgr_DirectWrite::onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset> stream,
                                                               const SkFontArguments& args) const {
-    SkTScopedComPtr<StreamFontFileLoader> fontFileLoader;
-    HRN(StreamFontFileLoader::Create(std::move(stream), &fontFileLoader));
-    HRN(fFactory->RegisterFontFileLoader(fontFileLoader.get()));
-    SkAutoIDWriteUnregister<StreamFontFileLoader> autoUnregisterFontFileLoader(
-            fFactory.get(), fontFileLoader.get());
-
-    SkTScopedComPtr<StreamFontCollectionLoader> fontCollectionLoader;
-    HRN(StreamFontCollectionLoader::Create(fontFileLoader.get(), &fontCollectionLoader));
-    HRN(fFactory->RegisterFontCollectionLoader(fontCollectionLoader.get()));
-    SkAutoIDWriteUnregister<StreamFontCollectionLoader> autoUnregisterFontCollectionLoader(
-            fFactory.get(), fontCollectionLoader.get());
-
-    SkTScopedComPtr<IDWriteFontCollection> fontCollection;
-    HRN(fFactory->CreateCustomFontCollection(fontCollectionLoader.get(), nullptr, 0,
-                                             &fontCollection));
-
-    // Find the first non-simulated font which has the given ttc index.
-    UINT32 familyCount = fontCollection->GetFontFamilyCount();
-    for (UINT32 familyIndex = 0; familyIndex < familyCount; ++familyIndex) {
-        SkTScopedComPtr<IDWriteFontFamily> fontFamily;
-        HRN(fontCollection->GetFontFamily(familyIndex, &fontFamily));
-
-        UINT32 fontCount = fontFamily->GetFontCount();
-        for (UINT32 fontIndex = 0; fontIndex < fontCount; ++fontIndex) {
-            SkTScopedComPtr<IDWriteFont> font;
-            HRN(fontFamily->GetFont(fontIndex, &font));
-
-            // Skip if the current font is simulated
-            if (font->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE) {
-                continue;
-            }
-            SkTScopedComPtr<IDWriteFontFace> fontFace;
-            HRN(font->CreateFontFace(&fontFace));
-            int faceIndex = fontFace->GetIndex();
-            int ttcIndex = args.getCollectionIndex();
-
-            // Skip if the current face index does not match the ttcIndex
-            if (faceIndex != ttcIndex) {
-                continue;
-            }
-
-            apply_fontargument_variation(fontFace, args);
-
-            return DWriteFontTypeface::Make(
-                    fFactory.get(), fontFace.get(), font.get(), fontFamily.get(),
-                    sk_make_sp<DWriteFontTypeface::Loaders>(
-                        fFactory.get(),
-                        autoUnregisterFontFileLoader.detatch(),
-                        autoUnregisterFontCollectionLoader.detatch()),
-                    args.getPalette());
-        }
-    }
-
-    return nullptr;
+    return DWriteFontTypeface::MakeFromStream(std::move(stream), args);
 }
 
 sk_sp<SkTypeface> SkFontMgr_DirectWrite::onMakeFromData(sk_sp<SkData> data, int ttcIndex) const {
diff --git a/src/ports/SkTypeface_mac_ct.cpp b/src/ports/SkTypeface_mac_ct.cpp
index 0e139b4..22a20cf 100644
--- a/src/ports/SkTypeface_mac_ct.cpp
+++ b/src/ports/SkTypeface_mac_ct.cpp
@@ -1078,6 +1078,7 @@
     desc->setFullName(get_str(CTFontCopyFullName(fFontRef.get()), &tmpStr));
     desc->setPostscriptName(get_str(CTFontCopyPostScriptName(fFontRef.get()), &tmpStr));
     desc->setStyle(this->fontStyle());
+    desc->setFactoryId(FactoryId);
     *isLocalStream = fIsFromStream;
 }
 
@@ -1128,8 +1129,8 @@
 }
 
 /** Creates a dictionary suitable for setting the axes on a CTFont. */
-CTFontVariation SkCTVariationFromSkFontArguments(CTFontRef ct, CFArrayRef ctAxes,
-                                                 const SkFontArguments& args) {
+static CTFontVariation ctvariation_from_SkFontArguments(CTFontRef ct, CFArrayRef ctAxes,
+                                                        const SkFontArguments& args) {
     OpszVariation opsz;
     constexpr const SkFourByteTag opszTag = SkSetFourByteTag('o','p','s','z');
 
@@ -1233,7 +1234,7 @@
 }
 
 sk_sp<SkTypeface> SkTypeface_Mac::onMakeClone(const SkFontArguments& args) const {
-    CTFontVariation ctVariation = SkCTVariationFromSkFontArguments(fFontRef.get(),
+    CTFontVariation ctVariation = ctvariation_from_SkFontArguments(fFontRef.get(),
                                                                    this->getVariationAxes(),
                                                                    args);
 
@@ -1281,6 +1282,103 @@
                                 fStream ? fStream->duplicate() : nullptr);
 }
 
+static sk_sp<SkData> skdata_from_skstreamasset(std::unique_ptr<SkStreamAsset> stream) {
+    size_t size = stream->getLength();
+    if (const void* base = stream->getMemoryBase()) {
+        return SkData::MakeWithProc(base, size,
+                                    [](const void*, void* ctx) -> void {
+                                        delete (SkStreamAsset*)ctx;
+                                    }, stream.release());
+    }
+    return SkData::MakeFromStream(stream.get(), size);
+}
+
+static SkUniqueCFRef<CFDataRef> cfdata_from_skdata(sk_sp<SkData> data) {
+    void const * const addr = data->data();
+    size_t const size = data->size();
+
+    CFAllocatorContext ctx = {
+        0, // CFIndex version
+        data.release(), // void* info
+        nullptr, // const void *(*retain)(const void *info);
+        nullptr, // void (*release)(const void *info);
+        nullptr, // CFStringRef (*copyDescription)(const void *info);
+        nullptr, // void * (*allocate)(CFIndex size, CFOptionFlags hint, void *info);
+        nullptr, // void*(*reallocate)(void* ptr,CFIndex newsize,CFOptionFlags hint,void* info);
+        [](void*,void* info) -> void { // void (*deallocate)(void *ptr, void *info);
+            SkASSERT(info);
+            ((SkData*)info)->unref();
+        },
+        nullptr, // CFIndex (*preferredSize)(CFIndex size, CFOptionFlags hint, void *info);
+    };
+    SkUniqueCFRef<CFAllocatorRef> alloc(CFAllocatorCreate(kCFAllocatorDefault, &ctx));
+    return SkUniqueCFRef<CFDataRef>(CFDataCreateWithBytesNoCopy(
+            kCFAllocatorDefault, (const UInt8 *)addr, size, alloc.get()));
+}
+
+static SkUniqueCFRef<CTFontRef> ctfont_from_skdata(sk_sp<SkData> data, int ttcIndex) {
+    // TODO: Use CTFontManagerCreateFontDescriptorsFromData when available.
+    if (ttcIndex != 0) {
+        return nullptr;
+    }
+
+    SkUniqueCFRef<CFDataRef> cfData(cfdata_from_skdata(std::move(data)));
+
+    SkUniqueCFRef<CTFontDescriptorRef> desc(
+            CTFontManagerCreateFontDescriptorFromData(cfData.get()));
+    if (!desc) {
+        return nullptr;
+    }
+    return SkUniqueCFRef<CTFontRef>(CTFontCreateWithFontDescriptor(desc.get(), 0, nullptr));
+}
+
+sk_sp<SkTypeface> SkTypeface_Mac::MakeFromStream(std::unique_ptr<SkStreamAsset> stream,
+                                                 const SkFontArguments& args)
+{
+    // TODO: Use CTFontManagerCreateFontDescriptorsFromData when available.
+    int ttcIndex = args.getCollectionIndex();
+    if (ttcIndex != 0) {
+        return nullptr;
+    }
+
+    sk_sp<SkData> data = skdata_from_skstreamasset(stream->duplicate());
+    if (!data) {
+        return nullptr;
+    }
+    SkUniqueCFRef<CTFontRef> ct = ctfont_from_skdata(std::move(data), ttcIndex);
+    if (!ct) {
+        return nullptr;
+    }
+
+    SkUniqueCFRef<CTFontRef> ctVariant;
+    CTFontVariation ctVariation;
+    if (args.getVariationDesignPosition().coordinateCount == 0) {
+        ctVariant.reset(ct.release());
+    } else {
+        SkUniqueCFRef<CFArrayRef> axes(CTFontCopyVariationAxes(ct.get()));
+        ctVariation = ctvariation_from_SkFontArguments(ct.get(), axes.get(), args);
+
+        if (ctVariation.variation) {
+            SkUniqueCFRef<CFMutableDictionaryRef> attributes(
+                     CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+                                               &kCFTypeDictionaryKeyCallBacks,
+                                               &kCFTypeDictionaryValueCallBacks));
+            CFDictionaryAddValue(attributes.get(),
+                                 kCTFontVariationAttribute, ctVariation.variation.get());
+            SkUniqueCFRef<CTFontDescriptorRef> varDesc(
+                    CTFontDescriptorCreateWithAttributes(attributes.get()));
+            ctVariant.reset(CTFontCreateCopyWithAttributes(ct.get(), 0, nullptr, varDesc.get()));
+        } else {
+            ctVariant.reset(ct.release());
+        }
+    }
+    if (!ctVariant) {
+        return nullptr;
+    }
+
+    return SkTypeface_Mac::Make(std::move(ctVariant), ctVariation.opsz, std::move(stream));
+}
+
 int SkTypeface_Mac::onGetVariationDesignParameters(SkFontParameters::Variation::Axis parameters[],
                                                    int parameterCount) const
 {
diff --git a/src/ports/SkTypeface_mac_ct.h b/src/ports/SkTypeface_mac_ct.h
index 1547e6e..b708de9 100644
--- a/src/ports/SkTypeface_mac_ct.h
+++ b/src/ports/SkTypeface_mac_ct.h
@@ -55,9 +55,6 @@
     OpszVariation opsz;
 };
 
-CTFontVariation SkCTVariationFromSkFontArguments(CTFontRef ct, CFArrayRef ctAxes,
-                                                 const SkFontArguments& args);
-
 SkUniqueCFRef<CTFontRef> SkCTFontCreateExactCopy(CTFontRef baseFont, CGFloat textSize,
                                                  OpszVariation opsz);
 
@@ -88,6 +85,9 @@
                                   OpszVariation opszVariation,
                                   std::unique_ptr<SkStreamAsset> providedData);
 
+    static constexpr SkTypeface::FactoryId FactoryId = SkSetFourByteTag('c','t','x','t');
+    static sk_sp<SkTypeface> MakeFromStream(std::unique_ptr<SkStreamAsset>, const SkFontArguments&);
+
     SkUniqueCFRef<CTFontRef> fFontRef;
     const OpszVariation fOpszVariation;
     const bool fHasColorGlyphs;
diff --git a/src/ports/SkTypeface_win_dw.cpp b/src/ports/SkTypeface_win_dw.cpp
index bc5ac35..92da4ae 100644
--- a/src/ports/SkTypeface_win_dw.cpp
+++ b/src/ports/SkTypeface_win_dw.cpp
@@ -128,6 +128,7 @@
     this->initializePalette();
 }
 
+DWriteFontTypeface::~DWriteFontTypeface() = default;
 
 DWriteFontTypeface::Loaders::~Loaders() {
     // Don't return if any fail, just keep going to free up as much as possible.
@@ -171,7 +172,7 @@
 }
 
 void DWriteFontTypeface::onGetFontDescriptor(SkFontDescriptor* desc,
-                                             bool* isLocalStream) const {
+                                             bool* serialize) const {
     // Get the family name.
     SkTScopedComPtr<IDWriteLocalizedStrings> familyNames;
     HRV(fDWriteFontFamily->GetFamilyNames(&familyNames));
@@ -187,7 +188,8 @@
                       fRequestedPalette.overrides,
                       fRequestedPalette.overrideCount * sizeof(fRequestedPalette.overrides[0]));
 
-    *isLocalStream = SkToBool(fLoaders);
+    desc->setFactoryId(FactoryId);
+    *serialize = SkToBool(fLoaders);
 }
 
 void DWriteFontTypeface::onCharsToGlyphs(const SkUnichar* uni, int count,
@@ -713,4 +715,371 @@
                                     (int32_t)SkEndian_SwapBE16((uint16_t)headTable->yMin));
     return info;
 }
+
+class StreamFontFileLoader : public IDWriteFontFileLoader {
+public:
+    // IUnknown methods
+    SK_STDMETHODIMP QueryInterface(REFIID iid, void** ppvObject) override;
+    SK_STDMETHODIMP_(ULONG) AddRef() override;
+    SK_STDMETHODIMP_(ULONG) Release() override;
+
+    // IDWriteFontFileLoader methods
+    SK_STDMETHODIMP CreateStreamFromKey(
+        void const* fontFileReferenceKey,
+        UINT32 fontFileReferenceKeySize,
+        IDWriteFontFileStream** fontFileStream) override;
+
+    // Takes ownership of stream.
+    static HRESULT Create(std::unique_ptr<SkStreamAsset> stream,
+        StreamFontFileLoader** streamFontFileLoader) {
+        *streamFontFileLoader = new StreamFontFileLoader(std::move(stream));
+        if (nullptr == *streamFontFileLoader) {
+            return E_OUTOFMEMORY;
+        }
+        return S_OK;
+    }
+
+private:
+    StreamFontFileLoader(std::unique_ptr<SkStreamAsset> stream)
+        : fStream(std::move(stream)), fRefCount(1)
+    {}
+    virtual ~StreamFontFileLoader() { }
+
+    std::unique_ptr<SkStreamAsset> fStream;
+    ULONG fRefCount;
+};
+
+SK_STDMETHODIMP StreamFontFileLoader::QueryInterface(REFIID iid, void** ppvObject) {
+    if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileLoader)) {
+        *ppvObject = this;
+        AddRef();
+        return S_OK;
+    } else {
+        *ppvObject = nullptr;
+        return E_NOINTERFACE;
+    }
+}
+
+SK_STDMETHODIMP_(ULONG) StreamFontFileLoader::AddRef() {
+    return InterlockedIncrement(&fRefCount);
+}
+
+SK_STDMETHODIMP_(ULONG) StreamFontFileLoader::Release() {
+    ULONG newCount = InterlockedDecrement(&fRefCount);
+    if (0 == newCount) {
+        delete this;
+    }
+    return newCount;
+}
+
+SK_STDMETHODIMP StreamFontFileLoader::CreateStreamFromKey(
+    void const* fontFileReferenceKey,
+    UINT32 fontFileReferenceKeySize,
+    IDWriteFontFileStream** fontFileStream)
+{
+    SkTScopedComPtr<SkDWriteFontFileStreamWrapper> stream;
+    HR(SkDWriteFontFileStreamWrapper::Create(fStream->duplicate().release(), &stream));
+    *fontFileStream = stream.release();
+    return S_OK;
+}
+
+class StreamFontFileEnumerator : public IDWriteFontFileEnumerator {
+public:
+    // IUnknown methods
+    SK_STDMETHODIMP QueryInterface(REFIID iid, void** ppvObject) override;
+    SK_STDMETHODIMP_(ULONG) AddRef() override;
+    SK_STDMETHODIMP_(ULONG) Release() override;
+
+    // IDWriteFontFileEnumerator methods
+    SK_STDMETHODIMP MoveNext(BOOL* hasCurrentFile) override;
+    SK_STDMETHODIMP GetCurrentFontFile(IDWriteFontFile** fontFile) override;
+
+    static HRESULT Create(IDWriteFactory* factory, IDWriteFontFileLoader* fontFileLoader,
+        StreamFontFileEnumerator** streamFontFileEnumerator) {
+        *streamFontFileEnumerator = new StreamFontFileEnumerator(factory, fontFileLoader);
+        if (nullptr == *streamFontFileEnumerator) {
+            return E_OUTOFMEMORY;
+        }
+        return S_OK;
+    }
+private:
+    StreamFontFileEnumerator(IDWriteFactory* factory, IDWriteFontFileLoader* fontFileLoader);
+    virtual ~StreamFontFileEnumerator() { }
+
+    ULONG fRefCount;
+
+    SkTScopedComPtr<IDWriteFactory> fFactory;
+    SkTScopedComPtr<IDWriteFontFile> fCurrentFile;
+    SkTScopedComPtr<IDWriteFontFileLoader> fFontFileLoader;
+    bool fHasNext;
+};
+
+StreamFontFileEnumerator::StreamFontFileEnumerator(IDWriteFactory* factory,
+    IDWriteFontFileLoader* fontFileLoader)
+    : fRefCount(1)
+    , fFactory(SkRefComPtr(factory))
+    , fCurrentFile()
+    , fFontFileLoader(SkRefComPtr(fontFileLoader))
+    , fHasNext(true)
+{ }
+
+SK_STDMETHODIMP StreamFontFileEnumerator::QueryInterface(REFIID iid, void** ppvObject) {
+    if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileEnumerator)) {
+        *ppvObject = this;
+        AddRef();
+        return S_OK;
+    } else {
+        *ppvObject = nullptr;
+        return E_NOINTERFACE;
+    }
+}
+
+SK_STDMETHODIMP_(ULONG) StreamFontFileEnumerator::AddRef() {
+    return InterlockedIncrement(&fRefCount);
+}
+
+SK_STDMETHODIMP_(ULONG) StreamFontFileEnumerator::Release() {
+    ULONG newCount = InterlockedDecrement(&fRefCount);
+    if (0 == newCount) {
+        delete this;
+    }
+    return newCount;
+}
+
+SK_STDMETHODIMP StreamFontFileEnumerator::MoveNext(BOOL* hasCurrentFile) {
+    *hasCurrentFile = FALSE;
+
+    if (!fHasNext) {
+        return S_OK;
+    }
+    fHasNext = false;
+
+    UINT32 fontFileReferenceKey = 0;
+    HR(fFactory->CreateCustomFontFileReference(
+        &fontFileReferenceKey, //cannot be nullptr
+        sizeof(fontFileReferenceKey), //even if this is 0
+        fFontFileLoader.get(),
+        &fCurrentFile));
+
+    *hasCurrentFile = TRUE;
+    return S_OK;
+}
+
+SK_STDMETHODIMP StreamFontFileEnumerator::GetCurrentFontFile(IDWriteFontFile** fontFile) {
+    if (fCurrentFile.get() == nullptr) {
+        *fontFile = nullptr;
+        return E_FAIL;
+    }
+
+    *fontFile = SkRefComPtr(fCurrentFile.get());
+    return  S_OK;
+}
+
+class StreamFontCollectionLoader : public IDWriteFontCollectionLoader {
+public:
+    // IUnknown methods
+    SK_STDMETHODIMP QueryInterface(REFIID iid, void** ppvObject) override;
+    SK_STDMETHODIMP_(ULONG) AddRef() override;
+    SK_STDMETHODIMP_(ULONG) Release() override;
+
+    // IDWriteFontCollectionLoader methods
+    SK_STDMETHODIMP CreateEnumeratorFromKey(
+        IDWriteFactory* factory,
+        void const* collectionKey,
+        UINT32 collectionKeySize,
+        IDWriteFontFileEnumerator** fontFileEnumerator) override;
+
+    static HRESULT Create(IDWriteFontFileLoader* fontFileLoader,
+        StreamFontCollectionLoader** streamFontCollectionLoader) {
+        *streamFontCollectionLoader = new StreamFontCollectionLoader(fontFileLoader);
+        if (nullptr == *streamFontCollectionLoader) {
+            return E_OUTOFMEMORY;
+        }
+        return S_OK;
+    }
+private:
+    StreamFontCollectionLoader(IDWriteFontFileLoader* fontFileLoader)
+        : fRefCount(1)
+        , fFontFileLoader(SkRefComPtr(fontFileLoader))
+    { }
+    virtual ~StreamFontCollectionLoader() { }
+
+    ULONG fRefCount;
+    SkTScopedComPtr<IDWriteFontFileLoader> fFontFileLoader;
+};
+
+SK_STDMETHODIMP StreamFontCollectionLoader::QueryInterface(REFIID iid, void** ppvObject) {
+    if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontCollectionLoader)) {
+        *ppvObject = this;
+        AddRef();
+        return S_OK;
+    } else {
+        *ppvObject = nullptr;
+        return E_NOINTERFACE;
+    }
+}
+
+SK_STDMETHODIMP_(ULONG) StreamFontCollectionLoader::AddRef() {
+    return InterlockedIncrement(&fRefCount);
+}
+
+SK_STDMETHODIMP_(ULONG) StreamFontCollectionLoader::Release() {
+    ULONG newCount = InterlockedDecrement(&fRefCount);
+    if (0 == newCount) {
+        delete this;
+    }
+    return newCount;
+}
+
+template <typename T> class SkAutoIDWriteUnregister {
+public:
+    SkAutoIDWriteUnregister(IDWriteFactory* factory, T* unregister)
+        : fFactory(factory), fUnregister(unregister)
+    { }
+    SkAutoIDWriteUnregister(const SkAutoIDWriteUnregister&) = delete;
+    SkAutoIDWriteUnregister& operator=(const SkAutoIDWriteUnregister&) = delete;
+    SkAutoIDWriteUnregister(SkAutoIDWriteUnregister&&) = delete;
+    SkAutoIDWriteUnregister& operator=(SkAutoIDWriteUnregister&&) = delete;
+
+    ~SkAutoIDWriteUnregister() {
+        if (fUnregister) {
+            unregister(fFactory, fUnregister);
+        }
+    }
+
+    T* detatch() {
+        T* old = fUnregister;
+        fUnregister = nullptr;
+        return old;
+    }
+
+private:
+    HRESULT unregister(IDWriteFactory* factory, IDWriteFontFileLoader* unregister) {
+        return factory->UnregisterFontFileLoader(unregister);
+    }
+
+    HRESULT unregister(IDWriteFactory* factory, IDWriteFontCollectionLoader* unregister) {
+        return factory->UnregisterFontCollectionLoader(unregister);
+    }
+
+    IDWriteFactory* fFactory;
+    T* fUnregister;
+};
+
+SK_STDMETHODIMP StreamFontCollectionLoader::CreateEnumeratorFromKey(
+    IDWriteFactory* factory,
+    void const* collectionKey,
+    UINT32 collectionKeySize,
+    IDWriteFontFileEnumerator** fontFileEnumerator)
+{
+    SkTScopedComPtr<StreamFontFileEnumerator> enumerator;
+    HR(StreamFontFileEnumerator::Create(factory, fFontFileLoader.get(), &enumerator));
+    *fontFileEnumerator = enumerator.release();
+    return S_OK;
+}
+
+static HRESULT apply_fontargument_variation(SkTScopedComPtr<IDWriteFontFace>& fontFace,
+    const SkFontArguments& args)
+{
+#if defined(NTDDI_WIN10_RS3) && NTDDI_VERSION >= NTDDI_WIN10_RS3
+
+    SkTScopedComPtr<IDWriteFontFace5> fontFace5;
+    if (FAILED(fontFace->QueryInterface(&fontFace5)) || !fontFace5->HasVariations()) {
+        return S_OK;
+    }
+
+    UINT32 fontAxisCount = fontFace5->GetFontAxisValueCount();
+    UINT32 argsCoordCount = args.getVariationDesignPosition().coordinateCount;
+    AutoSTMalloc<8, DWRITE_FONT_AXIS_VALUE> variation(fontAxisCount);
+    SkTScopedComPtr<IDWriteFontResource> fontResource;
+    HR(fontFace5->GetFontResource(&fontResource));
+    HR(fontResource->GetDefaultFontAxisValues(variation, fontAxisCount));
+
+    for (UINT32 fontAxisIndex = 0; fontAxisIndex < fontAxisCount; ++fontAxisIndex) {
+        DWRITE_FONT_AXIS_VALUE& fontCoordinate = variation[fontAxisIndex];
+
+        for (UINT32 argsCoordIndex = argsCoordCount; argsCoordIndex --> 0;) {
+            const SkFontArguments::VariationPosition::Coordinate& argsCoordinate =
+                args.getVariationDesignPosition().coordinates[argsCoordIndex];
+            if (SkEndian_SwapBE32(fontCoordinate.axisTag) == argsCoordinate.axis) {
+                fontCoordinate.value = argsCoordinate.value;
+                break;
+            }
+        }
+    }
+
+    SkTScopedComPtr<IDWriteFontFace5> fontFace5_Out;
+    HR(fontResource->CreateFontFace(DWRITE_FONT_SIMULATIONS_NONE,
+        variation.get(), fontAxisCount,
+        &fontFace5_Out));
+    fontFace.reset();
+    HR(fontFace5_Out->QueryInterface(&fontFace));
+#endif
+    return S_OK;
+}
+
+sk_sp<SkTypeface> DWriteFontTypeface::MakeFromStream(std::unique_ptr<SkStreamAsset> stream,
+                                                     const SkFontArguments& args) {
+    // TODO: do we need to use some user provided factory?
+    IDWriteFactory* factory = sk_get_dwrite_factory();
+    if (nullptr == factory) {
+        return nullptr;
+    }
+
+    SkTScopedComPtr<StreamFontFileLoader> fontFileLoader;
+    HRN(StreamFontFileLoader::Create(std::move(stream), &fontFileLoader));
+    HRN(factory->RegisterFontFileLoader(fontFileLoader.get()));
+    SkAutoIDWriteUnregister<StreamFontFileLoader> autoUnregisterFontFileLoader(
+        factory, fontFileLoader.get());
+
+    SkTScopedComPtr<StreamFontCollectionLoader> fontCollectionLoader;
+    HRN(StreamFontCollectionLoader::Create(fontFileLoader.get(), &fontCollectionLoader));
+    HRN(factory->RegisterFontCollectionLoader(fontCollectionLoader.get()));
+    SkAutoIDWriteUnregister<StreamFontCollectionLoader> autoUnregisterFontCollectionLoader(
+        factory, fontCollectionLoader.get());
+
+    SkTScopedComPtr<IDWriteFontCollection> fontCollection;
+    HRN(factory->CreateCustomFontCollection(fontCollectionLoader.get(), nullptr, 0,
+        &fontCollection));
+
+    // Find the first non-simulated font which has the given ttc index.
+    UINT32 familyCount = fontCollection->GetFontFamilyCount();
+    for (UINT32 familyIndex = 0; familyIndex < familyCount; ++familyIndex) {
+        SkTScopedComPtr<IDWriteFontFamily> fontFamily;
+        HRN(fontCollection->GetFontFamily(familyIndex, &fontFamily));
+
+        UINT32 fontCount = fontFamily->GetFontCount();
+        for (UINT32 fontIndex = 0; fontIndex < fontCount; ++fontIndex) {
+            SkTScopedComPtr<IDWriteFont> font;
+            HRN(fontFamily->GetFont(fontIndex, &font));
+
+            // Skip if the current font is simulated
+            if (font->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE) {
+                continue;
+            }
+            SkTScopedComPtr<IDWriteFontFace> fontFace;
+            HRN(font->CreateFontFace(&fontFace));
+            int faceIndex = fontFace->GetIndex();
+            int ttcIndex = args.getCollectionIndex();
+
+            // Skip if the current face index does not match the ttcIndex
+            if (faceIndex != ttcIndex) {
+                continue;
+            }
+
+            apply_fontargument_variation(fontFace, args);
+
+            return DWriteFontTypeface::Make(
+                factory, fontFace.get(), font.get(), fontFamily.get(),
+                sk_make_sp<DWriteFontTypeface::Loaders>(
+                    factory,
+                    autoUnregisterFontFileLoader.detatch(),
+                    autoUnregisterFontCollectionLoader.detatch()),
+                args.getPalette());
+        }
+    }
+
+    return nullptr;
+}
+
 #endif//defined(SK_BUILD_FOR_WIN)
diff --git a/src/ports/SkTypeface_win_dw.h b/src/ports/SkTypeface_win_dw.h
index df5078a..79b5c95 100644
--- a/src/ports/SkTypeface_win_dw.h
+++ b/src/ports/SkTypeface_win_dw.h
@@ -25,6 +25,14 @@
 class SkFontDescriptor;
 struct SkScalerContextRec;
 
+/* dwrite_3.h incorrectly uses NTDDI_VERSION to hide immutible interfaces (it should only be used to
+   gate changes to public ABI). The implementation files can (and must) get away with including
+   SkDWriteNTDDI_VERSION.h which simply unsets NTDDI_VERSION, but this doesn't work well for this
+   header which can be included in SkTypeface.cpp. Instead, ensure that any declarations hidden
+   behind the NTDDI_VERSION are forward (backward?) declared here in case dwrite_3.h did not declare
+   them. */
+interface IDWriteFontFace4;
+
 static SkFontStyle get_style(IDWriteFont* font) {
     int weight = font->GetWeight();
     int width = font->GetStretch();
@@ -59,6 +67,10 @@
         SkTScopedComPtr<IDWriteFontCollectionLoader> fDWriteFontCollectionLoader;
     };
 
+    static constexpr SkTypeface::FactoryId FactoryId = SkSetFourByteTag('d','w','r','t');
+    static sk_sp<SkTypeface> MakeFromStream(std::unique_ptr<SkStreamAsset>, const SkFontArguments&);
+
+    ~DWriteFontTypeface() override;
 private:
     DWriteFontTypeface(const SkFontStyle& style,
                        IDWriteFactory* factory,
diff --git a/src/utils/SkCustomTypeface.cpp b/src/utils/SkCustomTypeface.cpp
index cf779e2..d4c20de 100644
--- a/src/utils/SkCustomTypeface.cpp
+++ b/src/utils/SkCustomTypeface.cpp
@@ -32,6 +32,7 @@
 #include "include/private/base/SkMalloc.h"
 #include "include/private/base/SkTo.h"
 #include "src/core/SkAdvancedTypefaceMetrics.h" // IWYU pragma: keep
+#include "src/core/SkFontDescriptor.h"
 #include "src/core/SkGlyph.h"
 #include "src/core/SkMask.h"
 #include "src/core/SkScalerContext.h"
@@ -45,7 +46,6 @@
 
 class SkArenaAlloc;
 class SkDescriptor;
-class SkFontDescriptor;
 
 namespace {
 static inline const constexpr bool kSkShowTextBlitCoverage = false;
@@ -214,10 +214,11 @@
 }
 
 void SkUserTypeface::onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal) const {
+    desc->setFactoryId(SkCustomTypefaceBuilder::FactoryId);
     *isLocal = true;
 }
 
-void SkUserTypeface::onCharsToGlyphs(const SkUnichar chars[], int count, SkGlyphID glyphs[]) const {
+void SkUserTypeface::onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const {
     for (int i = 0; i < count; ++i) {
         glyphs[i] = chars[i] < this->glyphCount() ? SkTo<SkGlyphID>(chars[i]) : 0;
     }
@@ -515,3 +516,8 @@
     arp.markDone();
     return builder.detach();
 }
+
+sk_sp<SkTypeface> SkCustomTypefaceBuilder::MakeFromStream(std::unique_ptr<SkStreamAsset> stream,
+                                                          const SkFontArguments&) {
+    return Deserialize(stream.get());
+}
diff --git a/tests/FontHostStreamTest.cpp b/tests/FontHostStreamTest.cpp
index 0262cf2..6cd6c48 100644
--- a/tests/FontHostStreamTest.cpp
+++ b/tests/FontHostStreamTest.cpp
@@ -74,16 +74,11 @@
         SkFont font(SkTypeface::MakeFromName("Georgia", SkFontStyle()), 30);
         font.setEdging(SkFont::Edging::kAlias);
 
-        SkIRect origRect = SkIRect::MakeWH(64, 64);
+        const SkIRect origRect = SkIRect::MakeWH(64, 64);
         SkBitmap origBitmap;
         create(&origBitmap, origRect);
         SkCanvas origCanvas(origBitmap);
 
-        SkIRect streamRect = SkIRect::MakeWH(64, 64);
-        SkBitmap streamBitmap;
-        create(&streamBitmap, streamRect);
-        SkCanvas streamCanvas(streamBitmap);
-
         SkPoint point = SkPoint::Make(24, 32);
 
         // Test: origTypeface and streamTypeface from orig data draw the same
@@ -91,27 +86,63 @@
         origCanvas.drawString("A", point.fX, point.fY, font, paint);
 
         sk_sp<SkTypeface> typeface = font.refTypefaceOrDefault();
-        int ttcIndex;
-        std::unique_ptr<SkStreamAsset> fontData = typeface->openStream(&ttcIndex);
-        if (!fontData) {
-            // We're using a SkTypeface that can't give us a stream.
-            // This happens with portable or system fonts.  End the test now.
-            return;
+
+        {
+            SkDynamicMemoryWStream wstream;
+            typeface->serialize(&wstream, SkTypeface::SerializeBehavior::kDoIncludeData);
+            std::unique_ptr<SkStreamAsset> stream = wstream.detachAsStream();
+            sk_sp<SkTypeface> deserializedTypeface = SkTypeface::MakeDeserialize(&*stream);
+            if (!deserializedTypeface) {
+                REPORTER_ASSERT(reporter, deserializedTypeface);
+                return;
+            }
+
+            SkFontDescriptor desc;
+            bool mustSerializeData = false;
+            deserializedTypeface->getFontDescriptor(&desc, &mustSerializeData);
+            REPORTER_ASSERT(reporter, mustSerializeData);
+
+            SkBitmap deserializedBitmap;
+            create(&deserializedBitmap, origRect);
+            SkCanvas deserializedCanvas(deserializedBitmap);
+
+            font.setTypeface(deserializedTypeface);
+            drawBG(&deserializedCanvas);
+            deserializedCanvas.drawString("A", point.fX, point.fY, font, paint);
+
+            REPORTER_ASSERT(reporter, compare(origBitmap, origRect, deserializedBitmap, origRect));
         }
 
-        sk_sp<SkTypeface> streamTypeface(SkTypeface::MakeFromStream(std::move(fontData)));
+        {
+            int ttcIndex;
+            std::unique_ptr<SkStreamAsset> fontData = typeface->openStream(&ttcIndex);
+            if (!fontData) {
+                REPORTER_ASSERT(reporter, fontData);
+                return;
+            }
 
-        SkFontDescriptor desc;
-        bool isLocalStream = false;
-        streamTypeface->getFontDescriptor(&desc, &isLocalStream);
-        REPORTER_ASSERT(reporter, isLocalStream);
+            sk_sp<SkTypeface> streamTypeface(SkTypeface::MakeFromStream(std::move(fontData)));
+            if (!streamTypeface) {
+                // TODO: enable assert after SkTypeface::MakeFromStream uses factories
+                //REPORTER_ASSERT(reporter, streamTypeface);
+                return;
+            }
 
-        font.setTypeface(streamTypeface);
-        drawBG(&streamCanvas);
-        streamCanvas.drawString("A", point.fX, point.fY, font, paint);
+            SkBitmap streamBitmap;
+            create(&streamBitmap, origRect);
+            SkCanvas streamCanvas(streamBitmap);
 
-        REPORTER_ASSERT(reporter,
-                        compare(origBitmap, origRect, streamBitmap, streamRect));
+            SkFontDescriptor desc;
+            bool mustSerializeData = false;
+            streamTypeface->getFontDescriptor(&desc, &mustSerializeData);
+            REPORTER_ASSERT(reporter, mustSerializeData);
+
+            font.setTypeface(streamTypeface);
+            drawBG(&streamCanvas);
+            streamCanvas.drawString("A", point.fX, point.fY, font, paint);
+
+            REPORTER_ASSERT(reporter, compare(origBitmap, origRect, streamBitmap, origRect));
+        }
     }
     //Make sure the typeface is deleted and removed.
     SkGraphics::PurgeFontCache();
diff --git a/tests/TextBlobTest.cpp b/tests/TextBlobTest.cpp
index bb2731a..acf70a5 100644
--- a/tests/TextBlobTest.cpp
+++ b/tests/TextBlobTest.cpp
@@ -196,7 +196,7 @@
         // Kitchen sink font.
         font.setSize(42);
         font.setScaleX(4.2f);
-        font.setTypeface(ToolUtils::create_portable_typeface());
+        font.setTypeface(ToolUtils::create_portable_typeface(nullptr, SkFontStyle::Bold()));
         font.setSkewX(0.42f);
         font.setHinting(SkFontHinting::kFull);
         font.setEdging(SkFont::Edging::kSubpixelAntiAlias);
diff --git a/tests/TypefaceTest.cpp b/tests/TypefaceTest.cpp
index 17d737a..d917a7e 100644
--- a/tests/TypefaceTest.cpp
+++ b/tests/TypefaceTest.cpp
@@ -144,8 +144,7 @@
     int fontIndex;
     std::unique_ptr<SkStreamAsset> stream = typeface->openStream(&fontIndex);
 
-    sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
-    sk_sp<SkTypeface> typeface2 = fm->makeFromStream(std::move(stream), fontIndex);
+    sk_sp<SkTypeface> typeface2 = SkTypeface::MakeFromStream(std::move(stream), fontIndex);
     REPORTER_ASSERT(reporter, typeface2);
 }
 
@@ -525,18 +524,22 @@
     REPORTER_ASSERT(reporter, t1->unique());
 }
 
-static void check_serialize_behaviors(sk_sp<SkTypeface> tf, bool isLocalData,
-                                      skiatest::Reporter* reporter) {
+static void check_serialize_behaviors(sk_sp<SkTypeface> tf, skiatest::Reporter* reporter) {
     if (!tf) {
         return;
     }
+
+    SkFontDescriptor desc;
+    bool serialize;
+    tf->getFontDescriptor(&desc, &serialize);
+
     auto data0 = tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
     auto data1 = tf->serialize(SkTypeface::SerializeBehavior::kDontIncludeData);
     auto data2 = tf->serialize(SkTypeface::SerializeBehavior::kIncludeDataIfLocal);
 
     REPORTER_ASSERT(reporter, data0->size() >= data1->size());
 
-    if (isLocalData) {
+    if (serialize) {
         REPORTER_ASSERT(reporter, data0->equals(data2.get()));
     } else {
         REPORTER_ASSERT(reporter, data1->equals(data2.get()));
@@ -544,10 +547,9 @@
 }
 
 DEF_TEST(Typeface_serialize, reporter) {
-    check_serialize_behaviors(SkTypeface::MakeDefault(), false, reporter);
-    check_serialize_behaviors(SkTypeface::MakeFromStream(
-                                         GetResourceAsStream("fonts/Distortable.ttf")),
-                              true, reporter);
+    check_serialize_behaviors(SkTypeface::MakeDefault(), reporter);
+    check_serialize_behaviors(
+        SkTypeface::MakeFromStream(GetResourceAsStream("fonts/Distortable.ttf")), reporter);
 
 }
 
diff --git a/tools/fonts/TestFontMgr.cpp b/tools/fonts/TestFontMgr.cpp
index e82477a..92167b4 100644
--- a/tools/fonts/TestFontMgr.cpp
+++ b/tools/fonts/TestFontMgr.cpp
@@ -5,6 +5,9 @@
  * found in the LICENSE file.
  */
 
+#include "include/core/SkPathBuilder.h"
+#include "include/private/base/SkAssert.h"
+#include "include/utils/SkCustomTypeface.h"
 #include "src/core/SkFontDescriptor.h"
 #include "tools/ToolUtils.h"
 #include "tools/fonts/TestFontMgr.h"
@@ -18,12 +21,6 @@
 
 namespace {
 
-#include "tools/fonts/test_font_monospace.inc"
-#include "tools/fonts/test_font_sans_serif.inc"
-#include "tools/fonts/test_font_serif.inc"
-
-#include "tools/fonts/test_font_index.inc"
-
 class FontStyleSet final : public SkFontStyleSet {
 public:
     FontStyleSet(const char* familyName) : fFamilyName(familyName) {}
@@ -63,38 +60,23 @@
 class FontMgr final : public SkFontMgr {
 public:
     FontMgr() {
-        for (const auto& sub : gSubFonts) {
-            sk_sp<TestTypeface> typeface =
-                    sk_make_sp<TestTypeface>(sk_make_sp<SkTestFont>(sub.fFont), sub.fStyle);
-            bool defaultFamily = false;
-            if (&sub - gSubFonts == gDefaultFontIndex) {
-                defaultFamily    = true;
-                fDefaultTypeface = typeface;
-            }
-            bool found = false;
-            for (const auto& family : fFamilies) {
-                if (family->getFamilyName().equals(sub.fFamilyName)) {
-                    family->fTypefaces.emplace_back(
-                            std::move(typeface), sub.fStyle, sub.fStyleName);
-                    found = true;
-                    if (defaultFamily) {
-                        fDefaultFamily = family;
-                    }
-                    break;
-                }
-            }
-            if (!found) {
-                fFamilies.emplace_back(sk_make_sp<FontStyleSet>(sub.fFamilyName));
-                fFamilies.back()->fTypefaces.emplace_back(
-                        // NOLINTNEXTLINE(bugprone-use-after-move)
-                        std::move(typeface),
-                        sub.fStyle,
-                        sub.fStyleName);
-                if (defaultFamily) {
-                    fDefaultFamily = fFamilies.back();
+        auto&& list = TestTypeface::Typefaces();
+        for (auto&& family : list.families) {
+            auto&& ss = fFamilies.emplace_back(sk_make_sp<FontStyleSet>(family.name));
+            for (auto&& face : family.faces) {
+                ss->fTypefaces.emplace_back(face.typeface, face.typeface->fontStyle(), face.name);
+                if (face.isDefault) {
+                    fDefaultFamily = ss;
+                    fDefaultTypeface = face.typeface;
                 }
             }
         }
+        if (!fDefaultFamily) {
+            SkASSERTF(false, "expected TestTypeface to return a default");
+            fDefaultFamily = fFamilies[0];
+            fDefaultTypeface = fDefaultFamily->fTypefaces[0].fTypeface;
+        }
+
 #if defined(SK_ENABLE_SVG)
         fFamilies.emplace_back(sk_make_sp<FontStyleSet>("Emoji"));
         fFamilies.back()->fTypefaces.emplace_back(
diff --git a/tools/fonts/TestSVGTypeface.cpp b/tools/fonts/TestSVGTypeface.cpp
index e42d2d2..8d01084 100644
--- a/tools/fonts/TestSVGTypeface.cpp
+++ b/tools/fonts/TestSVGTypeface.cpp
@@ -149,10 +149,10 @@
     return info;
 }
 
-void TestSVGTypeface::onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal) const {
+void TestSVGTypeface::onGetFontDescriptor(SkFontDescriptor* desc, bool* serialize) const {
     desc->setFamilyName(fName.c_str());
     desc->setStyle(this->fontStyle());
-    *isLocal = false;
+    *serialize = true;
 }
 
 void TestSVGTypeface::onCharsToGlyphs(const SkUnichar uni[], int count, SkGlyphID glyphs[]) const {
@@ -303,6 +303,47 @@
             sk_ref_sp(const_cast<TestSVGTypeface*>(this)), e, desc);
 }
 
+class DefaultTypeface : public TestSVGTypeface {
+    using TestSVGTypeface::TestSVGTypeface;
+
+    bool getPathOp(SkColor color, SkPathOp* op) const override {
+        if ((SkColorGetR(color) + SkColorGetG(color) + SkColorGetB(color)) / 3 > 0x20) {
+            *op = SkPathOp::kDifference_SkPathOp;
+        } else {
+            *op = SkPathOp::kUnion_SkPathOp;
+        }
+        return true;
+    }
+
+    static constexpr SkTypeface::FactoryId FactoryId = SkSetFourByteTag('d','s','v','g');
+    static constexpr const char gHeaderString[] = "SkTestSVGTypefaceDefault01";
+    static constexpr const size_t kHeaderSize = sizeof(gHeaderString);
+
+    std::unique_ptr<SkStreamAsset> onOpenStream(int* ttcIndex) const override {
+        SkDynamicMemoryWStream wstream;
+        wstream.write(gHeaderString, kHeaderSize);
+        return wstream.detachAsStream();
+    }
+
+    static sk_sp<SkTypeface> MakeFromStream(std::unique_ptr<SkStreamAsset> stream,
+                                            const SkFontArguments&) {
+        char header[kHeaderSize];
+        if (stream->read(header, kHeaderSize) != kHeaderSize ||
+            0 != memcmp(header, gHeaderString, kHeaderSize))
+        {
+            return nullptr;
+        }
+        return TestSVGTypeface::Default();
+    }
+
+    void onGetFontDescriptor(SkFontDescriptor* desc, bool* serialize) const override {
+        TestSVGTypeface::onGetFontDescriptor(desc, serialize);
+        desc->setFactoryId(FactoryId);
+    }
+public:
+    struct Register { Register() { SkTypeface::Register(FactoryId, &MakeFromStream); } };
+};
+static DefaultTypeface::Register defaultTypefaceRegister;
 sk_sp<TestSVGTypeface> TestSVGTypeface::Default() {
     // Recommended that the first four be .notdef, .null, CR, space
     constexpr const static SkSVGTestTypefaceGlyphData glyphs[] = {
@@ -332,25 +373,47 @@
     metrics.fStrikeoutThickness = 20;
     metrics.fStrikeoutPosition  = -400;
 
-    class DefaultTypeface : public TestSVGTypeface {
-        using TestSVGTypeface::TestSVGTypeface;
-
-        bool getPathOp(SkColor color, SkPathOp* op) const override {
-            if ((SkColorGetR(color) + SkColorGetG(color) + SkColorGetB(color)) / 3 > 0x20) {
-                *op = SkPathOp::kDifference_SkPathOp;
-            } else {
-                *op = SkPathOp::kUnion_SkPathOp;
-            }
-            return true;
-        }
-    };
-    return sk_make_sp<DefaultTypeface>("Emoji",
-                                       1000,
-                                       metrics,
-                                       glyphs,
-                                       SkFontStyle::Normal());
+    return sk_sp<TestSVGTypeface>(
+        new DefaultTypeface("Emoji", 1000, metrics, glyphs, SkFontStyle::Normal()));
 }
 
+class PlanetTypeface : public TestSVGTypeface {
+    using TestSVGTypeface::TestSVGTypeface;
+
+    bool getPathOp(SkColor color, SkPathOp* op) const override {
+        *op = SkPathOp::kUnion_SkPathOp;
+        return true;
+    }
+
+    static constexpr SkTypeface::FactoryId FactoryId = SkSetFourByteTag('p','s','v','g');
+    static constexpr const char gHeaderString[] = "SkTestSVGTypefacePlanet01";
+    static constexpr const size_t kHeaderSize = sizeof(gHeaderString);
+
+    std::unique_ptr<SkStreamAsset> onOpenStream(int* ttcIndex) const override {
+        SkDynamicMemoryWStream wstream;
+        wstream.write(gHeaderString, kHeaderSize);
+        return wstream.detachAsStream();
+    }
+
+    static sk_sp<SkTypeface> MakeFromStream(std::unique_ptr<SkStreamAsset> stream,
+                                            const SkFontArguments&) {
+        char header[kHeaderSize];
+        if (stream->read(header, kHeaderSize) != kHeaderSize ||
+            0 != memcmp(header, gHeaderString, kHeaderSize))
+        {
+            return nullptr;
+        }
+        return TestSVGTypeface::Planets();
+    }
+
+    void onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal) const override {
+        TestSVGTypeface::onGetFontDescriptor(desc, isLocal);
+        desc->setFactoryId(FactoryId);
+    }
+public:
+    struct Register { Register() { SkTypeface::Register(FactoryId, &MakeFromStream); } };
+};
+static PlanetTypeface::Register planetTypefaceRegister;
 sk_sp<TestSVGTypeface> TestSVGTypeface::Planets() {
     // Recommended that the first four be .notdef, .null, CR, space
     constexpr const static SkSVGTestTypefaceGlyphData glyphs[] = {
@@ -386,19 +449,8 @@
     metrics.fStrikeoutThickness = 2;
     metrics.fStrikeoutPosition  = -80;
 
-    class PlanetTypeface : public TestSVGTypeface {
-        using TestSVGTypeface::TestSVGTypeface;
-
-        bool getPathOp(SkColor color, SkPathOp* op) const override {
-            *op = SkPathOp::kUnion_SkPathOp;
-            return true;
-        }
-    };
-    return sk_make_sp<PlanetTypeface>("Planets",
-                                      200,
-                                      metrics,
-                                      glyphs,
-                                      SkFontStyle::Normal());
+    return sk_sp<TestSVGTypeface>(
+        new PlanetTypeface("Planets", 200, metrics, glyphs, SkFontStyle::Normal()));
 }
 
 void TestSVGTypeface::exportTtxCommon(SkWStream*                out,
diff --git a/tools/fonts/TestSVGTypeface.h b/tools/fonts/TestSVGTypeface.h
index da54f6b..1245a51 100644
--- a/tools/fonts/TestSVGTypeface.h
+++ b/tools/fonts/TestSVGTypeface.h
@@ -51,11 +51,6 @@
 
 class TestSVGTypeface : public SkTypeface {
 public:
-    TestSVGTypeface(const char*                              name,
-                    int                                      upem,
-                    const SkFontMetrics&                     metrics,
-                    SkSpan<const SkSVGTestTypefaceGlyphData> data,
-                    const SkFontStyle&                       style);
     ~TestSVGTypeface() override;
     void getAdvance(SkGlyph* glyph) const;
     void getFontMetrics(SkFontMetrics* metrics) const;
@@ -88,13 +83,11 @@
     void getGlyphToUnicodeMap(SkUnichar*) const override;
     std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override;
 
-    std::unique_ptr<SkStreamAsset> onOpenStream(int* ttcIndex) const override { return nullptr; }
-
     sk_sp<SkTypeface> onMakeClone(const SkFontArguments& args) const override {
         return sk_ref_sp(this);
     }
 
-    void onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal) const override;
+    void onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal) const override = 0;
 
     void onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const override;
 
@@ -130,6 +123,11 @@
     }
 
 private:
+    TestSVGTypeface(const char*                              name,
+                    int                                      upem,
+                    const SkFontMetrics&                     metrics,
+                    SkSpan<const SkSVGTestTypefaceGlyphData> data,
+                    const SkFontStyle&                       style);
     struct Glyph {
         Glyph();
         ~Glyph();
diff --git a/tools/fonts/TestTypeface.cpp b/tools/fonts/TestTypeface.cpp
index 8f2a184..400424b 100644
--- a/tools/fonts/TestTypeface.cpp
+++ b/tools/fonts/TestTypeface.cpp
@@ -28,8 +28,45 @@
 
 #include <utility>
 
+namespace {
+
+#include "tools/fonts/test_font_monospace.inc"
+#include "tools/fonts/test_font_sans_serif.inc"
+#include "tools/fonts/test_font_serif.inc"
+
+#include "tools/fonts/test_font_index.inc"
+
+}  // namespace
+
 class SkDescriptor;
 
+const TestTypeface::List& TestTypeface::Typefaces() {
+    static List list = []() -> List {
+        TestTypeface::List list;
+        for (const auto& sub : gSubFonts) {
+            List::Family* existingFamily = nullptr;
+            for (auto& family : list.families) {
+                if (strcmp(family.name, sub.fFamilyName) == 0) {
+                    existingFamily = &family;
+                    break;
+                }
+            }
+            if (!existingFamily) {
+                existingFamily = &list.families.emplace_back();
+                existingFamily->name = sub.fFamilyName;
+            }
+
+            auto font = sk_make_sp<SkTestFont>(sub.fFont);
+            sk_sp<SkTypeface> typeface(new TestTypeface(std::move(font), sub.fStyle));
+            bool isDefault = (&sub - gSubFonts == gDefaultFontIndex);
+            existingFamily->faces.emplace_back(
+                List::Family::Face{std::move(typeface), sub.fStyleName, isDefault});
+        }
+        return list;
+    }();
+    return list;
+}
+
 SkTestFont::SkTestFont(const SkTestFontData& fontData)
         : INHERITED()
         , fCharCodes(fontData.fCharCodes)
@@ -123,12 +160,75 @@
     return info;
 }
 
-void TestTypeface::onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal) const {
+static constexpr const char gHeaderString[] = "SkTestTypeface01";
+static constexpr const size_t kHeaderSize = sizeof(gHeaderString);
+
+std::unique_ptr<SkStreamAsset> TestTypeface::onOpenStream(int* ttcIndex) const {
+    SkDynamicMemoryWStream wstream;
+    wstream.write(gHeaderString, kHeaderSize);
+
+    SkString name;
+    this->getFamilyName(&name);
+    SkFontStyle style = this->fontStyle();
+
+    wstream.writePackedUInt(name.size());
+    wstream.write(name.c_str(), name.size());
+    wstream.writeScalar(style.weight());
+    wstream.writeScalar(style.width());
+    wstream.writePackedUInt(style.slant());
+
+    *ttcIndex = 0;
+    return wstream.detachAsStream();
+}
+
+sk_sp<SkTypeface> TestTypeface::MakeFromStream(std::unique_ptr<SkStreamAsset> stream,
+                                               const SkFontArguments&) {
+    char header[kHeaderSize];
+    if (stream->read(header, kHeaderSize) != kHeaderSize ||
+        0 != memcmp(header, gHeaderString, kHeaderSize))
+    {
+        return nullptr;
+    }
+
+    size_t familyNameSize;
+    SkString familyName;
+    if (!stream->readPackedUInt(&familyNameSize)) { return nullptr; }
+    familyName.resize(familyNameSize);
+    if (!stream->read(familyName.data(), familyNameSize)) { return nullptr; }
+
+    SkScalar weight;
+    SkScalar width;
+    size_t slant;
+    if (!stream->readScalar(&weight)) { return nullptr; }
+    if (!stream->readScalar(&width)) { return nullptr; }
+    if (!stream->readPackedUInt(&slant)) { return nullptr; }
+    SkFontStyle style(weight, width, (SkFontStyle::Slant)slant);
+
+    auto&& list = TestTypeface::Typefaces();
+    for (auto&& family : list.families) {
+        if (familyName.equals(family.name)) {
+            for (auto&& face : family.faces) {
+                if (face.typeface->fontStyle() == style) {
+                    return face.typeface;
+                }
+            }
+        }
+    }
+    return nullptr;
+}
+
+void TestTypeface::onGetFontDescriptor(SkFontDescriptor* desc, bool* serialize) const {
     desc->setFamilyName(fTestFont->fName);
     desc->setStyle(this->fontStyle());
-    *isLocal = false;
+    desc->setFactoryId(FactoryId);
+    *serialize = true;
 }
 
+TestTypeface::Register::Register() {
+    SkTypeface::Register(TestTypeface::FactoryId, &TestTypeface::MakeFromStream);
+}
+static TestTypeface::Register registerer;
+
 void TestTypeface::onCharsToGlyphs(const SkUnichar* uni, int count, SkGlyphID glyphs[]) const {
     for (int i = 0; i < count; ++i) {
         glyphs[i] = fTestFont->glyphForUnichar(uni[i]);
diff --git a/tools/fonts/TestTypeface.h b/tools/fonts/TestTypeface.h
index e14d908..de4077c 100644
--- a/tools/fonts/TestTypeface.h
+++ b/tools/fonts/TestTypeface.h
@@ -64,11 +64,25 @@
 
 class TestTypeface : public SkTypeface {
 public:
-    TestTypeface(sk_sp<SkTestFont>, const SkFontStyle& style);
+    struct List {
+        struct Family {
+            struct Face {
+                sk_sp<SkTypeface> typeface;
+                const char* name;
+                bool isDefault;
+            };
+            std::vector<Face> faces;
+            const char* name;
+        };
+        std::vector<Family> families;
+    };
+    static const List& Typefaces();
+
     void getAdvance(SkGlyph* glyph);
     void getFontMetrics(SkFontMetrics* metrics);
     SkPath getPath(SkGlyphID glyph);
 
+    struct Register { Register(); };
 protected:
     std::unique_ptr<SkScalerContext> onCreateScalerContext(const SkScalerContextEffects&,
                                                            const SkDescriptor* desc) const override;
@@ -76,13 +90,13 @@
     void getGlyphToUnicodeMap(SkUnichar* glyphToUnicode) const override;
     std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override;
 
-    std::unique_ptr<SkStreamAsset> onOpenStream(int* ttcIndex) const override { return nullptr; }
+    std::unique_ptr<SkStreamAsset> onOpenStream(int* ttcIndex) const override;
 
     sk_sp<SkTypeface> onMakeClone(const SkFontArguments& args) const override {
         return sk_ref_sp(this);
     }
 
-    void onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal) const override;
+    void onGetFontDescriptor(SkFontDescriptor* desc, bool* serialize) const override;
 
     void onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const override;
 
@@ -118,6 +132,9 @@
     }
 
 private:
+    static constexpr SkTypeface::FactoryId FactoryId = SkSetFourByteTag('t','e','s','t');
+    static sk_sp<SkTypeface> MakeFromStream(std::unique_ptr<SkStreamAsset>, const SkFontArguments&);
+    TestTypeface(sk_sp<SkTestFont>, const SkFontStyle& style);
     sk_sp<SkTestFont> fTestFont;
     friend class SkTestScalerContext;
 };