Split out coretext fontmgr factory.

There can be only one SkFontMgr::Factory, so each should go in its own
translation unit. In addition, the name SkFontHost_mac is now rather out
of date and should now be SkFontMgr_mac_ct. Since the additional burden
of additional build changes after the first is minimal, also split out
SkTypeface_mac_ct and SkScalerContext_mac_ct. The original
SkFontHost_mac.cpp is kept as a shell which #includes the cpp files
which are replacing it. Once references to it are removed from all
builds it can be removed.

This is intended to be a reorganization without much code change. Most
changes are simple renaming of functions which are now shared between
translation units. However, there are a few behavior changes here.
  * Drop SkTypefaceCache global for SkTypeface_Mac 'local' global.
  * SkCTFontCTWidthForCSSWidth returns CGFloat instead of 'int'.
  * SkFontMgr_New_CoreText takes a CTFontCollectionRef.

Change-Id: I897f69f2f5ea06819f5daa964c09cdd36490af85
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/294456
Reviewed-by: Mike Klein <mtklein@google.com>
Commit-Queue: Ben Wagner <bungeman@google.com>
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/294704
diff --git a/BUILD.gn b/BUILD.gn
index 8c09b76..9e3b3b7 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -378,7 +378,16 @@
 optional("fontmgr_mac") {
   enabled = skia_use_fonthost_mac
 
-  sources = [ "src/ports/SkFontHost_mac.cpp" ]
+  public = [ "include/ports/SkFontMgr_mac_ct.h" ]
+  sources = [
+    #"src/ports/SkFontHost_mac.cpp",
+    "src/ports/SkFontMgr_mac_ct.cpp",
+    "src/ports/SkFontMgr_mac_ct_factory.cpp",
+    "src/ports/SkScalerContext_mac_ct.cpp",
+    "src/ports/SkScalerContext_mac_ct.h",
+    "src/ports/SkTypeface_mac_ct.cpp",
+    "src/ports/SkTypeface_mac_ct.h",
+  ]
 
   if (is_mac) {
     libs = [
diff --git a/gn/utils.gni b/gn/utils.gni
index 1b968fc..9b181dc 100644
--- a/gn/utils.gni
+++ b/gn/utils.gni
@@ -81,6 +81,10 @@
   "$_src/utils/SkWhitelistTypefaces.cpp",
 
   #mac
+  "$_src/utils/mac/SkCGBase.h",
+  "$_src/utils/mac/SkCGGeometry.h",
+  "$_src/utils/mac/SkCTFontSmoothBehavior.cpp",
+  "$_src/utils/mac/SkCTFontSmoothBehavior.h",
   "$_src/utils/mac/SkCreateCGImageRef.cpp",
   "$_src/utils/mac/SkUniqueCFRef.h",
 
diff --git a/include/ports/SkFontMgr_mac_ct.h b/include/ports/SkFontMgr_mac_ct.h
new file mode 100644
index 0000000..45cba65
--- /dev/null
+++ b/include/ports/SkFontMgr_mac_ct.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkFontMgr_mac_ct_DEFINED
+#define SkFontMgr_mac_ct_DEFINED
+
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkTypes.h"
+
+#ifdef SK_BUILD_FOR_MAC
+#import <ApplicationServices/ApplicationServices.h>
+#endif
+
+#ifdef SK_BUILD_FOR_IOS
+#include <CoreText/CoreText.h>
+#endif
+
+class SkFontMgr;
+
+/** Create a font manager for CoreText. If the collection is nullptr the system default will be used. */
+SK_API extern sk_sp<SkFontMgr> SkFontMgr_New_CoreText(CTFontCollectionRef);
+
+#endif // SkFontMgr_mac_ct_DEFINED
diff --git a/public.bzl b/public.bzl
index 0db0487..2d5b201 100644
--- a/public.bzl
+++ b/public.bzl
@@ -347,6 +347,7 @@
         "src/ports/*mozalloc*",
         "src/ports/*nacl*",
         "src/ports/*win*",
+        "src/ports/SkFontHost_mac.cpp",
         "src/ports/SkFontMgr_custom.cpp",
         "src/ports/SkFontMgr_custom_directory.cpp",
         "src/ports/SkFontMgr_custom_embedded.cpp",
diff --git a/src/ports/SkFontHost_mac.cpp b/src/ports/SkFontHost_mac.cpp
index 9e29b5c..b2f2b0b 100644
--- a/src/ports/SkFontHost_mac.cpp
+++ b/src/ports/SkFontHost_mac.cpp
@@ -1,3226 +1,13 @@
 /*
- * Copyright 2006 The Android Open Source Project
+ * Copyright 2020 Google Inc.
  *
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
 
-#include "include/core/SkTypes.h"
-#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
-
-#ifdef SK_BUILD_FOR_MAC
-#import <ApplicationServices/ApplicationServices.h>
-#endif
-
-#ifdef SK_BUILD_FOR_IOS
-#include <CoreText/CoreText.h>
-#include <CoreText/CTFontManager.h>
-#include <CoreGraphics/CoreGraphics.h>
-#include <CoreFoundation/CoreFoundation.h>
-#endif
-
-#include "include/core/SkFontMetrics.h"
-#include "include/core/SkFontMgr.h"
-#include "include/core/SkPaint.h"
-#include "include/core/SkPath.h"
-#include "include/core/SkStream.h"
-#include "include/core/SkString.h"
-#include "include/ports/SkTypeface_mac.h"
-#include "include/private/SkColorData.h"
-#include "include/private/SkFloatingPoint.h"
-#include "include/private/SkMutex.h"
-#include "include/private/SkOnce.h"
-#include "include/private/SkTemplates.h"
-#include "include/private/SkTo.h"
-#include "include/utils/mac/SkCGUtils.h"
-#include "src/core/SkAdvancedTypefaceMetrics.h"
-#include "src/core/SkAutoMalloc.h"
-#include "src/core/SkDescriptor.h"
-#include "src/core/SkEndian.h"
-#include "src/core/SkFontDescriptor.h"
-#include "src/core/SkGlyph.h"
-#include "src/core/SkMaskGamma.h"
-#include "src/core/SkMathPriv.h"
-#include "src/core/SkTypefaceCache.h"
-#include "src/core/SkUtils.h"
-#include "src/sfnt/SkOTTable_OS_2.h"
-#include "src/sfnt/SkOTUtils.h"
-#include "src/sfnt/SkSFNTHeader.h"
-#include "src/utils/SkUTF.h"
-#include "src/utils/mac/SkUniqueCFRef.h"
-
-#include <dlfcn.h>
-
-#include <utility>
-
-// Set to make glyph bounding boxes visible.
-#define SK_SHOW_TEXT_BLIT_COVERAGE 0
-
-CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face) {
-    return face ? (CTFontRef)face->internal_private_getCTFontRef() : nullptr;
-}
-
-class SkScalerContext_Mac;
-
-static SkUniqueCFRef<CFStringRef> make_CFString(const char s[]) {
-    return SkUniqueCFRef<CFStringRef>(CFStringCreateWithCString(nullptr, s, kCFStringEncodingUTF8));
-}
-
-// inline versions of these rect helpers
-
-static bool CGRectIsEmpty_inline(const CGRect& rect) {
-    return rect.size.width <= 0 || rect.size.height <= 0;
-}
-
-static CGFloat CGRectGetMinX_inline(const CGRect& rect) {
-    return rect.origin.x;
-}
-
-static CGFloat CGRectGetMaxX_inline(const CGRect& rect) {
-    return rect.origin.x + rect.size.width;
-}
-
-static CGFloat CGRectGetMinY_inline(const CGRect& rect) {
-    return rect.origin.y;
-}
-
-static CGFloat CGRectGetMaxY_inline(const CGRect& rect) {
-    return rect.origin.y + rect.size.height;
-}
-
-static CGFloat CGRectGetWidth_inline(const CGRect& rect) {
-    return rect.size.width;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-static void sk_memset_rect32(uint32_t* ptr, uint32_t value,
-                             int width, int height, size_t rowBytes) {
-    SkASSERT(width);
-    SkASSERT(width * sizeof(uint32_t) <= rowBytes);
-
-    if (width >= 32) {
-        while (height) {
-            sk_memset32(ptr, value, width);
-            ptr = (uint32_t*)((char*)ptr + rowBytes);
-            height -= 1;
-        }
-        return;
-    }
-
-    rowBytes -= width * sizeof(uint32_t);
-
-    if (width >= 8) {
-        while (height) {
-            int w = width;
-            do {
-                *ptr++ = value; *ptr++ = value;
-                *ptr++ = value; *ptr++ = value;
-                *ptr++ = value; *ptr++ = value;
-                *ptr++ = value; *ptr++ = value;
-                w -= 8;
-            } while (w >= 8);
-            while (--w >= 0) {
-                *ptr++ = value;
-            }
-            ptr = (uint32_t*)((char*)ptr + rowBytes);
-            height -= 1;
-        }
-    } else {
-        while (height) {
-            int w = width;
-            do {
-                *ptr++ = value;
-            } while (--w > 0);
-            ptr = (uint32_t*)((char*)ptr + rowBytes);
-            height -= 1;
-        }
-    }
-}
-
-typedef uint32_t CGRGBPixel;
-
-static unsigned CGRGBPixel_getAlpha(CGRGBPixel pixel) {
-    return pixel & 0xFF;
-}
-
-static CGFloat ScalarToCG(SkScalar scalar) {
-    if (sizeof(CGFloat) == sizeof(float)) {
-        return SkScalarToFloat(scalar);
-    } else {
-        SkASSERT(sizeof(CGFloat) == sizeof(double));
-        return (CGFloat) SkScalarToDouble(scalar);
-    }
-}
-
-static SkScalar CGToScalar(CGFloat cgFloat) {
-    if (sizeof(CGFloat) == sizeof(float)) {
-        return SkFloatToScalar(cgFloat);
-    } else {
-        SkASSERT(sizeof(CGFloat) == sizeof(double));
-        return SkDoubleToScalar(cgFloat);
-    }
-}
-
-static float CGToFloat(CGFloat cgFloat) {
-    if (sizeof(CGFloat) == sizeof(float)) {
-        return cgFloat;
-    } else {
-        SkASSERT(sizeof(CGFloat) == sizeof(double));
-        return static_cast<float>(cgFloat);
-    }
-}
-
-static CGAffineTransform MatrixToCGAffineTransform(const SkMatrix& matrix) {
-    return CGAffineTransformMake( ScalarToCG(matrix[SkMatrix::kMScaleX]),
-                                 -ScalarToCG(matrix[SkMatrix::kMSkewY] ),
-                                 -ScalarToCG(matrix[SkMatrix::kMSkewX] ),
-                                  ScalarToCG(matrix[SkMatrix::kMScaleY]),
-                                  ScalarToCG(matrix[SkMatrix::kMTransX]),
-                                  ScalarToCG(matrix[SkMatrix::kMTransY]));
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-#define BITMAP_INFO_RGB (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host)
-
-/** Drawn in FontForge, reduced with fonttools ttx, converted by xxd -i,
- *  this TrueType font contains a glyph of the spider.
- *
- *  To re-forge the original bytes of the TrueType font file,
- *  remove all ',|( +0x)' from this definition,
- *  copy the data to the clipboard,
- *  run 'pbpaste | xxd -p -r - spider.ttf'.
- */
-static constexpr const uint8_t kSpiderSymbol_ttf[] = {
-    0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x80, 0x00, 0x03, 0x00, 0x40,
-    0x47, 0x44, 0x45, 0x46, 0x00, 0x14, 0x00, 0x14, 0x00, 0x00, 0x07, 0xa8,
-    0x00, 0x00, 0x00, 0x18, 0x4f, 0x53, 0x2f, 0x32, 0x8a, 0xf4, 0xfb, 0xdb,
-    0x00, 0x00, 0x01, 0x48, 0x00, 0x00, 0x00, 0x60, 0x63, 0x6d, 0x61, 0x70,
-    0xe0, 0x7f, 0x10, 0x7e, 0x00, 0x00, 0x01, 0xb8, 0x00, 0x00, 0x00, 0x54,
-    0x67, 0x61, 0x73, 0x70, 0xff, 0xff, 0x00, 0x03, 0x00, 0x00, 0x07, 0xa0,
-    0x00, 0x00, 0x00, 0x08, 0x67, 0x6c, 0x79, 0x66, 0x97, 0x0b, 0x6a, 0xf6,
-    0x00, 0x00, 0x02, 0x18, 0x00, 0x00, 0x03, 0x40, 0x68, 0x65, 0x61, 0x64,
-    0x0f, 0xa2, 0x24, 0x1a, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x36,
-    0x68, 0x68, 0x65, 0x61, 0x0e, 0xd3, 0x07, 0x3f, 0x00, 0x00, 0x01, 0x04,
-    0x00, 0x00, 0x00, 0x24, 0x68, 0x6d, 0x74, 0x78, 0x10, 0x03, 0x00, 0x44,
-    0x00, 0x00, 0x01, 0xa8, 0x00, 0x00, 0x00, 0x0e, 0x6c, 0x6f, 0x63, 0x61,
-    0x01, 0xb4, 0x00, 0x28, 0x00, 0x00, 0x02, 0x0c, 0x00, 0x00, 0x00, 0x0a,
-    0x6d, 0x61, 0x78, 0x70, 0x00, 0x4a, 0x01, 0x4d, 0x00, 0x00, 0x01, 0x28,
-    0x00, 0x00, 0x00, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0xc3, 0xe5, 0x39, 0xd4,
-    0x00, 0x00, 0x05, 0x58, 0x00, 0x00, 0x02, 0x28, 0x70, 0x6f, 0x73, 0x74,
-    0xff, 0x03, 0x00, 0x67, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, 0x20,
-    0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0b, 0x0f, 0x08, 0x1d,
-    0x5f, 0x0f, 0x3c, 0xf5, 0x00, 0x0b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0xd1, 0x97, 0xa8, 0x5a, 0x00, 0x00, 0x00, 0x00, 0xd6, 0xe8, 0x32, 0x33,
-    0x00, 0x03, 0xff, 0x3b, 0x08, 0x00, 0x05, 0x55, 0x00, 0x00, 0x00, 0x08,
-    0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
-    0x05, 0x55, 0xff, 0x3b, 0x01, 0x79, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00,
-    0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00,
-    0x00, 0x04, 0x01, 0x1c, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
-    0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x2e,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x08, 0x00, 0x01, 0x90, 0x00, 0x05,
-    0x00, 0x00, 0x05, 0x33, 0x05, 0x99, 0x00, 0x00, 0x01, 0x1e, 0x05, 0x33,
-    0x05, 0x99, 0x00, 0x00, 0x03, 0xd7, 0x00, 0x66, 0x02, 0x12, 0x00, 0x00,
-    0x05, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x73, 0x6b, 0x69, 0x61, 0x00, 0xc0, 0x00, 0x00, 0xf0, 0x21,
-    0x06, 0x66, 0xfe, 0x66, 0x01, 0x79, 0x05, 0x55, 0x00, 0xc5, 0x80, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x20, 0x00, 0x01, 0x08, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00,
-    0x08, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
-    0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x04, 0x00, 0x48,
-    0x00, 0x00, 0x00, 0x0e, 0x00, 0x08, 0x00, 0x02, 0x00, 0x06, 0x00, 0x00,
-    0x00, 0x09, 0x00, 0x0d, 0x00, 0x1d, 0x00, 0x21, 0xf0, 0x21, 0xff, 0xff,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0d, 0x00, 0x1d, 0x00, 0x21,
-    0xf0, 0x21, 0xff, 0xff, 0x00, 0x01, 0xff, 0xf9, 0xff, 0xf5, 0xff, 0xe4,
-    0xff, 0xe2, 0x0f, 0xe2, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14,
-    0x00, 0x14, 0x00, 0x14, 0x01, 0xa0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x44,
-    0x00, 0x00, 0x02, 0x64, 0x05, 0x55, 0x00, 0x03, 0x00, 0x07, 0x00, 0x00,
-    0x33, 0x11, 0x21, 0x11, 0x25, 0x21, 0x11, 0x21, 0x44, 0x02, 0x20, 0xfe,
-    0x24, 0x01, 0x98, 0xfe, 0x68, 0x05, 0x55, 0xfa, 0xab, 0x44, 0x04, 0xcd,
-    0x00, 0x04, 0x00, 0x03, 0xff, 0x3b, 0x08, 0x00, 0x05, 0x4c, 0x00, 0x15,
-    0x00, 0x1d, 0x00, 0x25, 0x01, 0x1b, 0x00, 0x00, 0x01, 0x36, 0x37, 0x36,
-    0x27, 0x26, 0x07, 0x06, 0x06, 0x23, 0x22, 0x27, 0x26, 0x27, 0x26, 0x07,
-    0x06, 0x17, 0x16, 0x17, 0x16, 0x32, 0x37, 0x32, 0x35, 0x34, 0x23, 0x22,
-    0x15, 0x14, 0x27, 0x32, 0x35, 0x34, 0x23, 0x22, 0x15, 0x14, 0x03, 0x32,
-    0x17, 0x30, 0x17, 0x31, 0x36, 0x37, 0x36, 0x37, 0x36, 0x37, 0x36, 0x33,
-    0x32, 0x33, 0x16, 0x33, 0x32, 0x17, 0x16, 0x07, 0x06, 0x23, 0x22, 0x27,
-    0x26, 0x27, 0x26, 0x23, 0x22, 0x07, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06,
-    0x1f, 0x02, 0x37, 0x36, 0x37, 0x36, 0x33, 0x32, 0x17, 0x17, 0x16, 0x33,
-    0x16, 0x17, 0x16, 0x07, 0x06, 0x23, 0x22, 0x27, 0x27, 0x26, 0x23, 0x22,
-    0x07, 0x06, 0x07, 0x06, 0x17, 0x16, 0x17, 0x16, 0x33, 0x32, 0x33, 0x32,
-    0x37, 0x36, 0x37, 0x36, 0x17, 0x16, 0x1f, 0x02, 0x16, 0x17, 0x16, 0x15,
-    0x14, 0x23, 0x22, 0x27, 0x27, 0x26, 0x27, 0x27, 0x26, 0x27, 0x26, 0x07,
-    0x06, 0x07, 0x06, 0x17, 0x16, 0x17, 0x16, 0x15, 0x14, 0x07, 0x06, 0x07,
-    0x06, 0x23, 0x22, 0x27, 0x26, 0x07, 0x06, 0x07, 0x06, 0x15, 0x14, 0x17,
-    0x16, 0x17, 0x16, 0x15, 0x14, 0x07, 0x06, 0x23, 0x22, 0x27, 0x26, 0x27,
-    0x26, 0x35, 0x34, 0x37, 0x36, 0x37, 0x36, 0x37, 0x34, 0x27, 0x26, 0x07,
-    0x06, 0x07, 0x06, 0x0f, 0x02, 0x06, 0x23, 0x22, 0x27, 0x26, 0x35, 0x34,
-    0x37, 0x37, 0x36, 0x37, 0x36, 0x37, 0x36, 0x37, 0x36, 0x27, 0x26, 0x27,
-    0x26, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x07, 0x06, 0x23, 0x22,
-    0x27, 0x26, 0x35, 0x34, 0x37, 0x36, 0x37, 0x37, 0x36, 0x37, 0x37, 0x36,
-    0x37, 0x36, 0x37, 0x36, 0x35, 0x34, 0x27, 0x26, 0x27, 0x26, 0x27, 0x26,
-    0x23, 0x22, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x27, 0x26, 0x27, 0x26,
-    0x27, 0x26, 0x35, 0x34, 0x37, 0x36, 0x37, 0x36, 0x37, 0x36, 0x33, 0x32,
-    0x17, 0x16, 0x33, 0x32, 0x37, 0x36, 0x35, 0x34, 0x37, 0x36, 0x37, 0x36,
-    0x33, 0x04, 0xf5, 0x23, 0x13, 0x11, 0x14, 0x16, 0x1d, 0x1b, 0x4c, 0x1f,
-    0x0e, 0x2d, 0x23, 0x14, 0x2c, 0x13, 0x18, 0x25, 0x2c, 0x10, 0x3c, 0x71,
-    0x1d, 0x5c, 0x5c, 0x3f, 0xae, 0x5c, 0x5c, 0x3f, 0x6a, 0x27, 0x31, 0x5b,
-    0x09, 0x27, 0x36, 0x03, 0x0a, 0x26, 0x35, 0x2e, 0x09, 0x08, 0xc6, 0x13,
-    0x81, 0x17, 0x20, 0x18, 0x21, 0x1e, 0x04, 0x04, 0x15, 0x5c, 0x22, 0x26,
-    0x48, 0x56, 0x3b, 0x10, 0x21, 0x01, 0x0c, 0x06, 0x06, 0x0f, 0x31, 0x44,
-    0x3c, 0x52, 0x4a, 0x1d, 0x11, 0x3f, 0xb4, 0x71, 0x01, 0x26, 0x06, 0x0d,
-    0x15, 0x1a, 0x2a, 0x13, 0x53, 0xaa, 0x42, 0x1d, 0x0a, 0x33, 0x20, 0x21,
-    0x2b, 0x01, 0x02, 0x3e, 0x21, 0x09, 0x02, 0x02, 0x0f, 0x2d, 0x4b, 0x0a,
-    0x22, 0x15, 0x20, 0x1f, 0x72, 0x8b, 0x2d, 0x2f, 0x1d, 0x1f, 0x0e, 0x25,
-    0x3f, 0x4d, 0x1b, 0x63, 0x2a, 0x2c, 0x14, 0x22, 0x18, 0x1c, 0x0f, 0x08,
-    0x2a, 0x08, 0x08, 0x0d, 0x3b, 0x4c, 0x52, 0x74, 0x27, 0x71, 0x2e, 0x01,
-    0x0c, 0x10, 0x15, 0x0d, 0x06, 0x0d, 0x05, 0x01, 0x06, 0x2c, 0x28, 0x14,
-    0x1b, 0x05, 0x04, 0x10, 0x06, 0x12, 0x08, 0x0a, 0x16, 0x27, 0x03, 0x0d,
-    0x30, 0x4c, 0x4c, 0x4b, 0x1f, 0x0b, 0x22, 0x26, 0x0d, 0x15, 0x0d, 0x2d,
-    0x68, 0x34, 0x14, 0x3c, 0x25, 0x12, 0x04, 0x10, 0x18, 0x0b, 0x09, 0x30,
-    0x2b, 0x44, 0x66, 0x14, 0x47, 0x47, 0x59, 0x73, 0x25, 0x05, 0x03, 0x1f,
-    0x01, 0x08, 0x3f, 0x48, 0x4b, 0x4b, 0x76, 0x2f, 0x49, 0x2d, 0x22, 0x24,
-    0x0c, 0x15, 0x08, 0x0e, 0x33, 0x03, 0x44, 0x4c, 0x10, 0x46, 0x13, 0x1f,
-    0x27, 0x1b, 0x1d, 0x13, 0x02, 0x24, 0x08, 0x02, 0x42, 0x0e, 0x4d, 0x3c,
-    0x19, 0x1b, 0x40, 0x2b, 0x2b, 0x1e, 0x16, 0x11, 0x04, 0x1f, 0x11, 0x04,
-    0x18, 0x11, 0x35, 0x01, 0xa3, 0x13, 0x24, 0x1f, 0x0b, 0x0c, 0x19, 0x19,
-    0x18, 0x13, 0x0f, 0x0c, 0x1a, 0x18, 0x1f, 0x19, 0x1e, 0x07, 0x1a, 0xc3,
-    0x54, 0x51, 0x54, 0x51, 0x04, 0x53, 0x51, 0x54, 0x50, 0x02, 0x48, 0x1a,
-    0x31, 0x18, 0x55, 0x74, 0x04, 0x0e, 0x09, 0x0d, 0x06, 0x10, 0x16, 0x1b,
-    0x24, 0x01, 0x04, 0x0b, 0x04, 0x10, 0x3f, 0x0a, 0x41, 0x02, 0x41, 0x20,
-    0x06, 0x12, 0x16, 0x21, 0x17, 0x2a, 0x1e, 0x15, 0x40, 0x27, 0x11, 0x0e,
-    0x1e, 0x11, 0x15, 0x1f, 0x43, 0x13, 0x1a, 0x10, 0x15, 0x1b, 0x04, 0x09,
-    0x4d, 0x2a, 0x0f, 0x19, 0x0a, 0x0a, 0x03, 0x05, 0x15, 0x3c, 0x64, 0x21,
-    0x4b, 0x2e, 0x21, 0x28, 0x13, 0x47, 0x44, 0x19, 0x3f, 0x11, 0x18, 0x0b,
-    0x0a, 0x07, 0x18, 0x0d, 0x07, 0x24, 0x2c, 0x2b, 0x21, 0x32, 0x10, 0x48,
-    0x2a, 0x2d, 0x1e, 0x1a, 0x01, 0x0c, 0x43, 0x59, 0x28, 0x4e, 0x1c, 0x0d,
-    0x5d, 0x24, 0x14, 0x0a, 0x05, 0x1f, 0x24, 0x32, 0x46, 0x3e, 0x5f, 0x3e,
-    0x44, 0x1a, 0x30, 0x15, 0x0d, 0x07, 0x18, 0x2b, 0x03, 0x0d, 0x1a, 0x28,
-    0x28, 0x57, 0xb2, 0x29, 0x27, 0x40, 0x2c, 0x23, 0x16, 0x63, 0x58, 0x1a,
-    0x0a, 0x18, 0x11, 0x23, 0x08, 0x1b, 0x29, 0x05, 0x04, 0x0b, 0x15, 0x0d,
-    0x14, 0x0b, 0x2a, 0x29, 0x5a, 0x62, 0x01, 0x19, 0x1e, 0x05, 0x05, 0x26,
-    0x42, 0x42, 0x2a, 0x2a, 0x3f, 0x0d, 0x0f, 0x09, 0x05, 0x07, 0x01, 0x0b,
-    0x25, 0x3e, 0x0d, 0x17, 0x11, 0x01, 0x03, 0x0d, 0x13, 0x20, 0x19, 0x11,
-    0x03, 0x02, 0x01, 0x04, 0x11, 0x04, 0x05, 0x1b, 0x3d, 0x10, 0x29, 0x20,
-    0x04, 0x04, 0x0a, 0x07, 0x04, 0x1f, 0x15, 0x20, 0x3e, 0x0f, 0x2a, 0x1e,
-    0x00, 0x00, 0x00, 0x1b, 0x01, 0x4a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x01, 0x00, 0x0c, 0x00, 0x1b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x02, 0x00, 0x07, 0x00, 0x27, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x03, 0x00, 0x0c, 0x00, 0x1b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x04, 0x00, 0x0c, 0x00, 0x1b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x05, 0x00, 0x02, 0x00, 0x2e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x06, 0x00, 0x0c, 0x00, 0x1b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x0d, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x0e, 0x00, 0x1a, 0x00, 0x30, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
-    0x00, 0x00, 0x00, 0x36, 0x00, 0x4a, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
-    0x00, 0x01, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
-    0x00, 0x02, 0x00, 0x0e, 0x00, 0x98, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
-    0x00, 0x03, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
-    0x00, 0x04, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
-    0x00, 0x05, 0x00, 0x04, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
-    0x00, 0x06, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
-    0x00, 0x0d, 0x00, 0x36, 0x00, 0x4a, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
-    0x00, 0x0e, 0x00, 0x34, 0x00, 0xaa, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
-    0x00, 0x00, 0x00, 0x36, 0x00, 0x4a, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
-    0x00, 0x01, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
-    0x00, 0x02, 0x00, 0x0e, 0x00, 0x98, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
-    0x00, 0x03, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
-    0x00, 0x04, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
-    0x00, 0x05, 0x00, 0x04, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
-    0x00, 0x06, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
-    0x00, 0x0d, 0x00, 0x36, 0x00, 0x4a, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
-    0x00, 0x0e, 0x00, 0x34, 0x00, 0xaa, 0x43, 0x6f, 0x70, 0x79, 0x72, 0x69,
-    0x67, 0x68, 0x74, 0x20, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x31, 0x35,
-    0x2c, 0x20, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x53, 0x70, 0x69,
-    0x64, 0x65, 0x72, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x52, 0x65, 0x67,
-    0x75, 0x6c, 0x61, 0x72, 0x56, 0x31, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
-    0x2f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, 0x2e, 0x73, 0x69, 0x6c,
-    0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x4f, 0x46, 0x4c, 0x00, 0x43, 0x00, 0x6f,
-    0x00, 0x70, 0x00, 0x79, 0x00, 0x72, 0x00, 0x69, 0x00, 0x67, 0x00, 0x68,
-    0x00, 0x74, 0x00, 0x20, 0x00, 0x28, 0x00, 0x63, 0x00, 0x29, 0x00, 0x20,
-    0x00, 0x32, 0x00, 0x30, 0x00, 0x31, 0x00, 0x35, 0x00, 0x2c, 0x00, 0x20,
-    0x00, 0x47, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x67, 0x00, 0x6c, 0x00, 0x65,
-    0x00, 0x2e, 0x00, 0x53, 0x00, 0x70, 0x00, 0x69, 0x00, 0x64, 0x00, 0x65,
-    0x00, 0x72, 0x00, 0x53, 0x00, 0x79, 0x00, 0x6d, 0x00, 0x62, 0x00, 0x6f,
-    0x00, 0x6c, 0x00, 0x52, 0x00, 0x65, 0x00, 0x67, 0x00, 0x75, 0x00, 0x6c,
-    0x00, 0x61, 0x00, 0x72, 0x00, 0x56, 0x00, 0x31, 0x00, 0x68, 0x00, 0x74,
-    0x00, 0x74, 0x00, 0x70, 0x00, 0x3a, 0x00, 0x2f, 0x00, 0x2f, 0x00, 0x73,
-    0x00, 0x63, 0x00, 0x72, 0x00, 0x69, 0x00, 0x70, 0x00, 0x74, 0x00, 0x73,
-    0x00, 0x2e, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x2e, 0x00, 0x6f,
-    0x00, 0x72, 0x00, 0x67, 0x00, 0x2f, 0x00, 0x4f, 0x00, 0x46, 0x00, 0x4c,
-    0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x66,
-    0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
-    0xff, 0xff, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x0c, 0x00, 0x14, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
-    0x00, 0x02, 0x00, 0x00
-};
-
-enum class SmoothBehavior {
-    none, // SmoothFonts produces no effect.
-    some, // SmoothFonts produces some effect, but not subpixel coverage.
-    subpixel, // SmoothFonts produces some effect and provides subpixel coverage.
-};
-
-/**
- * There does not appear to be a publicly accessable API for determining if lcd
- * font smoothing will be applied if we request it. The main issue is that if
- * smoothing is applied a gamma of 2.0 will be used, if not a gamma of 1.0.
- */
-static SmoothBehavior smooth_behavior() {
-    static SmoothBehavior gSmoothBehavior = []{
-        uint32_t noSmoothBitmap[16][16] = {};
-        uint32_t smoothBitmap[16][16] = {};
-
-        SkUniqueCFRef<CGColorSpaceRef> colorspace(CGColorSpaceCreateDeviceRGB());
-        SkUniqueCFRef<CGContextRef> noSmoothContext(
-                CGBitmapContextCreate(&noSmoothBitmap, 16, 16, 8, 16*4,
-                                      colorspace.get(), BITMAP_INFO_RGB));
-        SkUniqueCFRef<CGContextRef> smoothContext(
-                CGBitmapContextCreate(&smoothBitmap, 16, 16, 8, 16*4,
-                                      colorspace.get(), BITMAP_INFO_RGB));
-
-        SkUniqueCFRef<CFDataRef> data(CFDataCreateWithBytesNoCopy(
-                kCFAllocatorDefault, kSpiderSymbol_ttf, SK_ARRAY_COUNT(kSpiderSymbol_ttf),
-                kCFAllocatorNull));
-        SkUniqueCFRef<CTFontDescriptorRef> desc(
-                CTFontManagerCreateFontDescriptorFromData(data.get()));
-        SkUniqueCFRef<CTFontRef> ctFont(CTFontCreateWithFontDescriptor(desc.get(), 16, nullptr));
-        SkASSERT(ctFont);
-
-        CGContextSetShouldSmoothFonts(noSmoothContext.get(), false);
-        CGContextSetShouldAntialias(noSmoothContext.get(), true);
-        CGContextSetTextDrawingMode(noSmoothContext.get(), kCGTextFill);
-        CGContextSetGrayFillColor(noSmoothContext.get(), 1, 1);
-
-        CGContextSetShouldSmoothFonts(smoothContext.get(), true);
-        CGContextSetShouldAntialias(smoothContext.get(), true);
-        CGContextSetTextDrawingMode(smoothContext.get(), kCGTextFill);
-        CGContextSetGrayFillColor(smoothContext.get(), 1, 1);
-
-        CGPoint point = CGPointMake(0, 3);
-        CGGlyph spiderGlyph = 3;
-        CTFontDrawGlyphs(ctFont.get(), &spiderGlyph, &point, 1, noSmoothContext.get());
-        CTFontDrawGlyphs(ctFont.get(), &spiderGlyph, &point, 1, smoothContext.get());
-
-        // For debugging.
-        //SkUniqueCFRef<CGImageRef> image(CGBitmapContextCreateImage(noSmoothContext()));
-        //SkUniqueCFRef<CGImageRef> image(CGBitmapContextCreateImage(smoothContext()));
-
-        SmoothBehavior smoothBehavior = SmoothBehavior::none;
-        for (int x = 0; x < 16; ++x) {
-            for (int y = 0; y < 16; ++y) {
-                uint32_t smoothPixel = smoothBitmap[x][y];
-                uint32_t r = (smoothPixel >> 16) & 0xFF;
-                uint32_t g = (smoothPixel >>  8) & 0xFF;
-                uint32_t b = (smoothPixel >>  0) & 0xFF;
-                if (r != g || r != b) {
-                    return SmoothBehavior::subpixel;
-                }
-                if (noSmoothBitmap[x][y] != smoothPixel) {
-                    smoothBehavior = SmoothBehavior::some;
-                }
-            }
-        }
-        return smoothBehavior;
-    }();
-    return gSmoothBehavior;
-}
-
-class Offscreen {
-public:
-    Offscreen()
-        : fRGBSpace(nullptr)
-        , fCG(nullptr)
-        , fDoAA(false)
-        , fDoLCD(false)
-    {
-        fSize.set(0, 0);
-    }
-
-    CGRGBPixel* getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
-                      CGGlyph glyphID, size_t* rowBytesPtr, bool generateA8FromLCD);
-
-private:
-    enum {
-        kSize = 32 * 32 * sizeof(CGRGBPixel)
-    };
-    SkAutoSMalloc<kSize> fImageStorage;
-    SkUniqueCFRef<CGColorSpaceRef> fRGBSpace;
-
-    // cached state
-    SkUniqueCFRef<CGContextRef> fCG;
-    SkISize fSize;
-    bool fDoAA;
-    bool fDoLCD;
-
-    static int RoundSize(int dimension) {
-        return SkNextPow2(dimension);
-    }
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-static bool find_dict_CGFloat(CFDictionaryRef dict, CFStringRef name, CGFloat* value) {
-    CFNumberRef num;
-    return CFDictionaryGetValueIfPresent(dict, name, (const void**)&num)
-        && CFNumberIsFloatType(num)
-        && CFNumberGetValue(num, kCFNumberCGFloatType, value);
-}
-
-template <typename S, typename D, typename C> struct LinearInterpolater {
-    struct Mapping {
-        S src_val;
-        D dst_val;
-    };
-    constexpr LinearInterpolater(Mapping const mapping[], int mappingCount)
-        : fMapping(mapping), fMappingCount(mappingCount) {}
-
-    static D map(S value, S src_min, S src_max, D dst_min, D dst_max) {
-        SkASSERT(src_min < src_max);
-        SkASSERT(dst_min <= dst_max);
-        return C()(dst_min + (((value - src_min) * (dst_max - dst_min)) / (src_max - src_min)));
-    }
-
-    D map(S val) const {
-        // -Inf to [0]
-        if (val < fMapping[0].src_val) {
-            return fMapping[0].dst_val;
-        }
-
-        // Linear from [i] to [i+1]
-        for (int i = 0; i < fMappingCount - 1; ++i) {
-            if (val < fMapping[i+1].src_val) {
-                return map(val, fMapping[i].src_val, fMapping[i+1].src_val,
-                                fMapping[i].dst_val, fMapping[i+1].dst_val);
-            }
-        }
-
-        // From [n] to +Inf
-        // if (fcweight < Inf)
-        return fMapping[fMappingCount - 1].dst_val;
-    }
-
-    Mapping const * fMapping;
-    int fMappingCount;
-};
-
-struct RoundCGFloatToInt {
-    int operator()(CGFloat s) { return s + 0.5; }
-};
-struct CGFloatIdentity {
-    CGFloat operator()(CGFloat s) { return s; }
-};
-
-/** Returns the [-1, 1] CTFontDescriptor weights for the
- *  <0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000> CSS weights.
- *
- *  It is assumed that the values will be interpolated linearly between these points.
- *  NSFontWeightXXX were added in 10.11, appear in 10.10, but do not appear in 10.9.
- *  The actual values appear to be stable, but they may change in the future without notice.
- */
-static CGFloat(&get_NSFontWeight_mapping())[11] {
-
-    // Declarations in <AppKit/AppKit.h> on macOS, <UIKit/UIKit.h> on iOS
-#ifdef SK_BUILD_FOR_MAC
-#  define SK_KIT_FONT_WEIGHT_PREFIX "NS"
-#endif
-#ifdef SK_BUILD_FOR_IOS
-#  define SK_KIT_FONT_WEIGHT_PREFIX "UI"
-#endif
-    static constexpr struct {
-        CGFloat defaultValue;
-        const char* name;
-    } nsFontWeightLoaderInfos[] = {
-        { -0.80f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightUltraLight" },
-        { -0.60f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightThin" },
-        { -0.40f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightLight" },
-        {  0.00f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightRegular" },
-        {  0.23f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightMedium" },
-        {  0.30f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightSemibold" },
-        {  0.40f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightBold" },
-        {  0.56f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightHeavy" },
-        {  0.62f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightBlack" },
-    };
-
-    static_assert(SK_ARRAY_COUNT(nsFontWeightLoaderInfos) == 9, "");
-    static CGFloat nsFontWeights[11];
-    static SkOnce once;
-    once([&] {
-        size_t i = 0;
-        nsFontWeights[i++] = -1.00;
-        for (const auto& nsFontWeightLoaderInfo : nsFontWeightLoaderInfos) {
-            void* nsFontWeightValuePtr = dlsym(RTLD_DEFAULT, nsFontWeightLoaderInfo.name);
-            if (nsFontWeightValuePtr) {
-                nsFontWeights[i++] = *(static_cast<CGFloat*>(nsFontWeightValuePtr));
-            } else {
-                nsFontWeights[i++] = nsFontWeightLoaderInfo.defaultValue;
-            }
-        }
-        nsFontWeights[i++] = 1.00;
-    });
-    return nsFontWeights;
-}
-
-/** Convert the [0, 1000] CSS weight to [-1, 1] CTFontDescriptor weight (for system fonts).
- *
- *  The -1 to 1 weights reported by CTFontDescriptors have different mappings depending on if the
- *  CTFont is native or created from a CGDataProvider.
- */
-static CGFloat fontstyle_to_ct_weight(int fontstyleWeight) {
-    using Interpolator = LinearInterpolater<int, CGFloat, CGFloatIdentity>;
-
-    // Note that Mac supports the old OS2 version A so 0 through 10 are as if multiplied by 100.
-    // However, on this end we can't tell, so this is ignored.
-
-    static Interpolator::Mapping nativeWeightMappings[11];
-    static SkOnce once;
-    once([&] {
-        CGFloat(&nsFontWeights)[11] = get_NSFontWeight_mapping();
-        for (int i = 0; i < 11; ++i) {
-            nativeWeightMappings[i].src_val = i * 100;
-            nativeWeightMappings[i].dst_val = nsFontWeights[i];
-        }
-    });
-    static constexpr Interpolator nativeInterpolator(
-            nativeWeightMappings, SK_ARRAY_COUNT(nativeWeightMappings));
-
-    return nativeInterpolator.map(fontstyleWeight);
-}
-
-
-/** Convert the [-1, 1] CTFontDescriptor weight to [0, 1000] CSS weight.
- *
- *  The -1 to 1 weights reported by CTFontDescriptors have different mappings depending on if the
- *  CTFont is native or created from a CGDataProvider.
- */
-static int ct_weight_to_fontstyle(CGFloat cgWeight, bool fromDataProvider) {
-    using Interpolator = LinearInterpolater<CGFloat, int, RoundCGFloatToInt>;
-
-    // Note that Mac supports the old OS2 version A so 0 through 10 are as if multiplied by 100.
-    // However, on this end we can't tell, so this is ignored.
-
-    /** This mapping for CGDataProvider created fonts is determined by creating font data with every
-     *  weight, creating a CTFont, and asking the CTFont for its weight. See the TypefaceStyle test
-     *  in tests/TypefaceTest.cpp for the code used to determine these values.
-     */
-    static constexpr Interpolator::Mapping dataProviderWeightMappings[] = {
-        { -1.00,    0 },
-        { -0.70,  100 },
-        { -0.50,  200 },
-        { -0.23,  300 },
-        {  0.00,  400 },
-        {  0.20,  500 },
-        {  0.30,  600 },
-        {  0.40,  700 },
-        {  0.60,  800 },
-        {  0.80,  900 },
-        {  1.00, 1000 },
-    };
-    static constexpr Interpolator dataProviderInterpolator(
-            dataProviderWeightMappings, SK_ARRAY_COUNT(dataProviderWeightMappings));
-
-    static Interpolator::Mapping nativeWeightMappings[11];
-    static SkOnce once;
-    once([&] {
-        CGFloat(&nsFontWeights)[11] = get_NSFontWeight_mapping();
-        for (int i = 0; i < 11; ++i) {
-            nativeWeightMappings[i].src_val = nsFontWeights[i];
-            nativeWeightMappings[i].dst_val = i * 100;
-        }
-    });
-    static constexpr Interpolator nativeInterpolator(
-            nativeWeightMappings, SK_ARRAY_COUNT(nativeWeightMappings));
-
-    return fromDataProvider ? dataProviderInterpolator.map(cgWeight)
-                            : nativeInterpolator.map(cgWeight);
-}
-
-/** Convert the [0, 10] CSS weight to [-1, 1] CTFontDescriptor width. */
-static int fontstyle_to_ct_width(int fontstyleWidth) {
-    using Interpolator = LinearInterpolater<int, CGFloat, CGFloatIdentity>;
-
-    // Values determined by creating font data with every width, creating a CTFont,
-    // and asking the CTFont for its width. See TypefaceStyle test for basics.
-    static constexpr Interpolator::Mapping widthMappings[] = {
-        {  0, -0.5 },
-        { 10,  0.5 },
-    };
-    static constexpr Interpolator interpolator(widthMappings, SK_ARRAY_COUNT(widthMappings));
-    return interpolator.map(fontstyleWidth);
-}
-
-/** Convert the [-1, 1] CTFontDescriptor width to [0, 10] CSS weight. */
-static int ct_width_to_fontstyle(CGFloat cgWidth) {
-    using Interpolator = LinearInterpolater<CGFloat, int, RoundCGFloatToInt>;
-
-    // Values determined by creating font data with every width, creating a CTFont,
-    // and asking the CTFont for its width. See TypefaceStyle test for basics.
-    static constexpr Interpolator::Mapping widthMappings[] = {
-        { -0.5,  0 },
-        {  0.5, 10 },
-    };
-    static constexpr Interpolator interpolator(widthMappings, SK_ARRAY_COUNT(widthMappings));
-    return interpolator.map(cgWidth);
-}
-
-static SkFontStyle fontstyle_from_descriptor(CTFontDescriptorRef desc, bool fromDataProvider) {
-    SkUniqueCFRef<CFTypeRef> traits(CTFontDescriptorCopyAttribute(desc, kCTFontTraitsAttribute));
-    if (!traits || CFDictionaryGetTypeID() != CFGetTypeID(traits.get())) {
-        return SkFontStyle();
-    }
-    SkUniqueCFRef<CFDictionaryRef> fontTraitsDict(static_cast<CFDictionaryRef>(traits.release()));
-
-    CGFloat weight, width, slant;
-    if (!find_dict_CGFloat(fontTraitsDict.get(), kCTFontWeightTrait, &weight)) {
-        weight = 0;
-    }
-    if (!find_dict_CGFloat(fontTraitsDict.get(), kCTFontWidthTrait, &width)) {
-        width = 0;
-    }
-    if (!find_dict_CGFloat(fontTraitsDict.get(), kCTFontSlantTrait, &slant)) {
-        slant = 0;
-    }
-
-    return SkFontStyle(ct_weight_to_fontstyle(weight, fromDataProvider),
-                       ct_width_to_fontstyle(width),
-                       slant ? SkFontStyle::kItalic_Slant
-                             : SkFontStyle::kUpright_Slant);
-}
-
-struct OpszVariation {
-    bool isSet = false;
-    double value = 0;
-};
-
-class SkTypeface_Mac : public SkTypeface {
-public:
-    SkTypeface_Mac(SkUniqueCFRef<CTFontRef> fontRef, const SkFontStyle& fs, bool isFixedPitch,
-                   OpszVariation opszVariation, std::unique_ptr<SkStreamAsset> providedData)
-        : SkTypeface(fs, isFixedPitch)
-        , fFontRef(std::move(fontRef))
-        , fOpszVariation(opszVariation)
-        , fHasColorGlyphs(
-                SkToBool(CTFontGetSymbolicTraits(fFontRef.get()) & kCTFontColorGlyphsTrait))
-        , fStream(std::move(providedData))
-        , fIsFromStream(fStream)
-    {
-        SkASSERT(fFontRef);
-    }
-
-    SkUniqueCFRef<CTFontRef> fFontRef;
-    const OpszVariation fOpszVariation;
-    const bool fHasColorGlyphs;
-
-protected:
-    int onGetUPEM() const override;
-    std::unique_ptr<SkStreamAsset> onOpenStream(int* ttcIndex) const override;
-    std::unique_ptr<SkFontData> onMakeFontData() const override;
-    int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[],
-                                     int coordinateCount) const override;
-    void onGetFamilyName(SkString* familyName) const override;
-    SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override;
-    int onGetTableTags(SkFontTableTag tags[]) const override;
-    size_t onGetTableData(SkFontTableTag, size_t offset, size_t length, void* data) const override;
-    sk_sp<SkData> onCopyTableData(SkFontTableTag) const override;
-    SkScalerContext* onCreateScalerContext(const SkScalerContextEffects&,
-                                           const SkDescriptor*) const override;
-    void onFilterRec(SkScalerContextRec*) const override;
-    void onGetFontDescriptor(SkFontDescriptor*, bool*) const override;
-    void getGlyphToUnicodeMap(SkUnichar*) const override;
-    std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override;
-    void onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const override;
-    int onCountGlyphs() const override;
-    void getPostScriptGlyphNames(SkString*) const override {}
-    int onGetVariationDesignParameters(SkFontParameters::Variation::Axis parameters[],
-                                       int parameterCount) const override;
-    sk_sp<SkTypeface> onMakeClone(const SkFontArguments&) const override;
-
-    void* onGetCTFontRef() const override { return (void*)fFontRef.get(); }
-
-private:
-    mutable std::unique_ptr<SkStreamAsset> fStream;
-    bool fIsFromStream;
-    mutable SkOnce fInitStream;
-
-    typedef SkTypeface INHERITED;
-};
-
-static bool find_by_CTFontRef(SkTypeface* cached, void* context) {
-    CTFontRef self = (CTFontRef)context;
-    CTFontRef other = (CTFontRef)cached->internal_private_getCTFontRef();
-
-    return CFEqual(self, other);
-}
-
-/** Creates a typeface, searching the cache if isLocalStream is false. */
-static sk_sp<SkTypeface> create_from_CTFontRef(SkUniqueCFRef<CTFontRef> font,
-                                               OpszVariation opszVariation,
-                                               std::unique_ptr<SkStreamAsset> providedData) {
-    SkASSERT(font);
-    const bool isFromStream(providedData);
-
-    if (!isFromStream) {
-        sk_sp<SkTypeface> face = SkTypefaceCache::FindByProcAndRef(find_by_CTFontRef,
-                                                                   (void*)font.get());
-        if (face) {
-            return face;
-        }
-    }
-
-    SkUniqueCFRef<CTFontDescriptorRef> desc(CTFontCopyFontDescriptor(font.get()));
-    SkFontStyle style = fontstyle_from_descriptor(desc.get(), isFromStream);
-    CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(font.get());
-    bool isFixedPitch = SkToBool(traits & kCTFontMonoSpaceTrait);
-
-    sk_sp<SkTypeface> face(new SkTypeface_Mac(std::move(font), style,
-                                              isFixedPitch, opszVariation,
-                                              std::move(providedData)));
-    if (!isFromStream) {
-        SkTypefaceCache::Add(face);
-    }
-    return face;
-}
-
-/** Creates a typeface from a descriptor, searching the cache. */
-static sk_sp<SkTypeface> create_from_desc(CTFontDescriptorRef desc) {
-    SkUniqueCFRef<CTFontRef> ctFont(CTFontCreateWithFontDescriptor(desc, 0, nullptr));
-    if (!ctFont) {
-        return nullptr;
-    }
-
-    return create_from_CTFontRef(std::move(ctFont), OpszVariation(), nullptr);
-}
-
-static SkUniqueCFRef<CTFontDescriptorRef> create_descriptor(const char familyName[],
-                                                            const SkFontStyle& style) {
-    SkUniqueCFRef<CFMutableDictionaryRef> cfAttributes(
-            CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
-                                      &kCFTypeDictionaryKeyCallBacks,
-                                      &kCFTypeDictionaryValueCallBacks));
-
-    SkUniqueCFRef<CFMutableDictionaryRef> cfTraits(
-            CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
-                                      &kCFTypeDictionaryKeyCallBacks,
-                                      &kCFTypeDictionaryValueCallBacks));
-
-    if (!cfAttributes || !cfTraits) {
-        return nullptr;
-    }
-
-    // TODO(crbug.com/1018581) Some CoreText versions have errant behavior when
-    // certain traits set.  Temporary workaround to omit specifying trait for
-    // those versions.
-    // Long term solution will involve serializing typefaces instead of relying
-    // upon this to match between processes.
-    //
-    // Compare CoreText.h in an up to date SDK for where these values come from.
-    static const uint32_t kSkiaLocalCTVersionNumber10_14 = 0x000B0000;
-    static const uint32_t kSkiaLocalCTVersionNumber10_15 = 0x000C0000;
-
-    // CTFontTraits (symbolic)
-    // macOS 14 and iOS 12 seem to behave badly when kCTFontSymbolicTrait is set.
-    // macOS 15 yields LastResort font instead of a good default font when
-    // kCTFontSymbolicTrait is set.
-    if (!(&CTGetCoreTextVersion && CTGetCoreTextVersion() >= kSkiaLocalCTVersionNumber10_14)) {
-        CTFontSymbolicTraits ctFontTraits = 0;
-        if (style.weight() >= SkFontStyle::kBold_Weight) {
-            ctFontTraits |= kCTFontBoldTrait;
-        }
-        if (style.slant() != SkFontStyle::kUpright_Slant) {
-            ctFontTraits |= kCTFontItalicTrait;
-        }
-        SkUniqueCFRef<CFNumberRef> cfFontTraits(
-                CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &ctFontTraits));
-        if (cfFontTraits) {
-            CFDictionaryAddValue(cfTraits.get(), kCTFontSymbolicTrait, cfFontTraits.get());
-        }
-    }
-
-    // CTFontTraits (weight)
-    CGFloat ctWeight = fontstyle_to_ct_weight(style.weight());
-    SkUniqueCFRef<CFNumberRef> cfFontWeight(
-            CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &ctWeight));
-    if (cfFontWeight) {
-        CFDictionaryAddValue(cfTraits.get(), kCTFontWeightTrait, cfFontWeight.get());
-    }
-    // CTFontTraits (width)
-    CGFloat ctWidth = fontstyle_to_ct_width(style.width());
-    SkUniqueCFRef<CFNumberRef> cfFontWidth(
-            CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &ctWidth));
-    if (cfFontWidth) {
-        CFDictionaryAddValue(cfTraits.get(), kCTFontWidthTrait, cfFontWidth.get());
-    }
-    // CTFontTraits (slant)
-    // macOS 15 behaves badly when kCTFontSlantTrait is set.
-    if (!(&CTGetCoreTextVersion && CTGetCoreTextVersion() == kSkiaLocalCTVersionNumber10_15)) {
-        CGFloat ctSlant = style.slant() == SkFontStyle::kUpright_Slant ? 0 : 1;
-        SkUniqueCFRef<CFNumberRef> cfFontSlant(
-                CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &ctSlant));
-        if (cfFontSlant) {
-            CFDictionaryAddValue(cfTraits.get(), kCTFontSlantTrait, cfFontSlant.get());
-        }
-    }
-    // CTFontTraits
-    CFDictionaryAddValue(cfAttributes.get(), kCTFontTraitsAttribute, cfTraits.get());
-
-    // CTFontFamilyName
-    if (familyName) {
-        SkUniqueCFRef<CFStringRef> cfFontName = make_CFString(familyName);
-        if (cfFontName) {
-            CFDictionaryAddValue(cfAttributes.get(), kCTFontFamilyNameAttribute, cfFontName.get());
-        }
-    }
-
-    return SkUniqueCFRef<CTFontDescriptorRef>(
-            CTFontDescriptorCreateWithAttributes(cfAttributes.get()));
-}
-
-// Same as the above function except style is included so we can
-// compare whether the created font conforms to the style. If not, we need
-// to recreate the font with symbolic traits. This is needed due to MacOS 10.11
-// font creation problem https://bugs.chromium.org/p/skia/issues/detail?id=8447.
-static sk_sp<SkTypeface> create_from_desc_and_style(CTFontDescriptorRef desc,
-                                                    const SkFontStyle& style) {
-    SkUniqueCFRef<CTFontRef> ctFont(CTFontCreateWithFontDescriptor(desc, 0, nullptr));
-    if (!ctFont) {
-        return nullptr;
-    }
-
-    const CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(ctFont.get());
-    CTFontSymbolicTraits expected_traits = traits;
-    if (style.slant() != SkFontStyle::kUpright_Slant) {
-        expected_traits |= kCTFontItalicTrait;
-    }
-    if (style.weight() >= SkFontStyle::kBold_Weight) {
-        expected_traits |= kCTFontBoldTrait;
-    }
-
-    if (expected_traits != traits) {
-        SkUniqueCFRef<CTFontRef> ctNewFont(CTFontCreateCopyWithSymbolicTraits(
-                    ctFont.get(), 0, nullptr, expected_traits, expected_traits));
-        if (ctNewFont) {
-            ctFont = std::move(ctNewFont);
-        }
-    }
-
-    return create_from_CTFontRef(std::move(ctFont), OpszVariation(), nullptr);
-}
-
-/** Creates a typeface from a name, searching the cache. */
-static sk_sp<SkTypeface> create_from_name(const char familyName[], const SkFontStyle& style) {
-    SkUniqueCFRef<CTFontDescriptorRef> desc = create_descriptor(familyName, style);
-    if (!desc) {
-        return nullptr;
-    }
-    return create_from_desc_and_style(desc.get(), style);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-/*  This function is visible on the outside. It first searches the cache, and if
- *  not found, returns a new entry (after adding it to the cache).
- */
-sk_sp<SkTypeface> SkMakeTypefaceFromCTFont(CTFontRef font) {
-    CFRetain(font);
-    return create_from_CTFontRef(SkUniqueCFRef<CTFontRef>(font),
-                                 OpszVariation(),
-                                 nullptr);
-}
-
-static const char* map_css_names(const char* name) {
-    static const struct {
-        const char* fFrom;  // name the caller specified
-        const char* fTo;    // "canonical" name we map to
-    } gPairs[] = {
-        { "sans-serif", "Helvetica" },
-        { "serif",      "Times"     },
-        { "monospace",  "Courier"   }
-    };
-
-    for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) {
-        if (strcmp(name, gPairs[i].fFrom) == 0) {
-            return gPairs[i].fTo;
-        }
-    }
-    return name;    // no change
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-class SkScalerContext_Mac : public SkScalerContext {
-public:
-    SkScalerContext_Mac(sk_sp<SkTypeface_Mac>, const SkScalerContextEffects&, const SkDescriptor*);
-
-protected:
-    unsigned generateGlyphCount(void) override;
-    bool generateAdvance(SkGlyph* glyph) override;
-    void generateMetrics(SkGlyph* glyph) override;
-    void generateImage(const SkGlyph& glyph) override;
-    bool generatePath(SkGlyphID glyph, SkPath* path) override;
-    void generateFontMetrics(SkFontMetrics*) override;
-
-private:
-    template<bool APPLY_PREBLEND>
-    static void RGBToA8(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes,
-                        const SkGlyph& glyph, const uint8_t* table8);
-    template<bool APPLY_PREBLEND>
-    static uint16_t RGBToLcd16(CGRGBPixel rgb, const uint8_t* tableR,
-                               const uint8_t* tableG,
-                               const uint8_t* tableB);
-    template<bool APPLY_PREBLEND>
-    static void RGBToLcd16(const CGRGBPixel* SK_RESTRICT cgPixels,
-                           size_t cgRowBytes,
-                           const SkGlyph& glyph,
-                           const uint8_t* tableR,
-                           const uint8_t* tableG,
-                           const uint8_t* tableB);
-
-    Offscreen fOffscreen;
-
-    /** Unrotated variant of fCTFont.
-     *
-     *  In 10.10.1 CTFontGetAdvancesForGlyphs applies the font transform to the width of the
-     *  advances, but always sets the height to 0. This font is used to get the advances of the
-     *  unrotated glyph, and then the rotation is applied separately.
-     *
-     *  CT vertical metrics are pre-rotated (in em space, before transform) 90deg clock-wise.
-     *  This makes kCTFontOrientationDefault dangerous, because the metrics from
-     *  kCTFontOrientationHorizontal are in a different space from kCTFontOrientationVertical.
-     *  With kCTFontOrientationVertical the advances must be unrotated.
-     *
-     *  Sometimes, creating a copy of a CTFont with the same size but different trasform will select
-     *  different underlying font data. As a result, avoid ever creating more than one CTFont per
-     *  SkScalerContext to ensure that only one CTFont is used.
-     *
-     *  As a result of the above (and other constraints) this font contains the size, but not the
-     *  transform. The transform must always be applied separately.
-     */
-    SkUniqueCFRef<CTFontRef> fCTFont;
-
-    /** The transform without the font size. */
-    CGAffineTransform fTransform;
-    CGAffineTransform fInvTransform;
-
-    SkUniqueCFRef<CGFontRef> fCGFont;
-    uint16_t fGlyphCount;
-    const bool fDoSubPosition;
-
-    friend class Offscreen;
-
-    typedef SkScalerContext INHERITED;
-};
-
-// In macOS 10.12 and later any variation on the CGFont which has default axis value will be
-// dropped when creating the CTFont. Unfortunately, in macOS 10.15 the priority of setting
-// the optical size (and opsz variation) is
-// 1. the value of kCTFontOpticalSizeAttribute in the CTFontDescriptor (undocumented)
-// 2. the opsz axis default value if kCTFontOpticalSizeAttribute is 'none' (undocumented)
-// 3. the opsz variation on the nascent CTFont from the CGFont (was dropped if default)
-// 4. the opsz variation in kCTFontVariationAttribute in CTFontDescriptor (crashes 10.10)
-// 5. the size requested (can fudge in SkTypeface but not SkScalerContext)
-// The first one which is found will be used to set the opsz variation (after clamping).
-static void add_opsz_attr(CFMutableDictionaryRef attr, double opsz) {
-    SkUniqueCFRef<CFNumberRef> opszValueNumber(
-        CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &opsz));
-    // Avoid using kCTFontOpticalSizeAttribute directly
-    CFStringRef SkCTFontOpticalSizeAttribute = CFSTR("NSCTFontOpticalSizeAttribute");
-    CFDictionarySetValue(attr, SkCTFontOpticalSizeAttribute, opszValueNumber.get());
-}
-
-// This turns off application of the 'trak' table to advances, but also all other tracking.
-static void add_notrak_attr(CFMutableDictionaryRef attr) {
-    int zero = 0;
-    SkUniqueCFRef<CFNumberRef> unscaledTrackingNumber(
-        CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &zero));
-    CFStringRef SkCTFontUnscaledTrackingAttribute = CFSTR("NSCTFontUnscaledTrackingAttribute");
-    CFDictionarySetValue(attr, SkCTFontUnscaledTrackingAttribute, unscaledTrackingNumber.get());
-}
-
-static SkUniqueCFRef<CTFontRef> ctfont_create_exact_copy(CTFontRef baseFont, CGFloat textSize,
-                                                         OpszVariation opsz)
-{
-    SkUniqueCFRef<CFMutableDictionaryRef> attr(
-    CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
-                              &kCFTypeDictionaryKeyCallBacks,
-                              &kCFTypeDictionaryValueCallBacks));
-
-    if (opsz.isSet) {
-        add_opsz_attr(attr.get(), opsz.value);
-#if !defined(SK_IGNORE_MAC_OPSZ_FORCE)
-    } else {
-        // On (at least) 10.10 though 10.14 the default system font was SFNSText/SFNSDisplay.
-        // The CTFont is backed by both; optical size < 20 means SFNSText else SFNSDisplay.
-        // On at least 10.11 the glyph ids in these fonts became non-interchangable.
-        // To keep glyph ids stable over size changes, preserve the optical size.
-        // In 10.15 this was replaced with use of variable fonts with an opsz axis.
-        // A CTFont backed by multiple fonts picked by opsz where the multiple backing fonts are
-        // variable fonts with opsz axis and non-interchangeable glyph ids would break the
-        // opsz.isSet branch above, but hopefully that never happens.
-        // See https://crbug.com/524646 .
-        CFStringRef SkCTFontOpticalSizeAttribute = CFSTR("NSCTFontOpticalSizeAttribute");
-        SkUniqueCFRef<CFTypeRef> opsz(CTFontCopyAttribute(baseFont, SkCTFontOpticalSizeAttribute));
-        double opsz_val;
-        if (!opsz ||
-            CFGetTypeID(opsz.get()) != CFNumberGetTypeID() ||
-            !CFNumberGetValue(static_cast<CFNumberRef>(opsz.get()),kCFNumberDoubleType,&opsz_val) ||
-            opsz_val <= 0)
-        {
-            opsz_val = CTFontGetSize(baseFont);
-        }
-        add_opsz_attr(attr.get(), opsz_val);
-#endif
-    }
-    add_notrak_attr(attr.get());
-
-    SkUniqueCFRef<CTFontDescriptorRef> desc(CTFontDescriptorCreateWithAttributes(attr.get()));
-
-#if !defined(SK_IGNORE_MAC_OPSZ_FORCE)
-    return SkUniqueCFRef<CTFontRef>(
-            CTFontCreateCopyWithAttributes(baseFont, textSize, nullptr, desc.get()));
-#else
-    SkUniqueCFRef<CGFontRef> baseCGFont(CTFontCopyGraphicsFont(baseFont, nullptr));
-    return SkUniqueCFRef<CTFontRef>(
-            CTFontCreateWithGraphicsFont(baseCGFont.get(), textSize, nullptr, desc.get()));
-
-#endif
-}
-
-SkScalerContext_Mac::SkScalerContext_Mac(sk_sp<SkTypeface_Mac> typeface,
-                                         const SkScalerContextEffects& effects,
-                                         const SkDescriptor* desc)
-        : INHERITED(std::move(typeface), effects, desc)
-        , fDoSubPosition(SkToBool(fRec.fFlags & kSubpixelPositioning_Flag))
-
-{
-    CTFontRef ctFont = (CTFontRef)this->getTypeface()->internal_private_getCTFontRef();
-    CFIndex numGlyphs = CTFontGetGlyphCount(ctFont);
-    SkASSERT(numGlyphs >= 1 && numGlyphs <= 0xFFFF);
-    fGlyphCount = SkToU16(numGlyphs);
-
-    // CT on (at least) 10.9 will size color glyphs down from the requested size, but not up.
-    // As a result, it is necessary to know the actual device size and request that.
-    SkVector scale;
-    SkMatrix skTransform;
-    bool invertible = fRec.computeMatrices(SkScalerContextRec::kVertical_PreMatrixScale,
-                                           &scale, &skTransform, nullptr, nullptr, nullptr);
-    fTransform = MatrixToCGAffineTransform(skTransform);
-    // CGAffineTransformInvert documents that if the transform is non-invertible it will return the
-    // passed transform unchanged. It does so, but then also prints a message to stdout. Avoid this.
-    if (invertible) {
-        fInvTransform = CGAffineTransformInvert(fTransform);
-    } else {
-        fInvTransform = fTransform;
-    }
-
-    // The transform contains everything except the requested text size.
-    // Some properties, like 'trak', are based on the optical text size.
-    CGFloat textSize = ScalarToCG(scale.y());
-    fCTFont = ctfont_create_exact_copy(ctFont, textSize,
-                                       ((SkTypeface_Mac*)this->getTypeface())->fOpszVariation);
-    fCGFont.reset(CTFontCopyGraphicsFont(fCTFont.get(), nullptr));
-}
-
-CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
-                             CGGlyph glyphID, size_t* rowBytesPtr,
-                             bool generateA8FromLCD) {
-    if (!fRGBSpace) {
-        //It doesn't appear to matter what color space is specified.
-        //Regular blends and antialiased text are always (s*a + d*(1-a))
-        //and subpixel antialiased text is always g=2.0.
-        fRGBSpace.reset(CGColorSpaceCreateDeviceRGB());
-    }
-
-    // default to kBW_Format
-    bool doAA = false;
-    bool doLCD = false;
-
-    if (SkMask::kBW_Format != glyph.maskFormat()) {
-        doLCD = true;
-        doAA = true;
-    }
-
-    // FIXME: lcd smoothed un-hinted rasterization unsupported.
-    if (!generateA8FromLCD && SkMask::kA8_Format == glyph.maskFormat()) {
-        doLCD = false;
-        doAA = true;
-    }
-
-    // If this font might have color glyphs, disable LCD as there's no way to support it.
-    // CoreText doesn't tell us which format it ended up using, so we can't detect it.
-    // A8 will end up black on transparent, but TODO: we can detect gray and set to A8.
-    if (SkMask::kARGB32_Format == glyph.maskFormat()) {
-        doLCD = false;
-    }
-
-    size_t rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
-    if (!fCG || fSize.fWidth < glyph.width() || fSize.fHeight < glyph.height()) {
-        if (fSize.fWidth < glyph.width()) {
-            fSize.fWidth = RoundSize(glyph.width());
-        }
-        if (fSize.fHeight < glyph.height()) {
-            fSize.fHeight = RoundSize(glyph.height());
-        }
-
-        rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
-        void* image = fImageStorage.reset(rowBytes * fSize.fHeight);
-        const CGImageAlphaInfo alpha = (glyph.isColor())
-                                     ? kCGImageAlphaPremultipliedFirst
-                                     : kCGImageAlphaNoneSkipFirst;
-        const CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host | alpha;
-        fCG.reset(CGBitmapContextCreate(image, fSize.fWidth, fSize.fHeight, 8,
-                                        rowBytes, fRGBSpace.get(), bitmapInfo));
-
-        // Skia handles quantization and subpixel positioning,
-        // so disable quantization and enabe subpixel positioning in CG.
-        CGContextSetAllowsFontSubpixelQuantization(fCG.get(), false);
-        CGContextSetShouldSubpixelQuantizeFonts(fCG.get(), false);
-
-        // Because CG always draws from the horizontal baseline,
-        // if there is a non-integral translation from the horizontal origin to the vertical origin,
-        // then CG cannot draw the glyph in the correct location without subpixel positioning.
-        CGContextSetAllowsFontSubpixelPositioning(fCG.get(), true);
-        CGContextSetShouldSubpixelPositionFonts(fCG.get(), true);
-
-        CGContextSetTextDrawingMode(fCG.get(), kCGTextFill);
-
-        // Draw black on white to create mask. (Special path exists to speed this up in CG.)
-        CGContextSetGrayFillColor(fCG.get(), 0.0f, 1.0f);
-
-        // force our checks below to happen
-        fDoAA = !doAA;
-        fDoLCD = !doLCD;
-
-        CGContextSetTextMatrix(fCG.get(), context.fTransform);
-    }
-
-    if (fDoAA != doAA) {
-        CGContextSetShouldAntialias(fCG.get(), doAA);
-        fDoAA = doAA;
-    }
-    if (fDoLCD != doLCD) {
-        CGContextSetShouldSmoothFonts(fCG.get(), doLCD);
-        fDoLCD = doLCD;
-    }
-
-    CGRGBPixel* image = (CGRGBPixel*)fImageStorage.get();
-    // skip rows based on the glyph's height
-    image += (fSize.fHeight - glyph.height()) * fSize.fWidth;
-
-    // Erase to white (or transparent black if it's a color glyph, to not composite against white).
-    uint32_t bgColor = (!glyph.isColor()) ? 0xFFFFFFFF : 0x00000000;
-    sk_memset_rect32(image, bgColor, glyph.width(), glyph.height(), rowBytes);
-
-    float subX = 0;
-    float subY = 0;
-    if (context.fDoSubPosition) {
-        subX = SkFixedToFloat(glyph.getSubXFixed());
-        subY = SkFixedToFloat(glyph.getSubYFixed());
-    }
-
-    CGPoint point = CGPointMake(-glyph.left() + subX, glyph.top() + glyph.height() - subY);
-    // Prior to 10.10, CTFontDrawGlyphs acted like CGContextShowGlyphsAtPositions and took
-    // 'positions' which are in text space. The glyph location (in device space) must be
-    // mapped into text space, so that CG can convert it back into device space.
-    // In 10.10.1, this is handled directly in CTFontDrawGlyphs.
-    //
-    // However, in 10.10.2 color glyphs no longer rotate based on the font transform.
-    // So always make the font transform identity and place the transform on the context.
-    point = CGPointApplyAffineTransform(point, context.fInvTransform);
-
-    CTFontDrawGlyphs(context.fCTFont.get(), &glyphID, &point, 1, fCG.get());
-
-    SkASSERT(rowBytesPtr);
-    *rowBytesPtr = rowBytes;
-    return image;
-}
-
-unsigned SkScalerContext_Mac::generateGlyphCount(void) {
-    return fGlyphCount;
-}
-
-bool SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {
-    return false;
-}
-
-void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) {
-    glyph->fMaskFormat = fRec.fMaskFormat;
-
-    const CGGlyph cgGlyph = (CGGlyph) glyph->getGlyphID();
-    glyph->zeroMetrics();
-
-    // The following block produces cgAdvance in CG units (pixels, y up).
-    CGSize cgAdvance;
-    CTFontGetAdvancesForGlyphs(fCTFont.get(), kCTFontOrientationHorizontal,
-                               &cgGlyph, &cgAdvance, 1);
-    cgAdvance = CGSizeApplyAffineTransform(cgAdvance, fTransform);
-    glyph->fAdvanceX =  CGToFloat(cgAdvance.width);
-    glyph->fAdvanceY = -CGToFloat(cgAdvance.height);
-
-    // The following produces skBounds in SkGlyph units (pixels, y down),
-    // or returns early if skBounds would be empty.
-    SkRect skBounds;
-
-    // Glyphs are always drawn from the horizontal origin. The caller must manually use the result
-    // of CTFontGetVerticalTranslationsForGlyphs to calculate where to draw the glyph for vertical
-    // glyphs. As a result, always get the horizontal bounds of a glyph and translate it if the
-    // glyph is vertical. This avoids any diagreement between the various means of retrieving
-    // vertical metrics.
-    {
-        // CTFontGetBoundingRectsForGlyphs produces cgBounds in CG units (pixels, y up).
-        CGRect cgBounds;
-        CTFontGetBoundingRectsForGlyphs(fCTFont.get(), kCTFontOrientationHorizontal,
-                                        &cgGlyph, &cgBounds, 1);
-        cgBounds = CGRectApplyAffineTransform(cgBounds, fTransform);
-
-        // BUG?
-        // 0x200B (zero-advance space) seems to return a huge (garbage) bounds, when
-        // it should be empty. So, if we see a zero-advance, we check if it has an
-        // empty path or not, and if so, we jam the bounds to 0. Hopefully a zero-advance
-        // is rare, so we won't incur a big performance cost for this extra check.
-        if (0 == cgAdvance.width && 0 == cgAdvance.height) {
-            SkUniqueCFRef<CGPathRef> path(CTFontCreatePathForGlyph(fCTFont.get(), cgGlyph,nullptr));
-            if (!path || CGPathIsEmpty(path.get())) {
-                return;
-            }
-        }
-
-        if (CGRectIsEmpty_inline(cgBounds)) {
-            return;
-        }
-
-        // Convert cgBounds to SkGlyph units (pixels, y down).
-        skBounds = SkRect::MakeXYWH(cgBounds.origin.x, -cgBounds.origin.y - cgBounds.size.height,
-                                    cgBounds.size.width, cgBounds.size.height);
-    }
-
-    // Currently the bounds are based on being rendered at (0,0).
-    // The top left must not move, since that is the base from which subpixel positioning is offset.
-    if (fDoSubPosition) {
-        skBounds.fRight += SkFixedToFloat(glyph->getSubXFixed());
-        skBounds.fBottom += SkFixedToFloat(glyph->getSubYFixed());
-    }
-
-    // We're trying to pack left and top into int16_t,
-    // and width and height into uint16_t, after outsetting by 1.
-    if (!SkRect::MakeXYWH(-32767, -32767, 65535, 65535).contains(skBounds)) {
-        return;
-    }
-
-    SkIRect skIBounds;
-    skBounds.roundOut(&skIBounds);
-    // Expand the bounds by 1 pixel, to give CG room for anti-aliasing.
-    // Note that this outset is to allow room for LCD smoothed glyphs. However, the correct outset
-    // is not currently known, as CG dilates the outlines by some percentage.
-    // Note that if this context is A8 and not back-forming from LCD, there is no need to outset.
-    skIBounds.outset(1, 1);
-    glyph->fLeft = SkToS16(skIBounds.fLeft);
-    glyph->fTop = SkToS16(skIBounds.fTop);
-    glyph->fWidth = SkToU16(skIBounds.width());
-    glyph->fHeight = SkToU16(skIBounds.height());
-}
-
-static constexpr uint8_t sk_pow2_table(size_t i) {
-    return SkToU8(((i * i + 128) / 255));
-}
-
-/**
- *  This will invert the gamma applied by CoreGraphics, so we can get linear
- *  values.
- *
- *  CoreGraphics obscurely defaults to 2.0 as the subpixel coverage gamma value.
- *  The color space used does not appear to affect this choice.
- */
-static constexpr auto gLinearCoverageFromCGLCDValue = SkMakeArray<256>(sk_pow2_table);
-
-static void cgpixels_to_bits(uint8_t dst[], const CGRGBPixel src[], int count) {
-    while (count > 0) {
-        uint8_t mask = 0;
-        for (int i = 7; i >= 0; --i) {
-            mask |= ((CGRGBPixel_getAlpha(*src++) >> 7) ^ 0x1) << i;
-            if (0 == --count) {
-                break;
-            }
-        }
-        *dst++ = mask;
-    }
-}
-
-template<bool APPLY_PREBLEND>
-static inline uint8_t rgb_to_a8(CGRGBPixel rgb, const uint8_t* table8) {
-    U8CPU r = 0xFF - ((rgb >> 16) & 0xFF);
-    U8CPU g = 0xFF - ((rgb >>  8) & 0xFF);
-    U8CPU b = 0xFF - ((rgb >>  0) & 0xFF);
-    U8CPU lum = sk_apply_lut_if<APPLY_PREBLEND>(SkComputeLuminance(r, g, b), table8);
-#if SK_SHOW_TEXT_BLIT_COVERAGE
-    lum = std::max(lum, (U8CPU)0x30);
-#endif
-    return lum;
-}
-
-template<bool APPLY_PREBLEND>
-void SkScalerContext_Mac::RGBToA8(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes,
-                                  const SkGlyph& glyph, const uint8_t* table8) {
-    const int width = glyph.fWidth;
-    size_t dstRB = glyph.rowBytes();
-    uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage;
-
-    for (int y = 0; y < glyph.fHeight; y++) {
-        for (int i = 0; i < width; ++i) {
-            dst[i] = rgb_to_a8<APPLY_PREBLEND>(cgPixels[i], table8);
-        }
-        cgPixels = SkTAddOffset<const CGRGBPixel>(cgPixels, cgRowBytes);
-        dst = SkTAddOffset<uint8_t>(dst, dstRB);
-    }
-}
-
-template<bool APPLY_PREBLEND>
-uint16_t SkScalerContext_Mac::RGBToLcd16(CGRGBPixel rgb, const uint8_t* tableR,
-                                         const uint8_t* tableG,
-                                         const uint8_t* tableB) {
-    U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >> 16) & 0xFF), tableR);
-    U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >>  8) & 0xFF), tableG);
-    U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >>  0) & 0xFF), tableB);
-#if SK_SHOW_TEXT_BLIT_COVERAGE
-    r = std::max(r, (U8CPU)0x30);
-    g = std::max(g, (U8CPU)0x30);
-    b = std::max(b, (U8CPU)0x30);
-#endif
-    return SkPack888ToRGB16(r, g, b);
-}
-
-template<bool APPLY_PREBLEND>
-void SkScalerContext_Mac::RGBToLcd16(const CGRGBPixel* SK_RESTRICT cgPixels,
-                                     size_t cgRowBytes,
-                                     const SkGlyph& glyph,
-                                     const uint8_t* tableR,
-                                     const uint8_t* tableG,
-                                     const uint8_t* tableB) {
-    const int width = glyph.fWidth;
-    size_t dstRB = glyph.rowBytes();
-    uint16_t* SK_RESTRICT dst = (uint16_t*)glyph.fImage;
-
-    for (int y = 0; y < glyph.fHeight; y++) {
-        for (int i = 0; i < width; i++) {
-            dst[i] = RGBToLcd16<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB);
-        }
-        cgPixels = SkTAddOffset<const CGRGBPixel>(cgPixels, cgRowBytes);
-        dst = SkTAddOffset<uint16_t>(dst, dstRB);
-    }
-}
-
-static SkPMColor cgpixels_to_pmcolor(CGRGBPixel rgb) {
-    U8CPU a = (rgb >> 24) & 0xFF;
-    U8CPU r = (rgb >> 16) & 0xFF;
-    U8CPU g = (rgb >>  8) & 0xFF;
-    U8CPU b = (rgb >>  0) & 0xFF;
-#if SK_SHOW_TEXT_BLIT_COVERAGE
-    a = std::max(a, (U8CPU)0x30);
-#endif
-    return SkPackARGB32(a, r, g, b);
-}
-
-void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) {
-    CGGlyph cgGlyph = SkTo<CGGlyph>(glyph.getGlyphID());
-
-    // FIXME: lcd smoothed un-hinted rasterization unsupported.
-    bool requestSmooth = fRec.getHinting() != SkFontHinting::kNone;
-
-    // Draw the glyph
-    size_t cgRowBytes;
-    CGRGBPixel* cgPixels = fOffscreen.getCG(*this, glyph, cgGlyph, &cgRowBytes, requestSmooth);
-    if (cgPixels == nullptr) {
-        return;
-    }
-
-    // Fix the glyph
-    if ((glyph.fMaskFormat == SkMask::kLCD16_Format) ||
-        (glyph.fMaskFormat == SkMask::kA8_Format
-         && requestSmooth
-         && smooth_behavior() != SmoothBehavior::none))
-    {
-        const uint8_t* linear = gLinearCoverageFromCGLCDValue.data();
-
-        //Note that the following cannot really be integrated into the
-        //pre-blend, since we may not be applying the pre-blend; when we aren't
-        //applying the pre-blend it means that a filter wants linear anyway.
-        //Other code may also be applying the pre-blend, so we'd need another
-        //one with this and one without.
-        CGRGBPixel* addr = cgPixels;
-        for (int y = 0; y < glyph.fHeight; ++y) {
-            for (int x = 0; x < glyph.fWidth; ++x) {
-                int r = (addr[x] >> 16) & 0xFF;
-                int g = (addr[x] >>  8) & 0xFF;
-                int b = (addr[x] >>  0) & 0xFF;
-                addr[x] = (linear[r] << 16) | (linear[g] << 8) | linear[b];
-            }
-            addr = SkTAddOffset<CGRGBPixel>(addr, cgRowBytes);
-        }
-    }
-
-    // Convert glyph to mask
-    switch (glyph.fMaskFormat) {
-        case SkMask::kLCD16_Format: {
-            if (fPreBlend.isApplicable()) {
-                RGBToLcd16<true>(cgPixels, cgRowBytes, glyph,
-                                 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
-            } else {
-                RGBToLcd16<false>(cgPixels, cgRowBytes, glyph,
-                                  fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
-            }
-        } break;
-        case SkMask::kA8_Format: {
-            if (fPreBlend.isApplicable()) {
-                RGBToA8<true>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
-            } else {
-                RGBToA8<false>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
-            }
-        } break;
-        case SkMask::kBW_Format: {
-            const int width = glyph.fWidth;
-            size_t dstRB = glyph.rowBytes();
-            uint8_t* dst = (uint8_t*)glyph.fImage;
-            for (int y = 0; y < glyph.fHeight; y++) {
-                cgpixels_to_bits(dst, cgPixels, width);
-                cgPixels = SkTAddOffset<CGRGBPixel>(cgPixels, cgRowBytes);
-                dst = SkTAddOffset<uint8_t>(dst, dstRB);
-            }
-        } break;
-        case SkMask::kARGB32_Format: {
-            const int width = glyph.fWidth;
-            size_t dstRB = glyph.rowBytes();
-            SkPMColor* dst = (SkPMColor*)glyph.fImage;
-            for (int y = 0; y < glyph.fHeight; y++) {
-                for (int x = 0; x < width; ++x) {
-                    dst[x] = cgpixels_to_pmcolor(cgPixels[x]);
-                }
-                cgPixels = SkTAddOffset<CGRGBPixel>(cgPixels, cgRowBytes);
-                dst = SkTAddOffset<SkPMColor>(dst, dstRB);
-            }
-        } break;
-        default:
-            SkDEBUGFAIL("unexpected mask format");
-            break;
-    }
-}
-
-namespace {
-class SkCTPathGeometrySink {
-    SkPath* fPath;
-    bool fStarted;
-    CGPoint fCurrent;
-
-    void goingTo(const CGPoint pt) {
-        if (!fStarted) {
-            fStarted = true;
-            fPath->moveTo(fCurrent.x, -fCurrent.y);
-        }
-        fCurrent = pt;
-    }
-
-    bool currentIsNot(const CGPoint pt) {
-        return fCurrent.x != pt.x || fCurrent.y != pt.y;
-    }
-
-public:
-    SkCTPathGeometrySink(SkPath* path) : fPath{path}, fStarted{false}, fCurrent{0,0} {}
-    static void ApplyElement(void *ctx, const CGPathElement *element) {
-        SkCTPathGeometrySink& self = *(SkCTPathGeometrySink*)ctx;
-        CGPoint* points = element->points;
-
-        switch (element->type) {
-            case kCGPathElementMoveToPoint:
-                self.fStarted = false;
-                self.fCurrent = points[0];
-                break;
-
-            case kCGPathElementAddLineToPoint:
-                if (self.currentIsNot(points[0])) {
-                    self.goingTo(points[0]);
-                    self.fPath->lineTo(points[0].x, -points[0].y);
-                }
-                break;
-
-            case kCGPathElementAddQuadCurveToPoint:
-                if (self.currentIsNot(points[0]) || self.currentIsNot(points[1])) {
-                    self.goingTo(points[1]);
-                    self.fPath->quadTo(points[0].x, -points[0].y,
-                                       points[1].x, -points[1].y);
-                }
-                break;
-
-            case kCGPathElementAddCurveToPoint:
-                if (self.currentIsNot(points[0]) ||
-                    self.currentIsNot(points[1]) ||
-                    self.currentIsNot(points[2]))
-                {
-                    self.goingTo(points[2]);
-                    self.fPath->cubicTo(points[0].x, -points[0].y,
-                                        points[1].x, -points[1].y,
-                                        points[2].x, -points[2].y);
-                }
-                break;
-
-            case kCGPathElementCloseSubpath:
-                if (self.fStarted) {
-                    self.fPath->close();
-                }
-                break;
-
-            default:
-                SkDEBUGFAIL("Unknown path element!");
-                break;
-            }
-    }
-};
-} // namespace
-
-/*
- *  Our subpixel resolution is only 2 bits in each direction, so a scale of 4
- *  seems sufficient, and possibly even correct, to allow the hinted outline
- *  to be subpixel positioned.
- */
-#define kScaleForSubPixelPositionHinting (4.0f)
-
-bool SkScalerContext_Mac::generatePath(SkGlyphID glyph, SkPath* path) {
-    SkScalar scaleX = SK_Scalar1;
-    SkScalar scaleY = SK_Scalar1;
-
-    CGAffineTransform xform = fTransform;
-    /*
-     *  For subpixel positioning, we want to return an unhinted outline, so it
-     *  can be positioned nicely at fractional offsets. However, we special-case
-     *  if the baseline of the (horizontal) text is axis-aligned. In those cases
-     *  we want to retain hinting in the direction orthogonal to the baseline.
-     *  e.g. for horizontal baseline, we want to retain hinting in Y.
-     *  The way we remove hinting is to scale the font by some value (4) in that
-     *  direction, ask for the path, and then scale the path back down.
-     */
-    if (fDoSubPosition) {
-        // start out by assuming that we want no hining in X and Y
-        scaleX = scaleY = kScaleForSubPixelPositionHinting;
-        // now see if we need to restore hinting for axis-aligned baselines
-        switch (this->computeAxisAlignmentForHText()) {
-            case kX_SkAxisAlignment:
-                scaleY = SK_Scalar1; // want hinting in the Y direction
-                break;
-            case kY_SkAxisAlignment:
-                scaleX = SK_Scalar1; // want hinting in the X direction
-                break;
-            default:
-                break;
-        }
-
-        CGAffineTransform scale(CGAffineTransformMakeScale(ScalarToCG(scaleX), ScalarToCG(scaleY)));
-        xform = CGAffineTransformConcat(fTransform, scale);
-    }
-
-    CGGlyph cgGlyph = SkTo<CGGlyph>(glyph);
-    SkUniqueCFRef<CGPathRef> cgPath(CTFontCreatePathForGlyph(fCTFont.get(), cgGlyph, &xform));
-
-    path->reset();
-    if (!cgPath) {
-        return false;
-    }
-
-    SkCTPathGeometrySink sink(path);
-    CGPathApply(cgPath.get(), &sink, SkCTPathGeometrySink::ApplyElement);
-    if (fDoSubPosition) {
-        SkMatrix m;
-        m.setScale(SkScalarInvert(scaleX), SkScalarInvert(scaleY));
-        path->transform(m);
-    }
-    return true;
-}
-
-void SkScalerContext_Mac::generateFontMetrics(SkFontMetrics* metrics) {
-    if (nullptr == metrics) {
-        return;
-    }
-
-    CGRect theBounds = CTFontGetBoundingBox(fCTFont.get());
-
-    metrics->fTop          = CGToScalar(-CGRectGetMaxY_inline(theBounds));
-    metrics->fAscent       = CGToScalar(-CTFontGetAscent(fCTFont.get()));
-    metrics->fDescent      = CGToScalar( CTFontGetDescent(fCTFont.get()));
-    metrics->fBottom       = CGToScalar(-CGRectGetMinY_inline(theBounds));
-    metrics->fLeading      = CGToScalar( CTFontGetLeading(fCTFont.get()));
-    metrics->fAvgCharWidth = CGToScalar( CGRectGetWidth_inline(theBounds));
-    metrics->fXMin         = CGToScalar( CGRectGetMinX_inline(theBounds));
-    metrics->fXMax         = CGToScalar( CGRectGetMaxX_inline(theBounds));
-    metrics->fMaxCharWidth = metrics->fXMax - metrics->fXMin;
-    metrics->fXHeight      = CGToScalar( CTFontGetXHeight(fCTFont.get()));
-    metrics->fCapHeight    = CGToScalar( CTFontGetCapHeight(fCTFont.get()));
-    metrics->fUnderlineThickness = CGToScalar( CTFontGetUnderlineThickness(fCTFont.get()));
-    metrics->fUnderlinePosition = -CGToScalar( CTFontGetUnderlinePosition(fCTFont.get()));
-
-    metrics->fFlags = 0;
-    metrics->fFlags |= SkFontMetrics::kUnderlineThicknessIsValid_Flag;
-    metrics->fFlags |= SkFontMetrics::kUnderlinePositionIsValid_Flag;
-
-    // See https://bugs.chromium.org/p/skia/issues/detail?id=6203
-    // At least on 10.12.3 with memory based fonts the x-height is always 0.6666 of the ascent and
-    // the cap-height is always 0.8888 of the ascent. It appears that the values from the 'OS/2'
-    // table are read, but then overwritten if the font is not a system font. As a result, if there
-    // is a valid 'OS/2' table available use the values from the table if they aren't too strange.
-    struct OS2HeightMetrics {
-        SK_OT_SHORT sxHeight;
-        SK_OT_SHORT sCapHeight;
-    } heights;
-    size_t bytesRead = this->getTypeface()->getTableData(
-            SkTEndian_SwapBE32(SkOTTableOS2::TAG), offsetof(SkOTTableOS2, version.v2.sxHeight),
-            sizeof(heights), &heights);
-    if (bytesRead == sizeof(heights)) {
-        // 'fontSize' is correct because the entire resolved size is set by the constructor.
-        CGFloat fontSize = CTFontGetSize(this->fCTFont.get());
-        unsigned upem = CTFontGetUnitsPerEm(this->fCTFont.get());
-        unsigned maxSaneHeight = upem * 2;
-        uint16_t xHeight = SkEndian_SwapBE16(heights.sxHeight);
-        if (xHeight && xHeight < maxSaneHeight) {
-            metrics->fXHeight = CGToScalar(xHeight * fontSize / upem);
-        }
-        uint16_t capHeight = SkEndian_SwapBE16(heights.sCapHeight);
-        if (capHeight && capHeight < maxSaneHeight) {
-            metrics->fCapHeight = CGToScalar(capHeight * fontSize / upem);
-        }
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-// Web fonts added to the CTFont registry do not return their character set.
-// Iterate through the font in this case. The existing caller caches the result,
-// so the performance impact isn't too bad.
-static void populate_glyph_to_unicode_slow(CTFontRef ctFont, CFIndex glyphCount,
-                                           SkUnichar* out) {
-    sk_bzero(out, glyphCount * sizeof(SkUnichar));
-    UniChar unichar = 0;
-    while (glyphCount > 0) {
-        CGGlyph glyph;
-        if (CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
-            if (out[glyph] == 0) {
-                out[glyph] = unichar;
-                --glyphCount;
-            }
-        }
-        if (++unichar == 0) {
-            break;
-        }
-    }
-}
-
-static constexpr uint16_t kPlaneSize = 1 << 13;
-
-static void get_plane_glyph_map(const uint8_t* bits,
-                                CTFontRef ctFont,
-                                CFIndex glyphCount,
-                                SkUnichar* glyphToUnicode,
-                                uint8_t planeIndex) {
-    SkUnichar planeOrigin = (SkUnichar)planeIndex << 16; // top half of codepoint.
-    for (uint16_t i = 0; i < kPlaneSize; i++) {
-        uint8_t mask = bits[i];
-        if (!mask) {
-            continue;
-        }
-        for (uint8_t j = 0; j < 8; j++) {
-            if (0 == (mask & ((uint8_t)1 << j))) {
-                continue;
-            }
-            uint16_t planeOffset = (i << 3) | j;
-            SkUnichar codepoint = planeOrigin | (SkUnichar)planeOffset;
-            uint16_t utf16[2] = {planeOffset, 0};
-            size_t count = 1;
-            if (planeOrigin != 0) {
-                count = SkUTF::ToUTF16(codepoint, utf16);
-            }
-            CGGlyph glyphs[2] = {0, 0};
-            if (CTFontGetGlyphsForCharacters(ctFont, utf16, glyphs, count)) {
-                SkASSERT(glyphs[1] == 0);
-                SkASSERT(glyphs[0] < glyphCount);
-                // CTFontCopyCharacterSet and CTFontGetGlyphsForCharacters seem to add 'support'
-                // for characters 0x9, 0xA, and 0xD mapping them to the glyph for character 0x20?
-                // Prefer mappings to codepoints at or above 0x20.
-                if (glyphToUnicode[glyphs[0]] < 0x20) {
-                    glyphToUnicode[glyphs[0]] = codepoint;
-                }
-            }
-        }
-    }
-}
-// Construct Glyph to Unicode table.
-static void populate_glyph_to_unicode(CTFontRef ctFont, CFIndex glyphCount,
-                                      SkUnichar* glyphToUnicode) {
-    sk_bzero(glyphToUnicode, sizeof(SkUnichar) * glyphCount);
-    SkUniqueCFRef<CFCharacterSetRef> charSet(CTFontCopyCharacterSet(ctFont));
-    if (!charSet) {
-        populate_glyph_to_unicode_slow(ctFont, glyphCount, glyphToUnicode);
-        return;
-    }
-
-    SkUniqueCFRef<CFDataRef> bitmap(
-            CFCharacterSetCreateBitmapRepresentation(nullptr, charSet.get()));
-    if (!bitmap) {
-        return;
-    }
-    CFIndex dataLength = CFDataGetLength(bitmap.get());
-    if (!dataLength) {
-        return;
-    }
-    SkASSERT(dataLength >= kPlaneSize);
-    const UInt8* bits = CFDataGetBytePtr(bitmap.get());
-
-    get_plane_glyph_map(bits, ctFont, glyphCount, glyphToUnicode, 0);
-    /*
-    A CFData object that specifies the bitmap representation of the Unicode
-    character points the for the new character set. The bitmap representation could
-    contain all the Unicode character range starting from BMP to Plane 16. The
-    first 8KiB (8192 bytes) of the data represent the BMP range. The BMP range 8KiB
-    can be followed by zero to sixteen 8KiB bitmaps, each prepended with the plane
-    index byte. For example, the bitmap representing the BMP and Plane 2 has the
-    size of 16385 bytes (8KiB for BMP, 1 byte index, and a 8KiB bitmap for Plane
-    2). The plane index byte, in this case, contains the integer value two.
-    */
-
-    if (dataLength <= kPlaneSize) {
-        return;
-    }
-    int extraPlaneCount = (dataLength - kPlaneSize) / (1 + kPlaneSize);
-    SkASSERT(dataLength == kPlaneSize + extraPlaneCount * (1 + kPlaneSize));
-    while (extraPlaneCount-- > 0) {
-        bits += kPlaneSize;
-        uint8_t planeIndex = *bits++;
-        SkASSERT(planeIndex >= 1);
-        SkASSERT(planeIndex <= 16);
-        get_plane_glyph_map(bits, ctFont, glyphCount, glyphToUnicode, planeIndex);
-    }
-}
-
-/** Assumes src and dst are not nullptr. */
-static void CFStringToSkString(CFStringRef src, SkString* dst) {
-    // Reserve enough room for the worst-case string,
-    // plus 1 byte for the trailing null.
-    CFIndex length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(src),
-                                                       kCFStringEncodingUTF8) + 1;
-    dst->resize(length);
-    CFStringGetCString(src, dst->writable_str(), length, kCFStringEncodingUTF8);
-    // Resize to the actual UTF-8 length used, stripping the null character.
-    dst->resize(strlen(dst->c_str()));
-}
-
-void SkTypeface_Mac::getGlyphToUnicodeMap(SkUnichar* dstArray) const {
-    SkUniqueCFRef<CTFontRef> ctFont =
-            ctfont_create_exact_copy(fFontRef.get(), CTFontGetUnitsPerEm(fFontRef.get()),
-                                     fOpszVariation);
-    CFIndex glyphCount = CTFontGetGlyphCount(ctFont.get());
-    populate_glyph_to_unicode(ctFont.get(), glyphCount, dstArray);
-}
-
-std::unique_ptr<SkAdvancedTypefaceMetrics> SkTypeface_Mac::onGetAdvancedMetrics() const {
-
-    SkUniqueCFRef<CTFontRef> ctFont =
-            ctfont_create_exact_copy(fFontRef.get(), CTFontGetUnitsPerEm(fFontRef.get()),
-                                     fOpszVariation);
-
-    std::unique_ptr<SkAdvancedTypefaceMetrics> info(new SkAdvancedTypefaceMetrics);
-
-    {
-        SkUniqueCFRef<CFStringRef> fontName(CTFontCopyPostScriptName(ctFont.get()));
-        if (fontName.get()) {
-            CFStringToSkString(fontName.get(), &info->fPostScriptName);
-            info->fFontName = info->fPostScriptName;
-        }
-    }
-
-    // In 10.10 and earlier, CTFontCopyVariationAxes and CTFontCopyVariation do not work when
-    // applied to fonts which started life with CGFontCreateWithDataProvider (they simply always
-    // return nullptr). As a result, we are limited to CGFontCopyVariationAxes and
-    // CGFontCopyVariations here until support for 10.10 and earlier is removed.
-    SkUniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont.get(), nullptr));
-    if (cgFont) {
-        SkUniqueCFRef<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cgFont.get()));
-        if (cgAxes && CFArrayGetCount(cgAxes.get()) > 0) {
-            info->fFlags |= SkAdvancedTypefaceMetrics::kMultiMaster_FontFlag;
-        }
-    }
-
-    SkOTTableOS2_V4::Type fsType;
-    if (sizeof(fsType) == this->getTableData(SkTEndian_SwapBE32(SkOTTableOS2::TAG),
-                                             offsetof(SkOTTableOS2_V4, fsType),
-                                             sizeof(fsType),
-                                             &fsType)) {
-        SkOTUtils::SetAdvancedTypefaceFlags(fsType, info.get());
-    }
-
-    // If it's not a truetype font, mark it as 'other'. Assume that TrueType
-    // fonts always have both glyf and loca tables. At the least, this is what
-    // sfntly needs to subset the font. CTFontCopyAttribute() does not always
-    // succeed in determining this directly.
-    if (!this->getTableSize('glyf') || !this->getTableSize('loca')) {
-        return info;
-    }
-
-    info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
-    CTFontSymbolicTraits symbolicTraits = CTFontGetSymbolicTraits(ctFont.get());
-    if (symbolicTraits & kCTFontMonoSpaceTrait) {
-        info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
-    }
-    if (symbolicTraits & kCTFontItalicTrait) {
-        info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
-    }
-    CTFontStylisticClass stylisticClass = symbolicTraits & kCTFontClassMaskTrait;
-    if (stylisticClass >= kCTFontOldStyleSerifsClass && stylisticClass <= kCTFontSlabSerifsClass) {
-        info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
-    } else if (stylisticClass & kCTFontScriptsClass) {
-        info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
-    }
-    info->fItalicAngle = (int16_t) CTFontGetSlantAngle(ctFont.get());
-    info->fAscent = (int16_t) CTFontGetAscent(ctFont.get());
-    info->fDescent = (int16_t) CTFontGetDescent(ctFont.get());
-    info->fCapHeight = (int16_t) CTFontGetCapHeight(ctFont.get());
-    CGRect bbox = CTFontGetBoundingBox(ctFont.get());
-
-    SkRect r;
-    r.setLTRB(CGToScalar(CGRectGetMinX_inline(bbox)),   // Left
-              CGToScalar(CGRectGetMaxY_inline(bbox)),   // Top
-              CGToScalar(CGRectGetMaxX_inline(bbox)),   // Right
-              CGToScalar(CGRectGetMinY_inline(bbox)));  // Bottom
-
-    r.roundOut(&(info->fBBox));
-
-    // Figure out a good guess for StemV - Min width of i, I, !, 1.
-    // This probably isn't very good with an italic font.
-    int16_t min_width = SHRT_MAX;
-    info->fStemV = 0;
-    static const UniChar stem_chars[] = {'i', 'I', '!', '1'};
-    const size_t count = sizeof(stem_chars) / sizeof(stem_chars[0]);
-    CGGlyph glyphs[count];
-    CGRect boundingRects[count];
-    if (CTFontGetGlyphsForCharacters(ctFont.get(), stem_chars, glyphs, count)) {
-        CTFontGetBoundingRectsForGlyphs(ctFont.get(), kCTFontOrientationHorizontal,
-                                        glyphs, boundingRects, count);
-        for (size_t i = 0; i < count; i++) {
-            int16_t width = (int16_t) boundingRects[i].size.width;
-            if (width > 0 && width < min_width) {
-                min_width = width;
-                info->fStemV = min_width;
-            }
-        }
-    }
-    return info;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-static SK_SFNT_ULONG get_font_type_tag(CTFontRef ctFont) {
-    SkUniqueCFRef<CFNumberRef> fontFormatRef(
-            static_cast<CFNumberRef>(CTFontCopyAttribute(ctFont, kCTFontFormatAttribute)));
-    if (!fontFormatRef) {
-        return 0;
-    }
-
-    SInt32 fontFormatValue;
-    if (!CFNumberGetValue(fontFormatRef.get(), kCFNumberSInt32Type, &fontFormatValue)) {
-        return 0;
-    }
-
-    switch (fontFormatValue) {
-        case kCTFontFormatOpenTypePostScript:
-            return SkSFNTHeader::fontType_OpenTypeCFF::TAG;
-        case kCTFontFormatOpenTypeTrueType:
-            return SkSFNTHeader::fontType_WindowsTrueType::TAG;
-        case kCTFontFormatTrueType:
-            return SkSFNTHeader::fontType_MacTrueType::TAG;
-        case kCTFontFormatPostScript:
-            return SkSFNTHeader::fontType_PostScript::TAG;
-        case kCTFontFormatBitmap:
-            return SkSFNTHeader::fontType_MacTrueType::TAG;
-        case kCTFontFormatUnrecognized:
-        default:
-            return 0;
-    }
-}
-
-std::unique_ptr<SkStreamAsset> SkTypeface_Mac::onOpenStream(int* ttcIndex) const {
-    *ttcIndex = 0;
-
-    fInitStream([this]{
-    if (fStream) {
-        return;
-    }
-
-    SK_SFNT_ULONG fontType = get_font_type_tag(fFontRef.get());
-
-    // get table tags
-    int numTables = this->countTables();
-    SkTDArray<SkFontTableTag> tableTags;
-    tableTags.setCount(numTables);
-    this->getTableTags(tableTags.begin());
-
-    // CT seems to be unreliable in being able to obtain the type,
-    // even if all we want is the first four bytes of the font resource.
-    // Just the presence of the FontForge 'FFTM' table seems to throw it off.
-    if (fontType == 0) {
-        fontType = SkSFNTHeader::fontType_WindowsTrueType::TAG;
-
-        // see https://skbug.com/7630#c7
-        bool couldBeCFF = false;
-        constexpr SkFontTableTag CFFTag = SkSetFourByteTag('C', 'F', 'F', ' ');
-        constexpr SkFontTableTag CFF2Tag = SkSetFourByteTag('C', 'F', 'F', '2');
-        for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
-            if (CFFTag == tableTags[tableIndex] || CFF2Tag == tableTags[tableIndex]) {
-                couldBeCFF = true;
-            }
-        }
-        if (couldBeCFF) {
-            fontType = SkSFNTHeader::fontType_OpenTypeCFF::TAG;
-        }
-    }
-
-    // Sometimes CoreGraphics incorrectly thinks a font is kCTFontFormatPostScript.
-    // It is exceedingly unlikely that this is the case, so double check
-    // (see https://crbug.com/809763 ).
-    if (fontType == SkSFNTHeader::fontType_PostScript::TAG) {
-        // see if there are any required 'typ1' tables (see Adobe Technical Note #5180)
-        bool couldBeTyp1 = false;
-        constexpr SkFontTableTag TYPE1Tag = SkSetFourByteTag('T', 'Y', 'P', '1');
-        constexpr SkFontTableTag CIDTag = SkSetFourByteTag('C', 'I', 'D', ' ');
-        for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
-            if (TYPE1Tag == tableTags[tableIndex] || CIDTag == tableTags[tableIndex]) {
-                couldBeTyp1 = true;
-            }
-        }
-        if (!couldBeTyp1) {
-            fontType = SkSFNTHeader::fontType_OpenTypeCFF::TAG;
-        }
-    }
-
-    // get the table sizes and accumulate the total size of the font
-    SkTDArray<size_t> tableSizes;
-    size_t totalSize = sizeof(SkSFNTHeader) + sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
-    for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
-        size_t tableSize = this->getTableSize(tableTags[tableIndex]);
-        totalSize += (tableSize + 3) & ~3;
-        *tableSizes.append() = tableSize;
-    }
-
-    // reserve memory for stream, and zero it (tables must be zero padded)
-    fStream.reset(new SkMemoryStream(totalSize));
-    char* dataStart = (char*)fStream->getMemoryBase();
-    sk_bzero(dataStart, totalSize);
-    char* dataPtr = dataStart;
-
-    // compute font header entries
-    uint16_t entrySelector = 0;
-    uint16_t searchRange = 1;
-    while (searchRange < numTables >> 1) {
-        entrySelector++;
-        searchRange <<= 1;
-    }
-    searchRange <<= 4;
-    uint16_t rangeShift = (numTables << 4) - searchRange;
-
-    // write font header
-    SkSFNTHeader* header = (SkSFNTHeader*)dataPtr;
-    header->fontType = fontType;
-    header->numTables = SkEndian_SwapBE16(numTables);
-    header->searchRange = SkEndian_SwapBE16(searchRange);
-    header->entrySelector = SkEndian_SwapBE16(entrySelector);
-    header->rangeShift = SkEndian_SwapBE16(rangeShift);
-    dataPtr += sizeof(SkSFNTHeader);
-
-    // write tables
-    SkSFNTHeader::TableDirectoryEntry* entry = (SkSFNTHeader::TableDirectoryEntry*)dataPtr;
-    dataPtr += sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
-    for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
-        size_t tableSize = tableSizes[tableIndex];
-        this->getTableData(tableTags[tableIndex], 0, tableSize, dataPtr);
-        entry->tag = SkEndian_SwapBE32(tableTags[tableIndex]);
-        entry->checksum = SkEndian_SwapBE32(SkOTUtils::CalcTableChecksum((SK_OT_ULONG*)dataPtr,
-                                                                         tableSize));
-        entry->offset = SkEndian_SwapBE32(SkToU32(dataPtr - dataStart));
-        entry->logicalLength = SkEndian_SwapBE32(SkToU32(tableSize));
-
-        dataPtr += (tableSize + 3) & ~3;
-        ++entry;
-    }
-    });
-    return fStream->duplicate();
-}
-
-struct NonDefaultAxesContext {
-    SkFixed* axisValue;
-    CFArrayRef cgAxes;
-};
-static void set_non_default_axes(CFTypeRef key, CFTypeRef value, void* context) {
-    NonDefaultAxesContext* self = static_cast<NonDefaultAxesContext*>(context);
-
-    if (CFGetTypeID(key) != CFStringGetTypeID() || CFGetTypeID(value) != CFNumberGetTypeID()) {
-        return;
-    }
-
-    // The key is a CFString which is a string from the 'name' table.
-    // Search the cgAxes for an axis with this name, and use its index to store the value.
-    CFIndex keyIndex = -1;
-    CFStringRef keyString = static_cast<CFStringRef>(key);
-    for (CFIndex i = 0; i < CFArrayGetCount(self->cgAxes); ++i) {
-        CFTypeRef cgAxis = CFArrayGetValueAtIndex(self->cgAxes, i);
-        if (CFGetTypeID(cgAxis) != CFDictionaryGetTypeID()) {
-            continue;
-        }
-
-        CFDictionaryRef cgAxisDict = static_cast<CFDictionaryRef>(cgAxis);
-        CFTypeRef cgAxisName = CFDictionaryGetValue(cgAxisDict, kCGFontVariationAxisName);
-        if (!cgAxisName || CFGetTypeID(cgAxisName) != CFStringGetTypeID()) {
-            continue;
-        }
-        CFStringRef cgAxisNameString = static_cast<CFStringRef>(cgAxisName);
-        if (CFStringCompare(keyString, cgAxisNameString, 0) == kCFCompareEqualTo) {
-            keyIndex = i;
-            break;
-        }
-    }
-    if (keyIndex == -1) {
-        return;
-    }
-
-    CFNumberRef valueNumber = static_cast<CFNumberRef>(value);
-    double valueDouble;
-    if (!CFNumberGetValue(valueNumber, kCFNumberDoubleType, &valueDouble) ||
-        valueDouble < SkFixedToDouble(SK_FixedMin) || SkFixedToDouble(SK_FixedMax) < valueDouble)
-    {
-        return;
-    }
-    self->axisValue[keyIndex] = SkDoubleToFixed(valueDouble);
-}
-static bool get_variations(CTFontRef ctFont, CFIndex* cgAxisCount,
-                           SkAutoSTMalloc<4, SkFixed>* axisValues)
-{
-    // In 10.10 and earlier, CTFontCopyVariationAxes and CTFontCopyVariation do not work when
-    // applied to fonts which started life with CGFontCreateWithDataProvider (they simply always
-    // return nullptr). As a result, we are limited to CGFontCopyVariationAxes and
-    // CGFontCopyVariations here until support for 10.10 and earlier is removed.
-    SkUniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont, nullptr));
-    if (!cgFont) {
-        return false;
-    }
-
-    SkUniqueCFRef<CFDictionaryRef> cgVariations(CGFontCopyVariations(cgFont.get()));
-    // If a font has no variations CGFontCopyVariations returns nullptr (instead of an empty dict).
-    if (!cgVariations) {
-        return false;
-    }
-
-    SkUniqueCFRef<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cgFont.get()));
-    if (!cgAxes) {
-        return false;
-    }
-    *cgAxisCount = CFArrayGetCount(cgAxes.get());
-    axisValues->reset(*cgAxisCount);
-
-    // Set all of the axes to their default values.
-    // Fail if any default value cannot be determined.
-    for (CFIndex i = 0; i < *cgAxisCount; ++i) {
-        CFTypeRef cgAxis = CFArrayGetValueAtIndex(cgAxes.get(), i);
-        if (CFGetTypeID(cgAxis) != CFDictionaryGetTypeID()) {
-            return false;
-        }
-
-        CFDictionaryRef cgAxisDict = static_cast<CFDictionaryRef>(cgAxis);
-        CFTypeRef axisDefaultValue = CFDictionaryGetValue(cgAxisDict,
-                                                          kCGFontVariationAxisDefaultValue);
-        if (!axisDefaultValue || CFGetTypeID(axisDefaultValue) != CFNumberGetTypeID()) {
-            return false;
-        }
-        CFNumberRef axisDefaultValueNumber = static_cast<CFNumberRef>(axisDefaultValue);
-        double axisDefaultValueDouble;
-        if (!CFNumberGetValue(axisDefaultValueNumber, kCFNumberDoubleType, &axisDefaultValueDouble))
-        {
-            return false;
-        }
-        if (axisDefaultValueDouble < SkFixedToDouble(SK_FixedMin) ||
-                                     SkFixedToDouble(SK_FixedMax) < axisDefaultValueDouble)
-        {
-            return false;
-        }
-        (*axisValues)[(int)i] = SkDoubleToFixed(axisDefaultValueDouble);
-    }
-
-    // Override the default values with the given font's stated axis values.
-    NonDefaultAxesContext c = { axisValues->get(), cgAxes.get() };
-    CFDictionaryApplyFunction(cgVariations.get(), set_non_default_axes, &c);
-
-    return true;
-}
-std::unique_ptr<SkFontData> SkTypeface_Mac::onMakeFontData() const {
-    int index;
-    std::unique_ptr<SkStreamAsset> stream(this->onOpenStream(&index));
-
-    CFIndex cgAxisCount;
-    SkAutoSTMalloc<4, SkFixed> axisValues;
-    if (get_variations(fFontRef.get(), &cgAxisCount, &axisValues)) {
-        return std::make_unique<SkFontData>(std::move(stream), index,
-                                              axisValues.get(), cgAxisCount);
-    }
-    return std::make_unique<SkFontData>(std::move(stream), index, nullptr, 0);
-}
-
-/** Creates a CT variation dictionary {tag, value} from a CG variation dictionary {name, value}. */
-static SkUniqueCFRef<CFDictionaryRef> ct_variation_from_cg_variation(CFDictionaryRef cgVariations,
-                                                                     CFArrayRef ctAxes) {
-
-    SkUniqueCFRef<CFMutableDictionaryRef> ctVariation(
-            CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
-                                      &kCFTypeDictionaryKeyCallBacks,
-                                      &kCFTypeDictionaryValueCallBacks));
-
-    CFIndex axisCount = CFArrayGetCount(ctAxes);
-    for (CFIndex i = 0; i < axisCount; ++i) {
-        CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes, i);
-        if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
-            return nullptr;
-        }
-        CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
-
-        // The assumption is that values produced by kCTFontVariationAxisNameKey and
-        // kCGFontVariationAxisName will always be equal.
-        CFTypeRef axisName = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisNameKey);
-        if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) {
-            return nullptr;
-        }
-
-        CFTypeRef axisValue = CFDictionaryGetValue(cgVariations, axisName);
-        if (!axisValue || CFGetTypeID(axisValue) != CFNumberGetTypeID()) {
-            return nullptr;
-        }
-
-        CFTypeRef axisTag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey);
-        if (!axisTag || CFGetTypeID(axisTag) != CFNumberGetTypeID()) {
-            return nullptr;
-        }
-
-        CFDictionaryAddValue(ctVariation.get(), axisTag, axisValue);
-    }
-    return std::move(ctVariation);
-}
-
-int SkTypeface_Mac::onGetVariationDesignPosition(
-        SkFontArguments::VariationPosition::Coordinate coordinates[], int coordinateCount) const
-{
-    // The CGFont variation data does not contain the tag.
-
-    // CTFontCopyVariationAxes returns nullptr for CGFontCreateWithDataProvider fonts with
-    // macOS 10.10 and iOS 9 or earlier. When this happens, there is no API to provide the tag.
-    SkUniqueCFRef<CFArrayRef> ctAxes(CTFontCopyVariationAxes(fFontRef.get()));
-    if (!ctAxes) {
-        return -1;
-    }
-    CFIndex axisCount = CFArrayGetCount(ctAxes.get());
-    if (!coordinates || coordinateCount < axisCount) {
-        return axisCount;
-    }
-
-    // This call always returns nullptr on 10.11 and under for CGFontCreateWithDataProvider fonts.
-    // When this happens, try converting the CG variation to a CT variation.
-    // On 10.12 and later, this only returns non-default variations.
-    SkUniqueCFRef<CFDictionaryRef> ctVariation(CTFontCopyVariation(fFontRef.get()));
-    if (!ctVariation) {
-        // When 10.11 and earlier are no longer supported, the following code can be replaced with
-        // return -1 and ct_variation_from_cg_variation can be removed.
-        SkUniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef.get(), nullptr));
-        if (!cgFont) {
-            return -1;
-        }
-        SkUniqueCFRef<CFDictionaryRef> cgVariations(CGFontCopyVariations(cgFont.get()));
-        if (!cgVariations) {
-            return -1;
-        }
-        ctVariation = ct_variation_from_cg_variation(cgVariations.get(), ctAxes.get());
-        if (!ctVariation) {
-            return -1;
-        }
-    }
-
-    for (int i = 0; i < axisCount; ++i) {
-        CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes.get(), i);
-        if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
-            return -1;
-        }
-        CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
-
-        CFTypeRef tag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey);
-        if (!tag || CFGetTypeID(tag) != CFNumberGetTypeID()) {
-            return -1;
-        }
-        CFNumberRef tagNumber = static_cast<CFNumberRef>(tag);
-        int64_t tagLong;
-        if (!CFNumberGetValue(tagNumber, kCFNumberSInt64Type, &tagLong)) {
-            return -1;
-        }
-        coordinates[i].axis = tagLong;
-
-        CGFloat variationCGFloat;
-        CFTypeRef variationValue = CFDictionaryGetValue(ctVariation.get(), tagNumber);
-        if (variationValue) {
-            if (CFGetTypeID(variationValue) != CFNumberGetTypeID()) {
-                return -1;
-            }
-            CFNumberRef variationNumber = static_cast<CFNumberRef>(variationValue);
-            if (!CFNumberGetValue(variationNumber, kCFNumberCGFloatType, &variationCGFloat)) {
-                return -1;
-            }
-        } else {
-            CFTypeRef def = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisDefaultValueKey);
-            if (!def || CFGetTypeID(def) != CFNumberGetTypeID()) {
-                return -1;
-            }
-            CFNumberRef defNumber = static_cast<CFNumberRef>(def);
-            if (!CFNumberGetValue(defNumber, kCFNumberCGFloatType, &variationCGFloat)) {
-                return -1;
-            }
-        }
-        coordinates[i].value = CGToScalar(variationCGFloat);
-
-    }
-    return axisCount;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-
-int SkTypeface_Mac::onGetUPEM() const {
-    SkUniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef.get(), nullptr));
-    return CGFontGetUnitsPerEm(cgFont.get());
-}
-
-SkTypeface::LocalizedStrings* SkTypeface_Mac::onCreateFamilyNameIterator() const {
-    sk_sp<SkTypeface::LocalizedStrings> nameIter =
-            SkOTUtils::LocalizedStrings_NameTable::MakeForFamilyNames(*this);
-    if (!nameIter) {
-        CFStringRef cfLanguageRaw;
-        SkUniqueCFRef<CFStringRef> cfFamilyName(
-                CTFontCopyLocalizedName(fFontRef.get(), kCTFontFamilyNameKey, &cfLanguageRaw));
-        SkUniqueCFRef<CFStringRef> cfLanguage(cfLanguageRaw);
-
-        SkString skLanguage;
-        SkString skFamilyName;
-        if (cfLanguage) {
-            CFStringToSkString(cfLanguage.get(), &skLanguage);
-        } else {
-            skLanguage = "und"; //undetermined
-        }
-        if (cfFamilyName) {
-            CFStringToSkString(cfFamilyName.get(), &skFamilyName);
-        }
-
-        nameIter = sk_make_sp<SkOTUtils::LocalizedStrings_SingleName>(skFamilyName, skLanguage);
-    }
-    return nameIter.release();
-}
-
-int SkTypeface_Mac::onGetTableTags(SkFontTableTag tags[]) const {
-    SkUniqueCFRef<CFArrayRef> cfArray(
-            CTFontCopyAvailableTables(fFontRef.get(), kCTFontTableOptionNoOptions));
-    if (!cfArray) {
-        return 0;
-    }
-    int count = SkToInt(CFArrayGetCount(cfArray.get()));
-    if (tags) {
-        for (int i = 0; i < count; ++i) {
-            uintptr_t fontTag = reinterpret_cast<uintptr_t>(
-                CFArrayGetValueAtIndex(cfArray.get(), i));
-            tags[i] = static_cast<SkFontTableTag>(fontTag);
-        }
-    }
-    return count;
-}
-
-// If, as is the case with web fonts, the CTFont data isn't available,
-// the CGFont data may work. While the CGFont may always provide the
-// right result, leave the CTFont code path to minimize disruption.
-static SkUniqueCFRef<CFDataRef> copy_table_from_font(CTFontRef ctFont, SkFontTableTag tag) {
-    SkUniqueCFRef<CFDataRef> data(CTFontCopyTable(ctFont, (CTFontTableTag) tag,
-                                                  kCTFontTableOptionNoOptions));
-    if (!data) {
-        SkUniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont, nullptr));
-        data.reset(CGFontCopyTableForTag(cgFont.get(), tag));
-    }
-    return data;
-}
-
-size_t SkTypeface_Mac::onGetTableData(SkFontTableTag tag, size_t offset,
-                                      size_t length, void* dstData) const {
-    SkUniqueCFRef<CFDataRef> srcData = copy_table_from_font(fFontRef.get(), tag);
-    if (!srcData) {
-        return 0;
-    }
-
-    size_t srcSize = CFDataGetLength(srcData.get());
-    if (offset >= srcSize) {
-        return 0;
-    }
-    if (length > srcSize - offset) {
-        length = srcSize - offset;
-    }
-    if (dstData) {
-        memcpy(dstData, CFDataGetBytePtr(srcData.get()) + offset, length);
-    }
-    return length;
-}
-
-sk_sp<SkData> SkTypeface_Mac::onCopyTableData(SkFontTableTag tag) const {
-    SkUniqueCFRef<CFDataRef> srcData = copy_table_from_font(fFontRef.get(), tag);
-    if (!srcData) {
-        return nullptr;
-    }
-    const UInt8* data = CFDataGetBytePtr(srcData.get());
-    CFIndex length = CFDataGetLength(srcData.get());
-    return SkData::MakeWithProc(data, length,
-                                [](const void*, void* ctx) {
-                                    CFRelease((CFDataRef)ctx);
-                                }, (void*)srcData.release());
-}
-
-SkScalerContext* SkTypeface_Mac::onCreateScalerContext(const SkScalerContextEffects& effects,
-                                                       const SkDescriptor* desc) const {
-    return new SkScalerContext_Mac(sk_ref_sp(const_cast<SkTypeface_Mac*>(this)), effects, desc);
-}
-
-void SkTypeface_Mac::onFilterRec(SkScalerContextRec* rec) const {
-    if (rec->fFlags & SkScalerContext::kLCD_BGROrder_Flag ||
-        rec->fFlags & SkScalerContext::kLCD_Vertical_Flag)
-    {
-        rec->fMaskFormat = SkMask::kA8_Format;
-        // Render the glyphs as close as possible to what was requested.
-        // The above turns off subpixel rendering, but the user requested it.
-        // Normal hinting will cause the A8 masks to be generated from CoreGraphics subpixel masks.
-        // See comments below for more details.
-        rec->setHinting(SkFontHinting::kNormal);
-    }
-
-    unsigned flagsWeDontSupport = SkScalerContext::kForceAutohinting_Flag  |
-                                  SkScalerContext::kLCD_BGROrder_Flag |
-                                  SkScalerContext::kLCD_Vertical_Flag;
-
-    rec->fFlags &= ~flagsWeDontSupport;
-
-    const SmoothBehavior smoothBehavior = smooth_behavior();
-
-    // Only two levels of hinting are supported.
-    // kNo_Hinting means avoid CoreGraphics outline dilation (smoothing).
-    // kNormal_Hinting means CoreGraphics outline dilation (smoothing) is allowed.
-    if (rec->getHinting() != SkFontHinting::kNone) {
-        rec->setHinting(SkFontHinting::kNormal);
-    }
-    // If smoothing has no effect, don't request it.
-    if (smoothBehavior == SmoothBehavior::none) {
-        rec->setHinting(SkFontHinting::kNone);
-    }
-
-    // FIXME: lcd smoothed un-hinted rasterization unsupported.
-    // Tracked by http://code.google.com/p/skia/issues/detail?id=915 .
-    // There is no current means to honor a request for unhinted lcd,
-    // so arbitrarilly ignore the hinting request and honor lcd.
-
-    // Hinting and smoothing should be orthogonal, but currently they are not.
-    // CoreGraphics has no API to influence hinting. However, its lcd smoothed
-    // output is drawn from auto-dilated outlines (the amount of which is
-    // determined by AppleFontSmoothing). Its regular anti-aliased output is
-    // drawn from un-dilated outlines.
-
-    // The behavior of Skia is as follows:
-    // [AA][no-hint]: generate AA using CoreGraphic's AA output.
-    // [AA][yes-hint]: use CoreGraphic's LCD output and reduce it to a single
-    // channel. This matches [LCD][yes-hint] in weight.
-    // [LCD][no-hint]: curently unable to honor, and must pick which to respect.
-    // Currenly side with LCD, effectively ignoring the hinting setting.
-    // [LCD][yes-hint]: generate LCD using CoreGraphic's LCD output.
-    if (rec->fMaskFormat == SkMask::kLCD16_Format) {
-        if (smoothBehavior == SmoothBehavior::subpixel) {
-            //CoreGraphics creates 555 masks for smoothed text anyway.
-            rec->fMaskFormat = SkMask::kLCD16_Format;
-            rec->setHinting(SkFontHinting::kNormal);
-        } else {
-            rec->fMaskFormat = SkMask::kA8_Format;
-            if (smoothBehavior != SmoothBehavior::none) {
-                rec->setHinting(SkFontHinting::kNormal);
-            }
-        }
-    }
-
-    // CoreText provides no information as to whether a glyph will be color or not.
-    // Fonts may mix outlines and bitmaps, so information is needed on a glyph by glyph basis.
-    // If a font contains an 'sbix' table, consider it to be a color font, and disable lcd.
-    if (fHasColorGlyphs) {
-        rec->fMaskFormat = SkMask::kARGB32_Format;
-    }
-
-    // Unhinted A8 masks (those not derived from LCD masks) must respect SK_GAMMA_APPLY_TO_A8.
-    // All other masks can use regular gamma.
-    if (SkMask::kA8_Format == rec->fMaskFormat && SkFontHinting::kNone == rec->getHinting()) {
-#ifndef SK_GAMMA_APPLY_TO_A8
-        // SRGBTODO: Is this correct? Do we want contrast boost?
-        rec->ignorePreBlend();
-#endif
-    } else {
-        SkColor color = rec->getLuminanceColor();
-        if (smoothBehavior == SmoothBehavior::some) {
-            // CoreGraphics smoothed text without subpixel coverage blitting goes from a gamma of
-            // 2.0 for black foreground to a gamma of 1.0 for white foreground. Emulate this
-            // through the mask gamma by reducing the color values to 1/2.
-            color = SkColorSetRGB(SkColorGetR(color) * 1/2,
-                                  SkColorGetG(color) * 1/2,
-                                  SkColorGetB(color) * 1/2);
-        } else if (smoothBehavior == SmoothBehavior::subpixel) {
-            // CoreGraphics smoothed text with subpixel coverage blitting goes from a gamma of
-            // 2.0 for black foreground to a gamma of ~1.4? for white foreground. Emulate this
-            // through the mask gamma by reducing the color values to 3/4.
-            color = SkColorSetRGB(SkColorGetR(color) * 3/4,
-                                  SkColorGetG(color) * 3/4,
-                                  SkColorGetB(color) * 3/4);
-        }
-        rec->setLuminanceColor(color);
-
-        // CoreGraphics dialates smoothed text to provide contrast.
-        rec->setContrast(0);
-    }
-}
-
-/** Takes ownership of the CFStringRef. */
-static const char* get_str(CFStringRef ref, SkString* str) {
-    if (nullptr == ref) {
-        return nullptr;
-    }
-    CFStringToSkString(ref, str);
-    CFRelease(ref);
-    return str->c_str();
-}
-
-void SkTypeface_Mac::onGetFamilyName(SkString* familyName) const {
-    get_str(CTFontCopyFamilyName(fFontRef.get()), familyName);
-}
-
-void SkTypeface_Mac::onGetFontDescriptor(SkFontDescriptor* desc,
-                                         bool* isLocalStream) const {
-    SkString tmpStr;
-
-    desc->setFamilyName(get_str(CTFontCopyFamilyName(fFontRef.get()), &tmpStr));
-    desc->setFullName(get_str(CTFontCopyFullName(fFontRef.get()), &tmpStr));
-    desc->setPostscriptName(get_str(CTFontCopyPostScriptName(fFontRef.get()), &tmpStr));
-    desc->setStyle(this->fontStyle());
-    *isLocalStream = fIsFromStream;
-}
-
-void SkTypeface_Mac::onCharsToGlyphs(const SkUnichar uni[], int count, SkGlyphID glyphs[]) const {
-    // Undocumented behavior of CTFontGetGlyphsForCharacters with non-bmp code points:
-    // When a surrogate pair is detected, the glyph index used is the index of the high surrogate.
-    // It is documented that if a mapping is unavailable, the glyph will be set to 0.
-
-    SkAutoSTMalloc<1024, UniChar> charStorage;
-    const UniChar* src; // UniChar is a UTF-16 16-bit code unit.
-    int srcCount;
-    const SkUnichar* utf32 = reinterpret_cast<const SkUnichar*>(uni);
-    UniChar* utf16 = charStorage.reset(2 * count);
-    src = utf16;
-    for (int i = 0; i < count; ++i) {
-        utf16 += SkUTF::ToUTF16(utf32[i], utf16);
-    }
-    srcCount = SkToInt(utf16 - src);
-
-    // If there are any non-bmp code points, the provided 'glyphs' storage will be inadequate.
-    SkAutoSTMalloc<1024, uint16_t> glyphStorage;
-    uint16_t* macGlyphs = glyphs;
-    if (srcCount > count) {
-        macGlyphs = glyphStorage.reset(srcCount);
-    }
-
-    CTFontGetGlyphsForCharacters(fFontRef.get(), src, macGlyphs, srcCount);
-
-    // If there were any non-bmp, then copy and compact.
-    // If all are bmp, 'glyphs' already contains the compact glyphs.
-    // If some are non-bmp, copy and compact into 'glyphs'.
-    if (srcCount > count) {
-        SkASSERT(glyphs != macGlyphs);
-        int extra = 0;
-        for (int i = 0; i < count; ++i) {
-            glyphs[i] = macGlyphs[i + extra];
-            if (SkUTF16_IsLeadingSurrogate(src[i + extra])) {
-                ++extra;
-            }
-        }
-    } else {
-        SkASSERT(glyphs == macGlyphs);
-    }
-}
-
-int SkTypeface_Mac::onCountGlyphs() const {
-    return SkToInt(CTFontGetGlyphCount(fFontRef.get()));
-}
-
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-
-namespace {
-
-struct CTFontVariation {
-    SkUniqueCFRef<CFDictionaryRef> dict;
-    OpszVariation opsz;
-};
-
-/** Creates a dictionary suitable for setting the axes on a CTFont. */
-static CTFontVariation ctvariation_from_skfontarguments(CTFontRef ct,
-                                                        const SkFontArguments& args)
-{
-    OpszVariation opsz;
-    constexpr const SkFourByteTag opszTag = SkSetFourByteTag('o','p','s','z');
-
-    SkUniqueCFRef<CFArrayRef> ctAxes(CTFontCopyVariationAxes(ct));
-    if (!ctAxes) {
-        return CTFontVariation();
-    }
-    CFIndex axisCount = CFArrayGetCount(ctAxes.get());
-
-    const SkFontArguments::VariationPosition position = args.getVariationDesignPosition();
-
-    SkUniqueCFRef<CFMutableDictionaryRef> dict(
-            CFDictionaryCreateMutable(kCFAllocatorDefault, axisCount,
-                                      &kCFTypeDictionaryKeyCallBacks,
-                                      &kCFTypeDictionaryValueCallBacks));
-
-    for (int i = 0; i < axisCount; ++i) {
-        CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes.get(), i);
-        if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
-            return CTFontVariation();
-        }
-        CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
-
-        CFTypeRef tag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey);
-        if (!tag || CFGetTypeID(tag) != CFNumberGetTypeID()) {
-            return CTFontVariation();
-        }
-        CFNumberRef tagNumber = static_cast<CFNumberRef>(tag);
-        int64_t tagLong;
-        if (!CFNumberGetValue(tagNumber, kCFNumberSInt64Type, &tagLong)) {
-            return CTFontVariation();
-        }
-
-        // The variation axes can be set to any value, but cg will effectively pin them.
-        // Pin them here to normalize.
-        CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMinimumValueKey);
-        CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMaximumValueKey);
-        CFTypeRef def = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisDefaultValueKey);
-        if (!min || CFGetTypeID(min) != CFNumberGetTypeID() ||
-            !max || CFGetTypeID(max) != CFNumberGetTypeID() ||
-            !def || CFGetTypeID(def) != CFNumberGetTypeID())
-        {
-            return CTFontVariation();
-        }
-        CFNumberRef minNumber = static_cast<CFNumberRef>(min);
-        CFNumberRef maxNumber = static_cast<CFNumberRef>(max);
-        CFNumberRef defNumber = static_cast<CFNumberRef>(def);
-        double minDouble;
-        double maxDouble;
-        double defDouble;
-        if (!CFNumberGetValue(minNumber, kCFNumberDoubleType, &minDouble) ||
-            !CFNumberGetValue(maxNumber, kCFNumberDoubleType, &maxDouble) ||
-            !CFNumberGetValue(defNumber, kCFNumberDoubleType, &defDouble))
-        {
-            return CTFontVariation();
-        }
-
-        double value = defDouble;
-        // The position may be over specified. If there are multiple values for a given axis,
-        // use the last one since that's what css-fonts-4 requires.
-        for (int j = position.coordinateCount; j --> 0;) {
-            if (position.coordinates[j].axis == tagLong) {
-                value = SkTPin(SkScalarToDouble(position.coordinates[j].value),
-                               minDouble, maxDouble);
-                if (tagLong == opszTag) {
-                    opsz.isSet = true;
-                }
-                break;
-            }
-        }
-        if (tagLong == opszTag) {
-            opsz.value = value;
-        }
-        SkUniqueCFRef<CFNumberRef> valueNumber(
-            CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &value));
-        CFDictionaryAddValue(dict.get(), tagNumber, valueNumber.get());
-    }
-    return { SkUniqueCFRef<CFDictionaryRef>(std::move(dict)), opsz };
-}
-
-/** Creates a dictionary suitable for setting the axes on a CTFont. */
-static CTFontVariation ctvariation_from_skfontdata(CTFontRef ct, SkFontData* fontData) {
-    // In macOS 10.15 CTFontCreate* overrides any 'opsz' variation with the 'size'.
-    // Track the 'opsz' and return it, since it is an out of band axis.
-    OpszVariation opsz;
-    constexpr const SkFourByteTag opszTag = SkSetFourByteTag('o','p','s','z');
-
-    SkUniqueCFRef<CFArrayRef> ctAxes(CTFontCopyVariationAxes(ct));
-    if (!ctAxes) {
-        return CTFontVariation();
-    }
-
-    CFIndex axisCount = CFArrayGetCount(ctAxes.get());
-    if (0 == axisCount || axisCount != fontData->getAxisCount()) {
-        return CTFontVariation();
-    }
-
-    SkUniqueCFRef<CFMutableDictionaryRef> dict(
-            CFDictionaryCreateMutable(kCFAllocatorDefault, axisCount,
-                                      &kCFTypeDictionaryKeyCallBacks,
-                                      &kCFTypeDictionaryValueCallBacks));
-
-    for (int i = 0; i < fontData->getAxisCount(); ++i) {
-        CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes.get(), i);
-        if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
-            return CTFontVariation();
-        }
-        CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
-
-        CFTypeRef tag = CFDictionaryGetValue(axisInfoDict,
-                                             kCTFontVariationAxisIdentifierKey);
-        if (!tag || CFGetTypeID(tag) != CFNumberGetTypeID()) {
-            return CTFontVariation();
-        }
-        CFNumberRef tagNumber = static_cast<CFNumberRef>(tag);
-        int64_t tagLong;
-        if (!CFNumberGetValue(tagNumber, kCFNumberSInt64Type, &tagLong)) {
-            return CTFontVariation();
-        }
-
-        // The variation axes can be set to any value, but cg will effectively pin them.
-        // Pin them here to normalize.
-        CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMinimumValueKey);
-        CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMaximumValueKey);
-        if (!min || CFGetTypeID(min) != CFNumberGetTypeID() ||
-            !max || CFGetTypeID(max) != CFNumberGetTypeID())
-        {
-            return CTFontVariation();
-        }
-        CFNumberRef minNumber = static_cast<CFNumberRef>(min);
-        CFNumberRef maxNumber = static_cast<CFNumberRef>(max);
-        double minDouble;
-        double maxDouble;
-        if (!CFNumberGetValue(minNumber, kCFNumberDoubleType, &minDouble) ||
-            !CFNumberGetValue(maxNumber, kCFNumberDoubleType, &maxDouble))
-        {
-            return CTFontVariation();
-        }
-        double value = SkTPin(SkFixedToDouble(fontData->getAxis()[i]), minDouble, maxDouble);
-
-        if (tagLong == opszTag) {
-            opsz.isSet = true;
-            opsz.value = value;
-        }
-
-        SkUniqueCFRef<CFNumberRef> valueNumber(
-                CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &value));
-        CFDictionaryAddValue(dict.get(), tagNumber, valueNumber.get());
-    }
-    return { SkUniqueCFRef<CFDictionaryRef>(std::move(dict)), opsz };
-}
-
-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) {
-        return false;
-    }
-    CFStringToSkString(ref.get(), value);
-    return true;
-}
-
-static inline int sqr(int value) {
-    SkASSERT(SkAbs32(value) < 0x7FFF);  // check for overflow
-    return value * value;
-}
-
-// We normalize each axis (weight, width, italic) to be base-900
-static int compute_metric(const SkFontStyle& a, const SkFontStyle& b) {
-    return sqr(a.weight() - b.weight()) +
-           sqr((a.width() - b.width()) * 100) +
-           sqr((a.slant() != b.slant()) * 900);
-}
-
-class SkFontStyleSet_Mac : public SkFontStyleSet {
-public:
-    SkFontStyleSet_Mac(CTFontDescriptorRef desc)
-        : fArray(CTFontDescriptorCreateMatchingFontDescriptors(desc, nullptr))
-        , fCount(0)
-    {
-        if (!fArray) {
-            fArray.reset(CFArrayCreate(nullptr, nullptr, 0, nullptr));
-        }
-        fCount = SkToInt(CFArrayGetCount(fArray.get()));
-    }
-
-    int count() override {
-        return fCount;
-    }
-
-    void getStyle(int index, SkFontStyle* style, SkString* name) override {
-        SkASSERT((unsigned)index < (unsigned)fCount);
-        CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray.get(), index);
-        if (style) {
-            *style = fontstyle_from_descriptor(desc, false);
-        }
-        if (name) {
-            if (!find_desc_str(desc, kCTFontStyleNameAttribute, name)) {
-                name->reset();
-            }
-        }
-    }
-
-    SkTypeface* createTypeface(int index) override {
-        SkASSERT((unsigned)index < (unsigned)CFArrayGetCount(fArray.get()));
-        CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray.get(), index);
-
-        return create_from_desc(desc).release();
-    }
-
-    SkTypeface* matchStyle(const SkFontStyle& pattern) override {
-        if (0 == fCount) {
-            return nullptr;
-        }
-        return create_from_desc(findMatchingDesc(pattern)).release();
-    }
-
-private:
-    SkUniqueCFRef<CFArrayRef> fArray;
-    int fCount;
-
-    CTFontDescriptorRef findMatchingDesc(const SkFontStyle& pattern) const {
-        int bestMetric = SK_MaxS32;
-        CTFontDescriptorRef bestDesc = nullptr;
-
-        for (int i = 0; i < fCount; ++i) {
-            CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray.get(), i);
-            int metric = compute_metric(pattern, fontstyle_from_descriptor(desc, false));
-            if (0 == metric) {
-                return desc;
-            }
-            if (metric < bestMetric) {
-                bestMetric = metric;
-                bestDesc = desc;
-            }
-        }
-        SkASSERT(bestDesc);
-        return bestDesc;
-    }
-};
-
-} // namespace
-
-sk_sp<SkTypeface> SkTypeface_Mac::onMakeClone(const SkFontArguments& args) const {
-    CTFontVariation ctVariation = ctvariation_from_skfontarguments(fFontRef.get(), args);
-
-    SkUniqueCFRef<CTFontRef> ctVariant;
-    if (ctVariation.dict) {
-        SkUniqueCFRef<CFMutableDictionaryRef> attributes(
-                CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
-                                          &kCFTypeDictionaryKeyCallBacks,
-                                          &kCFTypeDictionaryValueCallBacks));
-        CFDictionaryAddValue(attributes.get(),
-                             kCTFontVariationAttribute, ctVariation.dict.get());
-        SkUniqueCFRef<CTFontDescriptorRef> varDesc(
-                CTFontDescriptorCreateWithAttributes(attributes.get()));
-        ctVariant.reset(CTFontCreateCopyWithAttributes(fFontRef.get(), 0, nullptr, varDesc.get()));
-    } else {
-        ctVariant.reset((CTFontRef)CFRetain(fFontRef.get()));
-    }
-    if (!ctVariant) {
-        return nullptr;
-    }
-
-    return create_from_CTFontRef(std::move(ctVariant), ctVariation.opsz,
-                                 fStream ? fStream->duplicate() : nullptr);
-}
-
-int SkTypeface_Mac::onGetVariationDesignParameters(SkFontParameters::Variation::Axis parameters[],
-                                                   int parameterCount) const
-{
-    SkUniqueCFRef<CFArrayRef> ctAxes(CTFontCopyVariationAxes(fFontRef.get()));
-    if (!ctAxes) {
-        return -1;
-    }
-    CFIndex axisCount = CFArrayGetCount(ctAxes.get());
-
-    if (!parameters || parameterCount < axisCount) {
-        return axisCount;
-    }
-
-    // Added in 10.13
-    CFStringRef* kCTFontVariationAxisHiddenKeyPtr =
-            static_cast<CFStringRef*>(dlsym(RTLD_DEFAULT, "kCTFontVariationAxisHiddenKey"));
-
-    for (int i = 0; i < axisCount; ++i) {
-        CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes.get(), i);
-        if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
-            return -1;
-        }
-        CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
-
-        CFTypeRef tag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey);
-        if (!tag || CFGetTypeID(tag) != CFNumberGetTypeID()) {
-            return -1;
-        }
-        CFNumberRef tagNumber = static_cast<CFNumberRef>(tag);
-        int64_t tagLong;
-        if (!CFNumberGetValue(tagNumber, kCFNumberSInt64Type, &tagLong)) {
-            return -1;
-        }
-
-        CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMinimumValueKey);
-        CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMaximumValueKey);
-        CFTypeRef def = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisDefaultValueKey);
-        if (!min || CFGetTypeID(min) != CFNumberGetTypeID() ||
-            !max || CFGetTypeID(max) != CFNumberGetTypeID() ||
-            !def || CFGetTypeID(def) != CFNumberGetTypeID())
-        {
-            return -1;
-        }
-        CFNumberRef minNumber = static_cast<CFNumberRef>(min);
-        CFNumberRef maxNumber = static_cast<CFNumberRef>(max);
-        CFNumberRef defNumber = static_cast<CFNumberRef>(def);
-        double minDouble;
-        double maxDouble;
-        double defDouble;
-        if (!CFNumberGetValue(minNumber, kCFNumberDoubleType, &minDouble) ||
-            !CFNumberGetValue(maxNumber, kCFNumberDoubleType, &maxDouble) ||
-            !CFNumberGetValue(defNumber, kCFNumberDoubleType, &defDouble))
-        {
-            return -1;
-        }
-
-        SkFontParameters::Variation::Axis& skAxis = parameters[i];
-        skAxis.tag = tagLong;
-        skAxis.min = minDouble;
-        skAxis.max = maxDouble;
-        skAxis.def = defDouble;
-        skAxis.setHidden(false);
-        if (kCTFontVariationAxisHiddenKeyPtr) {
-            CFTypeRef hidden = CFDictionaryGetValue(axisInfoDict,*kCTFontVariationAxisHiddenKeyPtr);
-            if (hidden) {
-                if (CFGetTypeID(hidden) != CFBooleanGetTypeID()) {
-                    return -1;
-                }
-                CFBooleanRef hiddenBoolean = static_cast<CFBooleanRef>(hidden);
-                skAxis.setHidden(CFBooleanGetValue(hiddenBoolean));
-            }
-        }
-    }
-    return axisCount;
-}
-
-class SkFontMgr_Mac : public SkFontMgr {
-    SkUniqueCFRef<CFArrayRef> fNames;
-    int fCount;
-
-    CFStringRef getFamilyNameAt(int index) const {
-        SkASSERT((unsigned)index < (unsigned)fCount);
-        return (CFStringRef)CFArrayGetValueAtIndex(fNames.get(), index);
-    }
-
-    static SkFontStyleSet* CreateSet(CFStringRef cfFamilyName) {
-        SkUniqueCFRef<CFMutableDictionaryRef> cfAttr(
-                 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
-                                           &kCFTypeDictionaryKeyCallBacks,
-                                           &kCFTypeDictionaryValueCallBacks));
-
-        CFDictionaryAddValue(cfAttr.get(), kCTFontFamilyNameAttribute, cfFamilyName);
-
-        SkUniqueCFRef<CTFontDescriptorRef> desc(
-                CTFontDescriptorCreateWithAttributes(cfAttr.get()));
-        return new SkFontStyleSet_Mac(desc.get());
-    }
-
-    /** CTFontManagerCopyAvailableFontFamilyNames() is not always available, so we
-     *  provide a wrapper here that will return an empty array if need be.
-     */
-    static SkUniqueCFRef<CFArrayRef> CopyAvailableFontFamilyNames() {
-#ifdef SK_BUILD_FOR_IOS
-        return SkUniqueCFRef<CFArrayRef>(CFArrayCreate(nullptr, nullptr, 0, nullptr));
-#else
-        return SkUniqueCFRef<CFArrayRef>(CTFontManagerCopyAvailableFontFamilyNames());
-#endif
-    }
-
-public:
-    SkFontMgr_Mac()
-        : fNames(CopyAvailableFontFamilyNames())
-        , fCount(fNames ? SkToInt(CFArrayGetCount(fNames.get())) : 0) {}
-
-protected:
-    int onCountFamilies() const override {
-        return fCount;
-    }
-
-    void onGetFamilyName(int index, SkString* familyName) const override {
-        if ((unsigned)index < (unsigned)fCount) {
-            CFStringToSkString(this->getFamilyNameAt(index), familyName);
-        } else {
-            familyName->reset();
-        }
-    }
-
-    SkFontStyleSet* onCreateStyleSet(int index) const override {
-        if ((unsigned)index >= (unsigned)fCount) {
-            return nullptr;
-        }
-        return CreateSet(this->getFamilyNameAt(index));
-    }
-
-    SkFontStyleSet* onMatchFamily(const char familyName[]) const override {
-        if (!familyName) {
-            return nullptr;
-        }
-        SkUniqueCFRef<CFStringRef> cfName = make_CFString(familyName);
-        return CreateSet(cfName.get());
-    }
-
-    SkTypeface* onMatchFamilyStyle(const char familyName[],
-                                   const SkFontStyle& style) const override {
-        SkUniqueCFRef<CTFontDescriptorRef> desc = create_descriptor(familyName, style);
-        return create_from_desc(desc.get()).release();
-    }
-
-    SkTypeface* onMatchFamilyStyleCharacter(const char familyName[],
-                                            const SkFontStyle& style,
-                                            const char* bcp47[], int bcp47Count,
-                                            SkUnichar character) const override {
-        SkUniqueCFRef<CTFontDescriptorRef> desc = create_descriptor(familyName, style);
-        SkUniqueCFRef<CTFontRef> familyFont(CTFontCreateWithFontDescriptor(desc.get(), 0, nullptr));
-
-        // kCFStringEncodingUTF32 is BE unless there is a BOM.
-        // Since there is no machine endian option, explicitly state machine endian.
-#ifdef SK_CPU_LENDIAN
-        constexpr CFStringEncoding encoding = kCFStringEncodingUTF32LE;
-#else
-        constexpr CFStringEncoding encoding = kCFStringEncodingUTF32BE;
-#endif
-        SkUniqueCFRef<CFStringRef> string(CFStringCreateWithBytes(
-                kCFAllocatorDefault, reinterpret_cast<const UInt8 *>(&character), sizeof(character),
-                encoding, false));
-        // If 0xD800 <= codepoint <= 0xDFFF || 0x10FFFF < codepoint 'string' may be nullptr.
-        // No font should be covering such codepoints (even the magic fallback font).
-        if (!string) {
-            return nullptr;
-        }
-        CFRange range = CFRangeMake(0, CFStringGetLength(string.get()));  // in UniChar units.
-        SkUniqueCFRef<CTFontRef> fallbackFont(
-                CTFontCreateForString(familyFont.get(), string.get(), range));
-        return create_from_CTFontRef(std::move(fallbackFont), OpszVariation(), nullptr).release();
-    }
-
-    SkTypeface* onMatchFaceStyle(const SkTypeface* familyMember,
-                                 const SkFontStyle&) const override {
-        return nullptr;
-    }
-
-    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 create_from_CTFontRef(std::move(ct), OpszVariation(),
-                                     SkMemoryStream::Make(std::move(data)));
-    }
-
-    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 create_from_CTFontRef(std::move(ct), OpszVariation(), std::move(stream));
-    }
-
-    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;
-        }
-
-        CTFontVariation ctVariation = ctvariation_from_skfontarguments(ct.get(), args);
-
-        SkUniqueCFRef<CTFontRef> ctVariant;
-        if (ctVariation.dict) {
-            SkUniqueCFRef<CFMutableDictionaryRef> attributes(
-                    CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
-                                              &kCFTypeDictionaryKeyCallBacks,
-                                              &kCFTypeDictionaryValueCallBacks));
-            CFDictionaryAddValue(attributes.get(),
-                                 kCTFontVariationAttribute, ctVariation.dict.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 create_from_CTFontRef(std::move(ctVariant), ctVariation.opsz,
-                                     std::move(stream));
-    }
-
-    sk_sp<SkTypeface> onMakeFromFontData(std::unique_ptr<SkFontData> fontData) const override {
-        // TODO: Use CTFontManagerCreateFontDescriptorsFromData when available.
-        if (fontData->getIndex() != 0) {
-            return nullptr;
-        }
-
-        sk_sp<SkData> data = skdata_from_skstreamasset(fontData->getStream()->duplicate());
-        if (!data) {
-            return nullptr;
-        }
-        SkUniqueCFRef<CTFontRef> ct = ctfont_from_skdata(std::move(data), fontData->getIndex());
-        if (!ct) {
-            return nullptr;
-        }
-
-        CTFontVariation ctVariation = ctvariation_from_skfontdata(ct.get(), fontData.get());
-
-        SkUniqueCFRef<CTFontRef> ctVariant;
-        if (ctVariation.dict) {
-            SkUniqueCFRef<CFMutableDictionaryRef> attributes(
-                    CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
-                                              &kCFTypeDictionaryKeyCallBacks,
-                                              &kCFTypeDictionaryValueCallBacks));
-            CFDictionaryAddValue(attributes.get(),
-                                 kCTFontVariationAttribute, ctVariation.dict.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 create_from_CTFontRef(std::move(ctVariant), ctVariation.opsz,
-                                     fontData->detachStream());
-    }
-
-    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;
-        }
-
-        return this->onMakeFromData(std::move(data), ttcIndex);
-    }
-
-    sk_sp<SkTypeface> onLegacyMakeTypeface(const char familyName[], SkFontStyle style) const override {
-        if (familyName) {
-            familyName = map_css_names(familyName);
-        }
-
-        sk_sp<SkTypeface> face = create_from_name(familyName, style);
-        if (face) {
-            return face;
-        }
-
-        static SkTypeface* gDefaultFace;
-        static SkOnce lookupDefault;
-        static const char FONT_DEFAULT_NAME[] = "Lucida Sans";
-        lookupDefault([]{
-            gDefaultFace = create_from_name(FONT_DEFAULT_NAME, SkFontStyle()).release();
-        });
-        return sk_ref_sp(gDefaultFace);
-    }
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-sk_sp<SkFontMgr> SkFontMgr::Factory() { return sk_make_sp<SkFontMgr_Mac>(); }
-
-#endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
+// All prior contents of this file have been divided among the following files.
+// This file remains while builds are migrated.
+#include "src/ports/SkFontMgr_mac_ct.cpp"
+#include "src/ports/SkTypeface_mac_ct.cpp"
+#include "src/ports/SkScalerContext_mac_ct.cpp"
+#include "src/ports/SkFontMgr_mac_ct_factory.cpp"
diff --git a/src/ports/SkFontMgr_mac_ct.cpp b/src/ports/SkFontMgr_mac_ct.cpp
new file mode 100644
index 0000000..c362b16
--- /dev/null
+++ b/src/ports/SkFontMgr_mac_ct.cpp
@@ -0,0 +1,669 @@
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "include/core/SkTypes.h"
+#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
+
+#ifdef SK_BUILD_FOR_MAC
+#import <ApplicationServices/ApplicationServices.h>
+#endif
+
+#ifdef SK_BUILD_FOR_IOS
+#include <CoreText/CoreText.h>
+#include <CoreText/CTFontManager.h>
+#include <CoreGraphics/CoreGraphics.h>
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
+#include "include/core/SkData.h"
+#include "include/core/SkFontArguments.h"
+#include "include/core/SkFontMgr.h"
+#include "include/core/SkFontStyle.h"
+#include "include/core/SkStream.h"
+#include "include/core/SkString.h"
+#include "include/core/SkTypeface.h"
+#include "include/ports/SkFontMgr_mac_ct.h"
+#include "include/private/SkFixed.h"
+#include "include/private/SkOnce.h"
+#include "include/private/SkTemplates.h"
+#include "include/private/SkTo.h"
+#include "src/core/SkFontDescriptor.h"
+#include "src/ports/SkTypeface_mac_ct.h"
+#include "src/utils/SkUTF.h"
+
+#include <string.h>
+#include <memory>
+
+static SkUniqueCFRef<CFStringRef> make_CFString(const char s[]) {
+    return SkUniqueCFRef<CFStringRef>(CFStringCreateWithCString(nullptr, s, kCFStringEncodingUTF8));
+}
+
+/** Creates a typeface from a descriptor, searching the cache. */
+static sk_sp<SkTypeface> create_from_desc(CTFontDescriptorRef desc) {
+    SkUniqueCFRef<CTFontRef> ctFont(CTFontCreateWithFontDescriptor(desc, 0, nullptr));
+    if (!ctFont) {
+        return nullptr;
+    }
+
+    return SkTypeface_Mac::Make(std::move(ctFont), OpszVariation(), nullptr);
+}
+
+static SkUniqueCFRef<CTFontDescriptorRef> create_descriptor(const char familyName[],
+                                                            const SkFontStyle& style) {
+    SkUniqueCFRef<CFMutableDictionaryRef> cfAttributes(
+            CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+                                      &kCFTypeDictionaryKeyCallBacks,
+                                      &kCFTypeDictionaryValueCallBacks));
+
+    SkUniqueCFRef<CFMutableDictionaryRef> cfTraits(
+            CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+                                      &kCFTypeDictionaryKeyCallBacks,
+                                      &kCFTypeDictionaryValueCallBacks));
+
+    if (!cfAttributes || !cfTraits) {
+        return nullptr;
+    }
+
+    // TODO(crbug.com/1018581) Some CoreText versions have errant behavior when
+    // certain traits set.  Temporary workaround to omit specifying trait for
+    // those versions.
+    // Long term solution will involve serializing typefaces instead of relying
+    // upon this to match between processes.
+    //
+    // Compare CoreText.h in an up to date SDK for where these values come from.
+    static const uint32_t kSkiaLocalCTVersionNumber10_14 = 0x000B0000;
+    static const uint32_t kSkiaLocalCTVersionNumber10_15 = 0x000C0000;
+
+    // CTFontTraits (symbolic)
+    // macOS 14 and iOS 12 seem to behave badly when kCTFontSymbolicTrait is set.
+    // macOS 15 yields LastResort font instead of a good default font when
+    // kCTFontSymbolicTrait is set.
+    if (!(&CTGetCoreTextVersion && CTGetCoreTextVersion() >= kSkiaLocalCTVersionNumber10_14)) {
+        CTFontSymbolicTraits ctFontTraits = 0;
+        if (style.weight() >= SkFontStyle::kBold_Weight) {
+            ctFontTraits |= kCTFontBoldTrait;
+        }
+        if (style.slant() != SkFontStyle::kUpright_Slant) {
+            ctFontTraits |= kCTFontItalicTrait;
+        }
+        SkUniqueCFRef<CFNumberRef> cfFontTraits(
+                CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &ctFontTraits));
+        if (cfFontTraits) {
+            CFDictionaryAddValue(cfTraits.get(), kCTFontSymbolicTrait, cfFontTraits.get());
+        }
+    }
+
+    // CTFontTraits (weight)
+    CGFloat ctWeight = SkCTFontCTWeightForCSSWeight(style.weight());
+    SkUniqueCFRef<CFNumberRef> cfFontWeight(
+            CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &ctWeight));
+    if (cfFontWeight) {
+        CFDictionaryAddValue(cfTraits.get(), kCTFontWeightTrait, cfFontWeight.get());
+    }
+    // CTFontTraits (width)
+    CGFloat ctWidth = SkCTFontCTWidthForCSSWidth(style.width());
+    SkUniqueCFRef<CFNumberRef> cfFontWidth(
+            CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &ctWidth));
+    if (cfFontWidth) {
+        CFDictionaryAddValue(cfTraits.get(), kCTFontWidthTrait, cfFontWidth.get());
+    }
+    // CTFontTraits (slant)
+    // macOS 15 behaves badly when kCTFontSlantTrait is set.
+    if (!(&CTGetCoreTextVersion && CTGetCoreTextVersion() == kSkiaLocalCTVersionNumber10_15)) {
+        CGFloat ctSlant = style.slant() == SkFontStyle::kUpright_Slant ? 0 : 1;
+        SkUniqueCFRef<CFNumberRef> cfFontSlant(
+                CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &ctSlant));
+        if (cfFontSlant) {
+            CFDictionaryAddValue(cfTraits.get(), kCTFontSlantTrait, cfFontSlant.get());
+        }
+    }
+    // CTFontTraits
+    CFDictionaryAddValue(cfAttributes.get(), kCTFontTraitsAttribute, cfTraits.get());
+
+    // CTFontFamilyName
+    if (familyName) {
+        SkUniqueCFRef<CFStringRef> cfFontName = make_CFString(familyName);
+        if (cfFontName) {
+            CFDictionaryAddValue(cfAttributes.get(), kCTFontFamilyNameAttribute, cfFontName.get());
+        }
+    }
+
+    return SkUniqueCFRef<CTFontDescriptorRef>(
+            CTFontDescriptorCreateWithAttributes(cfAttributes.get()));
+}
+
+// Same as the above function except style is included so we can
+// compare whether the created font conforms to the style. If not, we need
+// to recreate the font with symbolic traits. This is needed due to MacOS 10.11
+// font creation problem https://bugs.chromium.org/p/skia/issues/detail?id=8447.
+static sk_sp<SkTypeface> create_from_desc_and_style(CTFontDescriptorRef desc,
+                                                    const SkFontStyle& style) {
+    SkUniqueCFRef<CTFontRef> ctFont(CTFontCreateWithFontDescriptor(desc, 0, nullptr));
+    if (!ctFont) {
+        return nullptr;
+    }
+
+    const CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(ctFont.get());
+    CTFontSymbolicTraits expected_traits = traits;
+    if (style.slant() != SkFontStyle::kUpright_Slant) {
+        expected_traits |= kCTFontItalicTrait;
+    }
+    if (style.weight() >= SkFontStyle::kBold_Weight) {
+        expected_traits |= kCTFontBoldTrait;
+    }
+
+    if (expected_traits != traits) {
+        SkUniqueCFRef<CTFontRef> ctNewFont(CTFontCreateCopyWithSymbolicTraits(
+                    ctFont.get(), 0, nullptr, expected_traits, expected_traits));
+        if (ctNewFont) {
+            ctFont = std::move(ctNewFont);
+        }
+    }
+
+    return SkTypeface_Mac::Make(std::move(ctFont), OpszVariation(), nullptr);
+}
+
+/** Creates a typeface from a name, searching the cache. */
+static sk_sp<SkTypeface> create_from_name(const char familyName[], const SkFontStyle& style) {
+    SkUniqueCFRef<CTFontDescriptorRef> desc = create_descriptor(familyName, style);
+    if (!desc) {
+        return nullptr;
+    }
+    return create_from_desc_and_style(desc.get(), style);
+}
+
+static const char* map_css_names(const char* name) {
+    static const struct {
+        const char* fFrom;  // name the caller specified
+        const char* fTo;    // "canonical" name we map to
+    } gPairs[] = {
+        { "sans-serif", "Helvetica" },
+        { "serif",      "Times"     },
+        { "monospace",  "Courier"   }
+    };
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) {
+        if (strcmp(name, gPairs[i].fFrom) == 0) {
+            return gPairs[i].fTo;
+        }
+    }
+    return name;    // no change
+}
+
+namespace {
+
+/** Creates a dictionary suitable for setting the axes on a CTFont. */
+static CTFontVariation ctvariation_from_skfontdata(CTFontRef ct, SkFontData* fontData) {
+    // In macOS 10.15 CTFontCreate* overrides any 'opsz' variation with the 'size'.
+    // Track the 'opsz' and return it, since it is an out of band axis.
+    OpszVariation opsz;
+    constexpr const SkFourByteTag opszTag = SkSetFourByteTag('o','p','s','z');
+
+    SkUniqueCFRef<CFArrayRef> ctAxes(CTFontCopyVariationAxes(ct));
+    if (!ctAxes) {
+        return CTFontVariation();
+    }
+
+    CFIndex axisCount = CFArrayGetCount(ctAxes.get());
+    if (0 == axisCount || axisCount != fontData->getAxisCount()) {
+        return CTFontVariation();
+    }
+
+    SkUniqueCFRef<CFMutableDictionaryRef> dict(
+            CFDictionaryCreateMutable(kCFAllocatorDefault, axisCount,
+                                      &kCFTypeDictionaryKeyCallBacks,
+                                      &kCFTypeDictionaryValueCallBacks));
+
+    for (int i = 0; i < fontData->getAxisCount(); ++i) {
+        CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes.get(), i);
+        if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
+            return CTFontVariation();
+        }
+        CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
+
+        CFTypeRef tag = CFDictionaryGetValue(axisInfoDict,
+                                             kCTFontVariationAxisIdentifierKey);
+        if (!tag || CFGetTypeID(tag) != CFNumberGetTypeID()) {
+            return CTFontVariation();
+        }
+        CFNumberRef tagNumber = static_cast<CFNumberRef>(tag);
+        int64_t tagLong;
+        if (!CFNumberGetValue(tagNumber, kCFNumberSInt64Type, &tagLong)) {
+            return CTFontVariation();
+        }
+
+        // The variation axes can be set to any value, but cg will effectively pin them.
+        // Pin them here to normalize.
+        CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMinimumValueKey);
+        CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMaximumValueKey);
+        if (!min || CFGetTypeID(min) != CFNumberGetTypeID() ||
+            !max || CFGetTypeID(max) != CFNumberGetTypeID())
+        {
+            return CTFontVariation();
+        }
+        CFNumberRef minNumber = static_cast<CFNumberRef>(min);
+        CFNumberRef maxNumber = static_cast<CFNumberRef>(max);
+        double minDouble;
+        double maxDouble;
+        if (!CFNumberGetValue(minNumber, kCFNumberDoubleType, &minDouble) ||
+            !CFNumberGetValue(maxNumber, kCFNumberDoubleType, &maxDouble))
+        {
+            return CTFontVariation();
+        }
+        double value = SkTPin(SkFixedToDouble(fontData->getAxis()[i]), minDouble, maxDouble);
+
+        if (tagLong == opszTag) {
+            opsz.isSet = true;
+            opsz.value = value;
+        }
+
+        SkUniqueCFRef<CFNumberRef> valueNumber(
+                CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &value));
+        CFDictionaryAddValue(dict.get(), tagNumber, valueNumber.get());
+    }
+    return { SkUniqueCFRef<CFDictionaryRef>(std::move(dict)), opsz };
+}
+
+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) {
+        return false;
+    }
+    SkStringFromCFString(ref.get(), value);
+    return true;
+}
+
+static inline int sqr(int value) {
+    SkASSERT(SkAbs32(value) < 0x7FFF);  // check for overflow
+    return value * value;
+}
+
+// We normalize each axis (weight, width, italic) to be base-900
+static int compute_metric(const SkFontStyle& a, const SkFontStyle& b) {
+    return sqr(a.weight() - b.weight()) +
+           sqr((a.width() - b.width()) * 100) +
+           sqr((a.slant() != b.slant()) * 900);
+}
+
+class SkFontStyleSet_Mac : public SkFontStyleSet {
+public:
+    SkFontStyleSet_Mac(CTFontDescriptorRef desc)
+        : fArray(CTFontDescriptorCreateMatchingFontDescriptors(desc, nullptr))
+        , fCount(0)
+    {
+        if (!fArray) {
+            fArray.reset(CFArrayCreate(nullptr, nullptr, 0, nullptr));
+        }
+        fCount = SkToInt(CFArrayGetCount(fArray.get()));
+    }
+
+    int count() override {
+        return fCount;
+    }
+
+    void getStyle(int index, SkFontStyle* style, SkString* name) override {
+        SkASSERT((unsigned)index < (unsigned)fCount);
+        CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray.get(), index);
+        if (style) {
+            *style = SkCTFontDescriptorGetSkFontStyle(desc, false);
+        }
+        if (name) {
+            if (!find_desc_str(desc, kCTFontStyleNameAttribute, name)) {
+                name->reset();
+            }
+        }
+    }
+
+    SkTypeface* createTypeface(int index) override {
+        SkASSERT((unsigned)index < (unsigned)CFArrayGetCount(fArray.get()));
+        CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray.get(), index);
+
+        return create_from_desc(desc).release();
+    }
+
+    SkTypeface* matchStyle(const SkFontStyle& pattern) override {
+        if (0 == fCount) {
+            return nullptr;
+        }
+        return create_from_desc(findMatchingDesc(pattern)).release();
+    }
+
+private:
+    SkUniqueCFRef<CFArrayRef> fArray;
+    int fCount;
+
+    CTFontDescriptorRef findMatchingDesc(const SkFontStyle& pattern) const {
+        int bestMetric = SK_MaxS32;
+        CTFontDescriptorRef bestDesc = nullptr;
+
+        for (int i = 0; i < fCount; ++i) {
+            CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray.get(), i);
+            int metric = compute_metric(pattern, SkCTFontDescriptorGetSkFontStyle(desc, false));
+            if (0 == metric) {
+                return desc;
+            }
+            if (metric < bestMetric) {
+                bestMetric = metric;
+                bestDesc = desc;
+            }
+        }
+        SkASSERT(bestDesc);
+        return bestDesc;
+    }
+};
+
+} // namespace
+
+class SkFontMgr_Mac : public SkFontMgr {
+    SkUniqueCFRef<CFArrayRef> fNames;
+    int fCount;
+
+    CFStringRef getFamilyNameAt(int index) const {
+        SkASSERT((unsigned)index < (unsigned)fCount);
+        return (CFStringRef)CFArrayGetValueAtIndex(fNames.get(), index);
+    }
+
+    static SkFontStyleSet* CreateSet(CFStringRef cfFamilyName) {
+        SkUniqueCFRef<CFMutableDictionaryRef> cfAttr(
+                 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+                                           &kCFTypeDictionaryKeyCallBacks,
+                                           &kCFTypeDictionaryValueCallBacks));
+
+        CFDictionaryAddValue(cfAttr.get(), kCTFontFamilyNameAttribute, cfFamilyName);
+
+        SkUniqueCFRef<CTFontDescriptorRef> desc(
+                CTFontDescriptorCreateWithAttributes(cfAttr.get()));
+        return new SkFontStyleSet_Mac(desc.get());
+    }
+
+    /** CTFontManagerCopyAvailableFontFamilyNames() is not always available, so we
+     *  provide a wrapper here that will return an empty array if need be.
+     */
+    static SkUniqueCFRef<CFArrayRef> CopyAvailableFontFamilyNames() {
+#ifdef SK_BUILD_FOR_IOS
+        return SkUniqueCFRef<CFArrayRef>(CFArrayCreate(nullptr, nullptr, 0, nullptr));
+#else
+        return SkUniqueCFRef<CFArrayRef>(CTFontManagerCopyAvailableFontFamilyNames());
+#endif
+    }
+
+public:
+    SkUniqueCFRef<CTFontCollectionRef> fFontCollection;
+    SkFontMgr_Mac(CTFontCollectionRef fontCollection)
+        : fNames(CopyAvailableFontFamilyNames())
+        , fCount(fNames ? SkToInt(CFArrayGetCount(fNames.get())) : 0)
+        , fFontCollection(fontCollection ? (CTFontCollectionRef)CFRetain(fontCollection)
+                                         : CTFontCollectionCreateFromAvailableFonts(nullptr))
+    {}
+
+protected:
+    int onCountFamilies() const override {
+        return fCount;
+    }
+
+    void onGetFamilyName(int index, SkString* familyName) const override {
+        if ((unsigned)index < (unsigned)fCount) {
+            SkStringFromCFString(this->getFamilyNameAt(index), familyName);
+        } else {
+            familyName->reset();
+        }
+    }
+
+    SkFontStyleSet* onCreateStyleSet(int index) const override {
+        if ((unsigned)index >= (unsigned)fCount) {
+            return nullptr;
+        }
+        return CreateSet(this->getFamilyNameAt(index));
+    }
+
+    SkFontStyleSet* onMatchFamily(const char familyName[]) const override {
+        if (!familyName) {
+            return nullptr;
+        }
+        SkUniqueCFRef<CFStringRef> cfName = make_CFString(familyName);
+        return CreateSet(cfName.get());
+    }
+
+    SkTypeface* onMatchFamilyStyle(const char familyName[],
+                                   const SkFontStyle& style) const override {
+        SkUniqueCFRef<CTFontDescriptorRef> desc = create_descriptor(familyName, style);
+        return create_from_desc(desc.get()).release();
+    }
+
+    SkTypeface* onMatchFamilyStyleCharacter(const char familyName[],
+                                            const SkFontStyle& style,
+                                            const char* bcp47[], int bcp47Count,
+                                            SkUnichar character) const override {
+        SkUniqueCFRef<CTFontDescriptorRef> desc = create_descriptor(familyName, style);
+        SkUniqueCFRef<CTFontRef> familyFont(CTFontCreateWithFontDescriptor(desc.get(), 0, nullptr));
+
+        // kCFStringEncodingUTF32 is BE unless there is a BOM.
+        // Since there is no machine endian option, explicitly state machine endian.
+#ifdef SK_CPU_LENDIAN
+        constexpr CFStringEncoding encoding = kCFStringEncodingUTF32LE;
+#else
+        constexpr CFStringEncoding encoding = kCFStringEncodingUTF32BE;
+#endif
+        SkUniqueCFRef<CFStringRef> string(CFStringCreateWithBytes(
+                kCFAllocatorDefault, reinterpret_cast<const UInt8 *>(&character), sizeof(character),
+                encoding, false));
+        // If 0xD800 <= codepoint <= 0xDFFF || 0x10FFFF < codepoint 'string' may be nullptr.
+        // No font should be covering such codepoints (even the magic fallback font).
+        if (!string) {
+            return nullptr;
+        }
+        CFRange range = CFRangeMake(0, CFStringGetLength(string.get()));  // in UniChar units.
+        SkUniqueCFRef<CTFontRef> fallbackFont(
+                CTFontCreateForString(familyFont.get(), string.get(), range));
+        return SkTypeface_Mac::Make(std::move(fallbackFont), OpszVariation(), nullptr).release();
+    }
+
+    SkTypeface* onMatchFaceStyle(const SkTypeface* familyMember,
+                                 const SkFontStyle&) const override {
+        return nullptr;
+    }
+
+    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)));
+    }
+
+    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));
+    }
+
+    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;
+        }
+
+        CTFontVariation ctVariation = SkCTVariationFromSkFontArguments(ct.get(), args);
+
+        SkUniqueCFRef<CTFontRef> ctVariant;
+        if (ctVariation.dict) {
+            SkUniqueCFRef<CFMutableDictionaryRef> attributes(
+                    CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+                                              &kCFTypeDictionaryKeyCallBacks,
+                                              &kCFTypeDictionaryValueCallBacks));
+            CFDictionaryAddValue(attributes.get(),
+                                 kCTFontVariationAttribute, ctVariation.dict.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));
+    }
+
+    sk_sp<SkTypeface> onMakeFromFontData(std::unique_ptr<SkFontData> fontData) const override {
+        // TODO: Use CTFontManagerCreateFontDescriptorsFromData when available.
+        if (fontData->getIndex() != 0) {
+            return nullptr;
+        }
+
+        sk_sp<SkData> data = skdata_from_skstreamasset(fontData->getStream()->duplicate());
+        if (!data) {
+            return nullptr;
+        }
+        SkUniqueCFRef<CTFontRef> ct = ctfont_from_skdata(std::move(data), fontData->getIndex());
+        if (!ct) {
+            return nullptr;
+        }
+
+        CTFontVariation ctVariation = ctvariation_from_skfontdata(ct.get(), fontData.get());
+
+        SkUniqueCFRef<CTFontRef> ctVariant;
+        if (ctVariation.dict) {
+            SkUniqueCFRef<CFMutableDictionaryRef> attributes(
+                    CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+                                              &kCFTypeDictionaryKeyCallBacks,
+                                              &kCFTypeDictionaryValueCallBacks));
+            CFDictionaryAddValue(attributes.get(),
+                                 kCTFontVariationAttribute, ctVariation.dict.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,
+                                    fontData->detachStream());
+    }
+
+    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;
+        }
+
+        return this->onMakeFromData(std::move(data), ttcIndex);
+    }
+
+    sk_sp<SkTypeface> onLegacyMakeTypeface(const char familyName[], SkFontStyle style) const override {
+        if (familyName) {
+            familyName = map_css_names(familyName);
+        }
+
+        sk_sp<SkTypeface> face = create_from_name(familyName, style);
+        if (face) {
+            return face;
+        }
+
+        static SkTypeface* gDefaultFace;
+        static SkOnce lookupDefault;
+        static const char FONT_DEFAULT_NAME[] = "Lucida Sans";
+        lookupDefault([]{
+            gDefaultFace = create_from_name(FONT_DEFAULT_NAME, SkFontStyle()).release();
+        });
+        return sk_ref_sp(gDefaultFace);
+    }
+};
+
+sk_sp<SkFontMgr> SkFontMgr_New_CoreText(CTFontCollectionRef fontCollection) {
+    return sk_make_sp<SkFontMgr_Mac>(fontCollection);
+}
+
+#endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
diff --git a/src/ports/SkFontMgr_mac_ct_factory.cpp b/src/ports/SkFontMgr_mac_ct_factory.cpp
new file mode 100644
index 0000000..ef834e4
--- /dev/null
+++ b/src/ports/SkFontMgr_mac_ct_factory.cpp
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "include/core/SkTypes.h"
+#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
+
+#include "include/core/SkFontMgr.h"
+#include "include/ports/SkFontMgr_mac_ct.h"
+
+sk_sp<SkFontMgr> SkFontMgr::Factory() {
+    return SkFontMgr_New_CoreText(nullptr);
+}
+
+#endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
diff --git a/src/ports/SkScalerContext_mac_ct.cpp b/src/ports/SkScalerContext_mac_ct.cpp
new file mode 100644
index 0000000..ad01dc6
--- /dev/null
+++ b/src/ports/SkScalerContext_mac_ct.cpp
@@ -0,0 +1,721 @@
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "include/core/SkTypes.h"
+#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
+
+#ifdef SK_BUILD_FOR_MAC
+#import <ApplicationServices/ApplicationServices.h>
+#endif
+
+#ifdef SK_BUILD_FOR_IOS
+#include <CoreText/CoreText.h>
+#include <CoreText/CTFontManager.h>
+#include <CoreGraphics/CoreGraphics.h>
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
+#include "include/core/SkColor.h"
+#include "include/core/SkColorPriv.h"
+#include "include/core/SkFontMetrics.h"
+#include "include/core/SkFontTypes.h"
+#include "include/core/SkMatrix.h"
+#include "include/core/SkPath.h"
+#include "include/core/SkPoint.h"
+#include "include/core/SkRect.h"
+#include "include/core/SkScalar.h"
+#include "include/core/SkTypeface.h"
+#include "include/private/SkColorData.h"
+#include "include/private/SkFixed.h"
+#include "include/private/SkTemplates.h"
+#include "include/private/SkTo.h"
+#include "src/core/SkAutoMalloc.h"
+#include "src/core/SkEndian.h"
+#include "src/core/SkGlyph.h"
+#include "src/core/SkMask.h"
+#include "src/core/SkMaskGamma.h"
+#include "src/core/SkMathPriv.h"
+#include "src/core/SkUtils.h"
+#include "src/ports/SkScalerContext_mac_ct.h"
+#include "src/ports/SkTypeface_mac_ct.h"
+#include "src/sfnt/SkOTTableTypes.h"
+#include "src/sfnt/SkOTTable_OS_2.h"
+#include "src/utils/mac/SkCGBase.h"
+#include "src/utils/mac/SkCGGeometry.h"
+#include "src/utils/mac/SkCTFontSmoothBehavior.h"
+#include "src/utils/mac/SkUniqueCFRef.h"
+
+#include <algorithm>
+
+class SkDescriptor;
+
+
+// Set to make glyph bounding boxes visible.
+#define SK_SHOW_TEXT_BLIT_COVERAGE 0
+
+static void sk_memset_rect32(uint32_t* ptr, uint32_t value,
+                             int width, int height, size_t rowBytes) {
+    SkASSERT(width);
+    SkASSERT(width * sizeof(uint32_t) <= rowBytes);
+
+    if (width >= 32) {
+        while (height) {
+            sk_memset32(ptr, value, width);
+            ptr = (uint32_t*)((char*)ptr + rowBytes);
+            height -= 1;
+        }
+        return;
+    }
+
+    rowBytes -= width * sizeof(uint32_t);
+
+    if (width >= 8) {
+        while (height) {
+            int w = width;
+            do {
+                *ptr++ = value; *ptr++ = value;
+                *ptr++ = value; *ptr++ = value;
+                *ptr++ = value; *ptr++ = value;
+                *ptr++ = value; *ptr++ = value;
+                w -= 8;
+            } while (w >= 8);
+            while (--w >= 0) {
+                *ptr++ = value;
+            }
+            ptr = (uint32_t*)((char*)ptr + rowBytes);
+            height -= 1;
+        }
+    } else {
+        while (height) {
+            int w = width;
+            do {
+                *ptr++ = value;
+            } while (--w > 0);
+            ptr = (uint32_t*)((char*)ptr + rowBytes);
+            height -= 1;
+        }
+    }
+}
+
+static unsigned CGRGBPixel_getAlpha(CGRGBPixel pixel) {
+    return pixel & 0xFF;
+}
+
+static CGAffineTransform MatrixToCGAffineTransform(const SkMatrix& matrix) {
+    return CGAffineTransformMake( SkScalarToCGFloat(matrix[SkMatrix::kMScaleX]),
+                                 -SkScalarToCGFloat(matrix[SkMatrix::kMSkewY] ),
+                                 -SkScalarToCGFloat(matrix[SkMatrix::kMSkewX] ),
+                                  SkScalarToCGFloat(matrix[SkMatrix::kMScaleY]),
+                                  SkScalarToCGFloat(matrix[SkMatrix::kMTransX]),
+                                  SkScalarToCGFloat(matrix[SkMatrix::kMTransY]));
+}
+
+SkScalerContext_Mac::SkScalerContext_Mac(sk_sp<SkTypeface_Mac> typeface,
+                                         const SkScalerContextEffects& effects,
+                                         const SkDescriptor* desc)
+        : INHERITED(std::move(typeface), effects, desc)
+        , fDoSubPosition(SkToBool(fRec.fFlags & kSubpixelPositioning_Flag))
+
+{
+    CTFontRef ctFont = (CTFontRef)this->getTypeface()->internal_private_getCTFontRef();
+    CFIndex numGlyphs = CTFontGetGlyphCount(ctFont);
+    SkASSERT(numGlyphs >= 1 && numGlyphs <= 0xFFFF);
+    fGlyphCount = SkToU16(numGlyphs);
+
+    // CT on (at least) 10.9 will size color glyphs down from the requested size, but not up.
+    // As a result, it is necessary to know the actual device size and request that.
+    SkVector scale;
+    SkMatrix skTransform;
+    bool invertible = fRec.computeMatrices(SkScalerContextRec::kVertical_PreMatrixScale,
+                                           &scale, &skTransform, nullptr, nullptr, nullptr);
+    fTransform = MatrixToCGAffineTransform(skTransform);
+    // CGAffineTransformInvert documents that if the transform is non-invertible it will return the
+    // passed transform unchanged. It does so, but then also prints a message to stdout. Avoid this.
+    if (invertible) {
+        fInvTransform = CGAffineTransformInvert(fTransform);
+    } else {
+        fInvTransform = fTransform;
+    }
+
+    // The transform contains everything except the requested text size.
+    // Some properties, like 'trak', are based on the optical text size.
+    CGFloat textSize = SkScalarToCGFloat(scale.y());
+    fCTFont = SkCTFontCreateExactCopy(ctFont, textSize,
+                                      ((SkTypeface_Mac*)this->getTypeface())->fOpszVariation);
+    fCGFont.reset(CTFontCopyGraphicsFont(fCTFont.get(), nullptr));
+}
+
+static int RoundSize(int dimension) {
+    return SkNextPow2(dimension);
+}
+
+CGRGBPixel* SkScalerContext_Mac::Offscreen::getCG(const SkScalerContext_Mac& context,
+                                                  const SkGlyph& glyph, CGGlyph glyphID,
+                                                  size_t* rowBytesPtr,
+                                                  bool generateA8FromLCD) {
+    if (!fRGBSpace) {
+        //It doesn't appear to matter what color space is specified.
+        //Regular blends and antialiased text are always (s*a + d*(1-a))
+        //and subpixel antialiased text is always g=2.0.
+        fRGBSpace.reset(CGColorSpaceCreateDeviceRGB());
+    }
+
+    // default to kBW_Format
+    bool doAA = false;
+    bool doLCD = false;
+
+    if (SkMask::kBW_Format != glyph.maskFormat()) {
+        doLCD = true;
+        doAA = true;
+    }
+
+    // FIXME: lcd smoothed un-hinted rasterization unsupported.
+    if (!generateA8FromLCD && SkMask::kA8_Format == glyph.maskFormat()) {
+        doLCD = false;
+        doAA = true;
+    }
+
+    // If this font might have color glyphs, disable LCD as there's no way to support it.
+    // CoreText doesn't tell us which format it ended up using, so we can't detect it.
+    // A8 will end up black on transparent, but TODO: we can detect gray and set to A8.
+    if (SkMask::kARGB32_Format == glyph.maskFormat()) {
+        doLCD = false;
+    }
+
+    size_t rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
+    if (!fCG || fSize.fWidth < glyph.width() || fSize.fHeight < glyph.height()) {
+        if (fSize.fWidth < glyph.width()) {
+            fSize.fWidth = RoundSize(glyph.width());
+        }
+        if (fSize.fHeight < glyph.height()) {
+            fSize.fHeight = RoundSize(glyph.height());
+        }
+
+        rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
+        void* image = fImageStorage.reset(rowBytes * fSize.fHeight);
+        const CGImageAlphaInfo alpha = (glyph.isColor())
+                                     ? kCGImageAlphaPremultipliedFirst
+                                     : kCGImageAlphaNoneSkipFirst;
+        const CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host | alpha;
+        fCG.reset(CGBitmapContextCreate(image, fSize.fWidth, fSize.fHeight, 8,
+                                        rowBytes, fRGBSpace.get(), bitmapInfo));
+
+        // Skia handles quantization and subpixel positioning,
+        // so disable quantization and enable subpixel positioning in CG.
+        CGContextSetAllowsFontSubpixelQuantization(fCG.get(), false);
+        CGContextSetShouldSubpixelQuantizeFonts(fCG.get(), false);
+
+        // Because CG always draws from the horizontal baseline,
+        // if there is a non-integral translation from the horizontal origin to the vertical origin,
+        // then CG cannot draw the glyph in the correct location without subpixel positioning.
+        CGContextSetAllowsFontSubpixelPositioning(fCG.get(), true);
+        CGContextSetShouldSubpixelPositionFonts(fCG.get(), true);
+
+        CGContextSetTextDrawingMode(fCG.get(), kCGTextFill);
+
+        // Draw black on white to create mask. (Special path exists to speed this up in CG.)
+        CGContextSetGrayFillColor(fCG.get(), 0.0f, 1.0f);
+
+        // force our checks below to happen
+        fDoAA = !doAA;
+        fDoLCD = !doLCD;
+
+        CGContextSetTextMatrix(fCG.get(), context.fTransform);
+    }
+
+    if (fDoAA != doAA) {
+        CGContextSetShouldAntialias(fCG.get(), doAA);
+        fDoAA = doAA;
+    }
+    if (fDoLCD != doLCD) {
+        CGContextSetShouldSmoothFonts(fCG.get(), doLCD);
+        fDoLCD = doLCD;
+    }
+
+    CGRGBPixel* image = (CGRGBPixel*)fImageStorage.get();
+    // skip rows based on the glyph's height
+    image += (fSize.fHeight - glyph.height()) * fSize.fWidth;
+
+    // Erase to white (or transparent black if it's a color glyph, to not composite against white).
+    uint32_t bgColor = (!glyph.isColor()) ? 0xFFFFFFFF : 0x00000000;
+    sk_memset_rect32(image, bgColor, glyph.width(), glyph.height(), rowBytes);
+
+    float subX = 0;
+    float subY = 0;
+    if (context.fDoSubPosition) {
+        subX = SkFixedToFloat(glyph.getSubXFixed());
+        subY = SkFixedToFloat(glyph.getSubYFixed());
+    }
+
+    CGPoint point = CGPointMake(-glyph.left() + subX, glyph.top() + glyph.height() - subY);
+    // Prior to 10.10, CTFontDrawGlyphs acted like CGContextShowGlyphsAtPositions and took
+    // 'positions' which are in text space. The glyph location (in device space) must be
+    // mapped into text space, so that CG can convert it back into device space.
+    // In 10.10.1, this is handled directly in CTFontDrawGlyphs.
+    //
+    // However, in 10.10.2 color glyphs no longer rotate based on the font transform.
+    // So always make the font transform identity and place the transform on the context.
+    point = CGPointApplyAffineTransform(point, context.fInvTransform);
+
+    CTFontDrawGlyphs(context.fCTFont.get(), &glyphID, &point, 1, fCG.get());
+
+    SkASSERT(rowBytesPtr);
+    *rowBytesPtr = rowBytes;
+    return image;
+}
+
+unsigned SkScalerContext_Mac::generateGlyphCount(void) {
+    return fGlyphCount;
+}
+
+bool SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {
+    return false;
+}
+
+void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) {
+    glyph->fMaskFormat = fRec.fMaskFormat;
+
+    const CGGlyph cgGlyph = (CGGlyph) glyph->getGlyphID();
+    glyph->zeroMetrics();
+
+    // The following block produces cgAdvance in CG units (pixels, y up).
+    CGSize cgAdvance;
+    CTFontGetAdvancesForGlyphs(fCTFont.get(), kCTFontOrientationHorizontal,
+                               &cgGlyph, &cgAdvance, 1);
+    cgAdvance = CGSizeApplyAffineTransform(cgAdvance, fTransform);
+    glyph->fAdvanceX =  SkFloatFromCGFloat(cgAdvance.width);
+    glyph->fAdvanceY = -SkFloatFromCGFloat(cgAdvance.height);
+
+    // The following produces skBounds in SkGlyph units (pixels, y down),
+    // or returns early if skBounds would be empty.
+    SkRect skBounds;
+
+    // Glyphs are always drawn from the horizontal origin. The caller must manually use the result
+    // of CTFontGetVerticalTranslationsForGlyphs to calculate where to draw the glyph for vertical
+    // glyphs. As a result, always get the horizontal bounds of a glyph and translate it if the
+    // glyph is vertical. This avoids any diagreement between the various means of retrieving
+    // vertical metrics.
+    {
+        // CTFontGetBoundingRectsForGlyphs produces cgBounds in CG units (pixels, y up).
+        CGRect cgBounds;
+        CTFontGetBoundingRectsForGlyphs(fCTFont.get(), kCTFontOrientationHorizontal,
+                                        &cgGlyph, &cgBounds, 1);
+        cgBounds = CGRectApplyAffineTransform(cgBounds, fTransform);
+
+        // BUG?
+        // 0x200B (zero-advance space) seems to return a huge (garbage) bounds, when
+        // it should be empty. So, if we see a zero-advance, we check if it has an
+        // empty path or not, and if so, we jam the bounds to 0. Hopefully a zero-advance
+        // is rare, so we won't incur a big performance cost for this extra check.
+        if (0 == cgAdvance.width && 0 == cgAdvance.height) {
+            SkUniqueCFRef<CGPathRef> path(CTFontCreatePathForGlyph(fCTFont.get(), cgGlyph,nullptr));
+            if (!path || CGPathIsEmpty(path.get())) {
+                return;
+            }
+        }
+
+        if (SkCGRectIsEmpty(cgBounds)) {
+            return;
+        }
+
+        // Convert cgBounds to SkGlyph units (pixels, y down).
+        skBounds = SkRect::MakeXYWH(cgBounds.origin.x, -cgBounds.origin.y - cgBounds.size.height,
+                                    cgBounds.size.width, cgBounds.size.height);
+    }
+
+    // Currently the bounds are based on being rendered at (0,0).
+    // The top left must not move, since that is the base from which subpixel positioning is offset.
+    if (fDoSubPosition) {
+        skBounds.fRight += SkFixedToFloat(glyph->getSubXFixed());
+        skBounds.fBottom += SkFixedToFloat(glyph->getSubYFixed());
+    }
+
+    // We're trying to pack left and top into int16_t,
+    // and width and height into uint16_t, after outsetting by 1.
+    if (!SkRect::MakeXYWH(-32767, -32767, 65535, 65535).contains(skBounds)) {
+        return;
+    }
+
+    SkIRect skIBounds;
+    skBounds.roundOut(&skIBounds);
+    // Expand the bounds by 1 pixel, to give CG room for anti-aliasing.
+    // Note that this outset is to allow room for LCD smoothed glyphs. However, the correct outset
+    // is not currently known, as CG dilates the outlines by some percentage.
+    // Note that if this context is A8 and not back-forming from LCD, there is no need to outset.
+    skIBounds.outset(1, 1);
+    glyph->fLeft = SkToS16(skIBounds.fLeft);
+    glyph->fTop = SkToS16(skIBounds.fTop);
+    glyph->fWidth = SkToU16(skIBounds.width());
+    glyph->fHeight = SkToU16(skIBounds.height());
+}
+
+static constexpr uint8_t sk_pow2_table(size_t i) {
+    return SkToU8(((i * i + 128) / 255));
+}
+
+/**
+ *  This will invert the gamma applied by CoreGraphics, so we can get linear
+ *  values.
+ *
+ *  CoreGraphics obscurely defaults to 2.0 as the subpixel coverage gamma value.
+ *  The color space used does not appear to affect this choice.
+ */
+static constexpr auto gLinearCoverageFromCGLCDValue = SkMakeArray<256>(sk_pow2_table);
+
+static void cgpixels_to_bits(uint8_t dst[], const CGRGBPixel src[], int count) {
+    while (count > 0) {
+        uint8_t mask = 0;
+        for (int i = 7; i >= 0; --i) {
+            mask |= ((CGRGBPixel_getAlpha(*src++) >> 7) ^ 0x1) << i;
+            if (0 == --count) {
+                break;
+            }
+        }
+        *dst++ = mask;
+    }
+}
+
+template<bool APPLY_PREBLEND>
+static inline uint8_t rgb_to_a8(CGRGBPixel rgb, const uint8_t* table8) {
+    U8CPU r = 0xFF - ((rgb >> 16) & 0xFF);
+    U8CPU g = 0xFF - ((rgb >>  8) & 0xFF);
+    U8CPU b = 0xFF - ((rgb >>  0) & 0xFF);
+    U8CPU lum = sk_apply_lut_if<APPLY_PREBLEND>(SkComputeLuminance(r, g, b), table8);
+#if SK_SHOW_TEXT_BLIT_COVERAGE
+    lum = std::max(lum, (U8CPU)0x30);
+#endif
+    return lum;
+}
+
+template<bool APPLY_PREBLEND>
+static void RGBToA8(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes,
+                    const SkGlyph& glyph, void* glyphImage, const uint8_t* table8) {
+    const int width = glyph.width();
+    const int height = glyph.height();
+    size_t dstRB = glyph.rowBytes();
+    uint8_t* SK_RESTRICT dst = (uint8_t*)glyphImage;
+
+    for (int y = 0; y < height; y++) {
+        for (int i = 0; i < width; ++i) {
+            dst[i] = rgb_to_a8<APPLY_PREBLEND>(cgPixels[i], table8);
+        }
+        cgPixels = SkTAddOffset<const CGRGBPixel>(cgPixels, cgRowBytes);
+        dst = SkTAddOffset<uint8_t>(dst, dstRB);
+    }
+}
+
+template<bool APPLY_PREBLEND>
+static uint16_t RGBToLcd16(CGRGBPixel rgb,
+                           const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
+    U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >> 16) & 0xFF), tableR);
+    U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >>  8) & 0xFF), tableG);
+    U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >>  0) & 0xFF), tableB);
+#if SK_SHOW_TEXT_BLIT_COVERAGE
+    r = std::max(r, (U8CPU)0x30);
+    g = std::max(g, (U8CPU)0x30);
+    b = std::max(b, (U8CPU)0x30);
+#endif
+    return SkPack888ToRGB16(r, g, b);
+}
+
+template<bool APPLY_PREBLEND>
+static void RGBToLcd16(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes,
+                       const SkGlyph& glyph, void* glyphImage,
+                       const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
+    const int width = glyph.width();
+    const int height = glyph.height();
+    size_t dstRB = glyph.rowBytes();
+    uint16_t* SK_RESTRICT dst = (uint16_t*)glyphImage;
+
+    for (int y = 0; y < height; y++) {
+        for (int i = 0; i < width; i++) {
+            dst[i] = RGBToLcd16<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB);
+        }
+        cgPixels = SkTAddOffset<const CGRGBPixel>(cgPixels, cgRowBytes);
+        dst = SkTAddOffset<uint16_t>(dst, dstRB);
+    }
+}
+
+static SkPMColor cgpixels_to_pmcolor(CGRGBPixel rgb) {
+    U8CPU a = (rgb >> 24) & 0xFF;
+    U8CPU r = (rgb >> 16) & 0xFF;
+    U8CPU g = (rgb >>  8) & 0xFF;
+    U8CPU b = (rgb >>  0) & 0xFF;
+#if SK_SHOW_TEXT_BLIT_COVERAGE
+    a = std::max(a, (U8CPU)0x30);
+#endif
+    return SkPackARGB32(a, r, g, b);
+}
+
+void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) {
+    CGGlyph cgGlyph = SkTo<CGGlyph>(glyph.getGlyphID());
+
+    // FIXME: lcd smoothed un-hinted rasterization unsupported.
+    bool requestSmooth = fRec.getHinting() != SkFontHinting::kNone;
+
+    // Draw the glyph
+    size_t cgRowBytes;
+    CGRGBPixel* cgPixels = fOffscreen.getCG(*this, glyph, cgGlyph, &cgRowBytes, requestSmooth);
+    if (cgPixels == nullptr) {
+        return;
+    }
+
+    // Fix the glyph
+    if ((glyph.fMaskFormat == SkMask::kLCD16_Format) ||
+        (glyph.fMaskFormat == SkMask::kA8_Format
+         && requestSmooth
+         && SkCTFontGetSmoothBehavior() != SkCTFontSmoothBehavior::none))
+    {
+        const uint8_t* linear = gLinearCoverageFromCGLCDValue.data();
+
+        //Note that the following cannot really be integrated into the
+        //pre-blend, since we may not be applying the pre-blend; when we aren't
+        //applying the pre-blend it means that a filter wants linear anyway.
+        //Other code may also be applying the pre-blend, so we'd need another
+        //one with this and one without.
+        CGRGBPixel* addr = cgPixels;
+        for (int y = 0; y < glyph.fHeight; ++y) {
+            for (int x = 0; x < glyph.fWidth; ++x) {
+                int r = (addr[x] >> 16) & 0xFF;
+                int g = (addr[x] >>  8) & 0xFF;
+                int b = (addr[x] >>  0) & 0xFF;
+                addr[x] = (linear[r] << 16) | (linear[g] << 8) | linear[b];
+            }
+            addr = SkTAddOffset<CGRGBPixel>(addr, cgRowBytes);
+        }
+    }
+
+    // Convert glyph to mask
+    switch (glyph.fMaskFormat) {
+        case SkMask::kLCD16_Format: {
+            if (fPreBlend.isApplicable()) {
+                RGBToLcd16<true>(cgPixels, cgRowBytes, glyph, glyph.fImage,
+                                 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
+            } else {
+                RGBToLcd16<false>(cgPixels, cgRowBytes, glyph, glyph.fImage,
+                                  fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
+            }
+        } break;
+        case SkMask::kA8_Format: {
+            if (fPreBlend.isApplicable()) {
+                RGBToA8<true>(cgPixels, cgRowBytes, glyph, glyph.fImage, fPreBlend.fG);
+            } else {
+                RGBToA8<false>(cgPixels, cgRowBytes, glyph, glyph.fImage, fPreBlend.fG);
+            }
+        } break;
+        case SkMask::kBW_Format: {
+            const int width = glyph.fWidth;
+            size_t dstRB = glyph.rowBytes();
+            uint8_t* dst = (uint8_t*)glyph.fImage;
+            for (int y = 0; y < glyph.fHeight; y++) {
+                cgpixels_to_bits(dst, cgPixels, width);
+                cgPixels = SkTAddOffset<CGRGBPixel>(cgPixels, cgRowBytes);
+                dst = SkTAddOffset<uint8_t>(dst, dstRB);
+            }
+        } break;
+        case SkMask::kARGB32_Format: {
+            const int width = glyph.fWidth;
+            size_t dstRB = glyph.rowBytes();
+            SkPMColor* dst = (SkPMColor*)glyph.fImage;
+            for (int y = 0; y < glyph.fHeight; y++) {
+                for (int x = 0; x < width; ++x) {
+                    dst[x] = cgpixels_to_pmcolor(cgPixels[x]);
+                }
+                cgPixels = SkTAddOffset<CGRGBPixel>(cgPixels, cgRowBytes);
+                dst = SkTAddOffset<SkPMColor>(dst, dstRB);
+            }
+        } break;
+        default:
+            SkDEBUGFAIL("unexpected mask format");
+            break;
+    }
+}
+
+namespace {
+class SkCTPathGeometrySink {
+    SkPath* fPath;
+    bool fStarted;
+    CGPoint fCurrent;
+
+    void goingTo(const CGPoint pt) {
+        if (!fStarted) {
+            fStarted = true;
+            fPath->moveTo(fCurrent.x, -fCurrent.y);
+        }
+        fCurrent = pt;
+    }
+
+    bool currentIsNot(const CGPoint pt) {
+        return fCurrent.x != pt.x || fCurrent.y != pt.y;
+    }
+
+public:
+    SkCTPathGeometrySink(SkPath* path) : fPath{path}, fStarted{false}, fCurrent{0,0} {}
+    static void ApplyElement(void *ctx, const CGPathElement *element) {
+        SkCTPathGeometrySink& self = *(SkCTPathGeometrySink*)ctx;
+        CGPoint* points = element->points;
+
+        switch (element->type) {
+            case kCGPathElementMoveToPoint:
+                self.fStarted = false;
+                self.fCurrent = points[0];
+                break;
+
+            case kCGPathElementAddLineToPoint:
+                if (self.currentIsNot(points[0])) {
+                    self.goingTo(points[0]);
+                    self.fPath->lineTo(points[0].x, -points[0].y);
+                }
+                break;
+
+            case kCGPathElementAddQuadCurveToPoint:
+                if (self.currentIsNot(points[0]) || self.currentIsNot(points[1])) {
+                    self.goingTo(points[1]);
+                    self.fPath->quadTo(points[0].x, -points[0].y,
+                                       points[1].x, -points[1].y);
+                }
+                break;
+
+            case kCGPathElementAddCurveToPoint:
+                if (self.currentIsNot(points[0]) ||
+                    self.currentIsNot(points[1]) ||
+                    self.currentIsNot(points[2]))
+                {
+                    self.goingTo(points[2]);
+                    self.fPath->cubicTo(points[0].x, -points[0].y,
+                                        points[1].x, -points[1].y,
+                                        points[2].x, -points[2].y);
+                }
+                break;
+
+            case kCGPathElementCloseSubpath:
+                if (self.fStarted) {
+                    self.fPath->close();
+                }
+                break;
+
+            default:
+                SkDEBUGFAIL("Unknown path element!");
+                break;
+            }
+    }
+};
+} // namespace
+
+/*
+ *  Our subpixel resolution is only 2 bits in each direction, so a scale of 4
+ *  seems sufficient, and possibly even correct, to allow the hinted outline
+ *  to be subpixel positioned.
+ */
+#define kScaleForSubPixelPositionHinting (4.0f)
+
+bool SkScalerContext_Mac::generatePath(SkGlyphID glyph, SkPath* path) {
+    SkScalar scaleX = SK_Scalar1;
+    SkScalar scaleY = SK_Scalar1;
+
+    CGAffineTransform xform = fTransform;
+    /*
+     *  For subpixel positioning, we want to return an unhinted outline, so it
+     *  can be positioned nicely at fractional offsets. However, we special-case
+     *  if the baseline of the (horizontal) text is axis-aligned. In those cases
+     *  we want to retain hinting in the direction orthogonal to the baseline.
+     *  e.g. for horizontal baseline, we want to retain hinting in Y.
+     *  The way we remove hinting is to scale the font by some value (4) in that
+     *  direction, ask for the path, and then scale the path back down.
+     */
+    if (fDoSubPosition) {
+        // start out by assuming that we want no hining in X and Y
+        scaleX = scaleY = kScaleForSubPixelPositionHinting;
+        // now see if we need to restore hinting for axis-aligned baselines
+        switch (this->computeAxisAlignmentForHText()) {
+            case kX_SkAxisAlignment:
+                scaleY = SK_Scalar1; // want hinting in the Y direction
+                break;
+            case kY_SkAxisAlignment:
+                scaleX = SK_Scalar1; // want hinting in the X direction
+                break;
+            default:
+                break;
+        }
+
+        CGAffineTransform scale(CGAffineTransformMakeScale(SkScalarToCGFloat(scaleX),
+                                                           SkScalarToCGFloat(scaleY)));
+        xform = CGAffineTransformConcat(fTransform, scale);
+    }
+
+    CGGlyph cgGlyph = SkTo<CGGlyph>(glyph);
+    SkUniqueCFRef<CGPathRef> cgPath(CTFontCreatePathForGlyph(fCTFont.get(), cgGlyph, &xform));
+
+    path->reset();
+    if (!cgPath) {
+        return false;
+    }
+
+    SkCTPathGeometrySink sink(path);
+    CGPathApply(cgPath.get(), &sink, SkCTPathGeometrySink::ApplyElement);
+    if (fDoSubPosition) {
+        SkMatrix m;
+        m.setScale(SkScalarInvert(scaleX), SkScalarInvert(scaleY));
+        path->transform(m);
+    }
+    return true;
+}
+
+void SkScalerContext_Mac::generateFontMetrics(SkFontMetrics* metrics) {
+    if (nullptr == metrics) {
+        return;
+    }
+
+    CGRect theBounds = CTFontGetBoundingBox(fCTFont.get());
+
+    metrics->fTop          = SkScalarFromCGFloat(-SkCGRectGetMaxY(theBounds));
+    metrics->fAscent       = SkScalarFromCGFloat(-CTFontGetAscent(fCTFont.get()));
+    metrics->fDescent      = SkScalarFromCGFloat( CTFontGetDescent(fCTFont.get()));
+    metrics->fBottom       = SkScalarFromCGFloat(-SkCGRectGetMinY(theBounds));
+    metrics->fLeading      = SkScalarFromCGFloat( CTFontGetLeading(fCTFont.get()));
+    metrics->fAvgCharWidth = SkScalarFromCGFloat( SkCGRectGetWidth(theBounds));
+    metrics->fXMin         = SkScalarFromCGFloat( SkCGRectGetMinX(theBounds));
+    metrics->fXMax         = SkScalarFromCGFloat( SkCGRectGetMaxX(theBounds));
+    metrics->fMaxCharWidth = metrics->fXMax - metrics->fXMin;
+    metrics->fXHeight      = SkScalarFromCGFloat( CTFontGetXHeight(fCTFont.get()));
+    metrics->fCapHeight    = SkScalarFromCGFloat( CTFontGetCapHeight(fCTFont.get()));
+    metrics->fUnderlineThickness = SkScalarFromCGFloat( CTFontGetUnderlineThickness(fCTFont.get()));
+    metrics->fUnderlinePosition = -SkScalarFromCGFloat( CTFontGetUnderlinePosition(fCTFont.get()));
+
+    metrics->fFlags = 0;
+    metrics->fFlags |= SkFontMetrics::kUnderlineThicknessIsValid_Flag;
+    metrics->fFlags |= SkFontMetrics::kUnderlinePositionIsValid_Flag;
+
+    // See https://bugs.chromium.org/p/skia/issues/detail?id=6203
+    // At least on 10.12.3 with memory based fonts the x-height is always 0.6666 of the ascent and
+    // the cap-height is always 0.8888 of the ascent. It appears that the values from the 'OS/2'
+    // table are read, but then overwritten if the font is not a system font. As a result, if there
+    // is a valid 'OS/2' table available use the values from the table if they aren't too strange.
+    struct OS2HeightMetrics {
+        SK_OT_SHORT sxHeight;
+        SK_OT_SHORT sCapHeight;
+    } heights;
+    size_t bytesRead = this->getTypeface()->getTableData(
+            SkTEndian_SwapBE32(SkOTTableOS2::TAG), offsetof(SkOTTableOS2, version.v2.sxHeight),
+            sizeof(heights), &heights);
+    if (bytesRead == sizeof(heights)) {
+        // 'fontSize' is correct because the entire resolved size is set by the constructor.
+        CGFloat fontSize = CTFontGetSize(this->fCTFont.get());
+        unsigned upem = CTFontGetUnitsPerEm(this->fCTFont.get());
+        unsigned maxSaneHeight = upem * 2;
+        uint16_t xHeight = SkEndian_SwapBE16(heights.sxHeight);
+        if (xHeight && xHeight < maxSaneHeight) {
+            metrics->fXHeight = SkScalarFromCGFloat(xHeight * fontSize / upem);
+        }
+        uint16_t capHeight = SkEndian_SwapBE16(heights.sCapHeight);
+        if (capHeight && capHeight < maxSaneHeight) {
+            metrics->fCapHeight = SkScalarFromCGFloat(capHeight * fontSize / upem);
+        }
+    }
+}
+
+#endif
diff --git a/src/ports/SkScalerContext_mac_ct.h b/src/ports/SkScalerContext_mac_ct.h
new file mode 100644
index 0000000..4b7f548
--- /dev/null
+++ b/src/ports/SkScalerContext_mac_ct.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2020 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkScalerContext_mac_ct_DEFINED
+#define SkScalerContext_mac_ct_DEFINED
+
+#include "include/core/SkTypes.h"
+#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
+
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkSize.h"
+#include "src/core/SkAutoMalloc.h"
+#include "src/core/SkScalerContext.h"
+#include "src/utils/mac/SkUniqueCFRef.h"
+
+#ifdef SK_BUILD_FOR_MAC
+#import <ApplicationServices/ApplicationServices.h>
+#endif
+
+#ifdef SK_BUILD_FOR_IOS
+#include <CoreText/CoreText.h>
+#include <CoreText/CTFontManager.h>
+#include <CoreGraphics/CoreGraphics.h>
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
+#include <memory>
+
+class SkDescriptor;
+class SkGlyph;
+class SkPath;
+class SkTypeface_Mac;
+struct SkFontMetrics;
+
+
+typedef uint32_t CGRGBPixel;
+
+class SkScalerContext_Mac : public SkScalerContext {
+public:
+    SkScalerContext_Mac(sk_sp<SkTypeface_Mac>, const SkScalerContextEffects&, const SkDescriptor*);
+
+protected:
+    unsigned generateGlyphCount(void) override;
+    bool generateAdvance(SkGlyph* glyph) override;
+    void generateMetrics(SkGlyph* glyph) override;
+    void generateImage(const SkGlyph& glyph) override;
+    bool generatePath(SkGlyphID glyph, SkPath* path) override;
+    void generateFontMetrics(SkFontMetrics*) override;
+
+private:
+    class Offscreen {
+    public:
+        Offscreen()
+            : fRGBSpace(nullptr)
+            , fCG(nullptr)
+            , fDoAA(false)
+            , fDoLCD(false)
+        {
+            fSize.set(0, 0);
+        }
+
+        CGRGBPixel* getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
+                          CGGlyph glyphID, size_t* rowBytesPtr, bool generateA8FromLCD);
+
+    private:
+        enum {
+            kSize = 32 * 32 * sizeof(CGRGBPixel)
+        };
+        SkAutoSMalloc<kSize> fImageStorage;
+        SkUniqueCFRef<CGColorSpaceRef> fRGBSpace;
+
+        // cached state
+        SkUniqueCFRef<CGContextRef> fCG;
+        SkISize fSize;
+        bool fDoAA;
+        bool fDoLCD;
+    };
+    Offscreen fOffscreen;
+
+    /** Unrotated variant of fCTFont.
+     *
+     *  In 10.10.1 CTFontGetAdvancesForGlyphs applies the font transform to the width of the
+     *  advances, but always sets the height to 0. This font is used to get the advances of the
+     *  unrotated glyph, and then the rotation is applied separately.
+     *
+     *  CT vertical metrics are pre-rotated (in em space, before transform) 90deg clock-wise.
+     *  This makes kCTFontOrientationDefault dangerous, because the metrics from
+     *  kCTFontOrientationHorizontal are in a different space from kCTFontOrientationVertical.
+     *  With kCTFontOrientationVertical the advances must be unrotated.
+     *
+     *  Sometimes, creating a copy of a CTFont with the same size but different trasform will select
+     *  different underlying font data. As a result, avoid ever creating more than one CTFont per
+     *  SkScalerContext to ensure that only one CTFont is used.
+     *
+     *  As a result of the above (and other constraints) this font contains the size, but not the
+     *  transform. The transform must always be applied separately.
+     */
+    SkUniqueCFRef<CTFontRef> fCTFont;
+
+    /** The transform without the font size. */
+    CGAffineTransform fTransform;
+    CGAffineTransform fInvTransform;
+
+    SkUniqueCFRef<CGFontRef> fCGFont;
+    uint16_t fGlyphCount;
+    const bool fDoSubPosition;
+
+    friend class Offscreen;
+
+    typedef SkScalerContext INHERITED;
+};
+
+#endif
+#endif //SkScalerContext_mac_ct_DEFINED
diff --git a/src/ports/SkTypeface_mac_ct.cpp b/src/ports/SkTypeface_mac_ct.cpp
new file mode 100644
index 0000000..8055f6b
--- /dev/null
+++ b/src/ports/SkTypeface_mac_ct.cpp
@@ -0,0 +1,1470 @@
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "include/core/SkTypes.h"
+#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
+
+#ifdef SK_BUILD_FOR_MAC
+#import <ApplicationServices/ApplicationServices.h>
+#endif
+
+#ifdef SK_BUILD_FOR_IOS
+#include <CoreText/CoreText.h>
+#include <CoreText/CTFontManager.h>
+#include <CoreGraphics/CoreGraphics.h>
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
+#include "include/core/SkColor.h"
+#include "include/core/SkData.h"
+#include "include/core/SkFontArguments.h"
+#include "include/core/SkFontParameters.h"
+#include "include/core/SkFontStyle.h"
+#include "include/core/SkFontTypes.h"
+#include "include/core/SkRect.h"
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkScalar.h"
+#include "include/core/SkStream.h"
+#include "include/core/SkString.h"
+#include "include/core/SkTypeface.h"
+#include "include/ports/SkTypeface_mac.h"
+#include "include/private/SkFixed.h"
+#include "include/private/SkMalloc.h"
+#include "include/private/SkMutex.h"
+#include "include/private/SkOnce.h"
+#include "include/private/SkTDArray.h"
+#include "include/private/SkTemplates.h"
+#include "include/private/SkTo.h"
+#include "src/core/SkAdvancedTypefaceMetrics.h"
+#include "src/core/SkEndian.h"
+#include "src/core/SkFontDescriptor.h"
+#include "src/core/SkMask.h"
+#include "src/core/SkScalerContext.h"
+#include "src/core/SkTypefaceCache.h"
+#include "src/core/SkUtils.h"
+#include "src/ports/SkScalerContext_mac_ct.h"
+#include "src/ports/SkTypeface_mac_ct.h"
+#include "src/sfnt/SkOTTableTypes.h"
+#include "src/sfnt/SkOTTable_OS_2.h"
+#include "src/sfnt/SkOTTable_OS_2_V4.h"
+#include "src/sfnt/SkOTUtils.h"
+#include "src/sfnt/SkSFNTHeader.h"
+#include "src/utils/SkUTF.h"
+#include "src/utils/mac/SkCGBase.h"
+#include "src/utils/mac/SkCGGeometry.h"
+#include "src/utils/mac/SkCTFontSmoothBehavior.h"
+#include "src/utils/mac/SkUniqueCFRef.h"
+
+#include <dlfcn.h>
+#include <limits.h>
+#include <string.h>
+#include <memory>
+
+// In macOS 10.12 and later any variation on the CGFont which has default axis value will be
+// dropped when creating the CTFont. Unfortunately, in macOS 10.15 the priority of setting
+// the optical size (and opsz variation) is
+// 1. the value of kCTFontOpticalSizeAttribute in the CTFontDescriptor (undocumented)
+// 2. the opsz axis default value if kCTFontOpticalSizeAttribute is 'none' (undocumented)
+// 3. the opsz variation on the nascent CTFont from the CGFont (was dropped if default)
+// 4. the opsz variation in kCTFontVariationAttribute in CTFontDescriptor (crashes 10.10)
+// 5. the size requested (can fudge in SkTypeface but not SkScalerContext)
+// The first one which is found will be used to set the opsz variation (after clamping).
+static void add_opsz_attr(CFMutableDictionaryRef attr, double opsz) {
+    SkUniqueCFRef<CFNumberRef> opszValueNumber(
+        CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &opsz));
+    // Avoid using kCTFontOpticalSizeAttribute directly
+    CFStringRef SkCTFontOpticalSizeAttribute = CFSTR("NSCTFontOpticalSizeAttribute");
+    CFDictionarySetValue(attr, SkCTFontOpticalSizeAttribute, opszValueNumber.get());
+}
+
+// This turns off application of the 'trak' table to advances, but also all other tracking.
+static void add_notrak_attr(CFMutableDictionaryRef attr) {
+    int zero = 0;
+    SkUniqueCFRef<CFNumberRef> unscaledTrackingNumber(
+        CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &zero));
+    CFStringRef SkCTFontUnscaledTrackingAttribute = CFSTR("NSCTFontUnscaledTrackingAttribute");
+    CFDictionarySetValue(attr, SkCTFontUnscaledTrackingAttribute, unscaledTrackingNumber.get());
+}
+
+SkUniqueCFRef<CTFontRef> SkCTFontCreateExactCopy(CTFontRef baseFont, CGFloat textSize,
+                                                 OpszVariation opsz)
+{
+    SkUniqueCFRef<CFMutableDictionaryRef> attr(
+    CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+                              &kCFTypeDictionaryKeyCallBacks,
+                              &kCFTypeDictionaryValueCallBacks));
+
+    if (opsz.isSet) {
+        add_opsz_attr(attr.get(), opsz.value);
+#if !defined(SK_IGNORE_MAC_OPSZ_FORCE)
+    } else {
+        // On (at least) 10.10 though 10.14 the default system font was SFNSText/SFNSDisplay.
+        // The CTFont is backed by both; optical size < 20 means SFNSText else SFNSDisplay.
+        // On at least 10.11 the glyph ids in these fonts became non-interchangable.
+        // To keep glyph ids stable over size changes, preserve the optical size.
+        // In 10.15 this was replaced with use of variable fonts with an opsz axis.
+        // A CTFont backed by multiple fonts picked by opsz where the multiple backing fonts are
+        // variable fonts with opsz axis and non-interchangeable glyph ids would break the
+        // opsz.isSet branch above, but hopefully that never happens.
+        // See https://crbug.com/524646 .
+        CFStringRef SkCTFontOpticalSizeAttribute = CFSTR("NSCTFontOpticalSizeAttribute");
+        SkUniqueCFRef<CFTypeRef> opsz(CTFontCopyAttribute(baseFont, SkCTFontOpticalSizeAttribute));
+        double opsz_val;
+        if (!opsz ||
+            CFGetTypeID(opsz.get()) != CFNumberGetTypeID() ||
+            !CFNumberGetValue(static_cast<CFNumberRef>(opsz.get()),kCFNumberDoubleType,&opsz_val) ||
+            opsz_val <= 0)
+        {
+            opsz_val = CTFontGetSize(baseFont);
+        }
+        add_opsz_attr(attr.get(), opsz_val);
+#endif
+    }
+    add_notrak_attr(attr.get());
+
+    SkUniqueCFRef<CTFontDescriptorRef> desc(CTFontDescriptorCreateWithAttributes(attr.get()));
+
+#if !defined(SK_IGNORE_MAC_OPSZ_FORCE)
+    return SkUniqueCFRef<CTFontRef>(
+            CTFontCreateCopyWithAttributes(baseFont, textSize, nullptr, desc.get()));
+#else
+    SkUniqueCFRef<CGFontRef> baseCGFont(CTFontCopyGraphicsFont(baseFont, nullptr));
+    return SkUniqueCFRef<CTFontRef>(
+            CTFontCreateWithGraphicsFont(baseCGFont.get(), textSize, nullptr, desc.get()));
+
+#endif
+}
+
+CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face) {
+    return face ? (CTFontRef)face->internal_private_getCTFontRef() : nullptr;
+}
+
+static bool find_by_CTFontRef(SkTypeface* cached, void* context) {
+    CTFontRef self = (CTFontRef)context;
+    CTFontRef other = (CTFontRef)cached->internal_private_getCTFontRef();
+
+    return CFEqual(self, other);
+}
+
+/** Creates a typeface, searching the cache if isLocalStream is false. */
+sk_sp<SkTypeface> SkTypeface_Mac::Make(SkUniqueCFRef<CTFontRef> font,
+                                       OpszVariation opszVariation,
+                                       std::unique_ptr<SkStreamAsset> providedData) {
+    static SkMutex gTFCacheMutex;
+    static SkTypefaceCache gTFCache;
+
+    SkASSERT(font);
+    const bool isFromStream(providedData);
+
+    if (!isFromStream) {
+        SkAutoMutexExclusive ama(gTFCacheMutex);
+        sk_sp<SkTypeface> face = gTFCache.findByProcAndRef(find_by_CTFontRef, (void*)font.get());
+        if (face) {
+            return face;
+        }
+    }
+
+    SkUniqueCFRef<CTFontDescriptorRef> desc(CTFontCopyFontDescriptor(font.get()));
+    SkFontStyle style = SkCTFontDescriptorGetSkFontStyle(desc.get(), isFromStream);
+    CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(font.get());
+    bool isFixedPitch = SkToBool(traits & kCTFontMonoSpaceTrait);
+
+    sk_sp<SkTypeface> face(new SkTypeface_Mac(std::move(font), style,
+                                              isFixedPitch, opszVariation,
+                                              std::move(providedData)));
+    if (!isFromStream) {
+        SkAutoMutexExclusive ama(gTFCacheMutex);
+        gTFCache.add(face);
+    }
+    return face;
+}
+
+/*  This function is visible on the outside. It first searches the cache, and if
+ *  not found, returns a new entry (after adding it to the cache).
+ */
+sk_sp<SkTypeface> SkMakeTypefaceFromCTFont(CTFontRef font) {
+    CFRetain(font);
+    return SkTypeface_Mac::Make(SkUniqueCFRef<CTFontRef>(font),
+                                OpszVariation(),
+                                nullptr);
+}
+
+static bool find_dict_CGFloat(CFDictionaryRef dict, CFStringRef name, CGFloat* value) {
+    CFNumberRef num;
+    return CFDictionaryGetValueIfPresent(dict, name, (const void**)&num)
+        && CFNumberIsFloatType(num)
+        && CFNumberGetValue(num, kCFNumberCGFloatType, value);
+}
+
+template <typename S, typename D, typename C> struct LinearInterpolater {
+    struct Mapping {
+        S src_val;
+        D dst_val;
+    };
+    constexpr LinearInterpolater(Mapping const mapping[], int mappingCount)
+        : fMapping(mapping), fMappingCount(mappingCount) {}
+
+    static D map(S value, S src_min, S src_max, D dst_min, D dst_max) {
+        SkASSERT(src_min < src_max);
+        SkASSERT(dst_min <= dst_max);
+        return C()(dst_min + (((value - src_min) * (dst_max - dst_min)) / (src_max - src_min)));
+    }
+
+    D map(S val) const {
+        // -Inf to [0]
+        if (val < fMapping[0].src_val) {
+            return fMapping[0].dst_val;
+        }
+
+        // Linear from [i] to [i+1]
+        for (int i = 0; i < fMappingCount - 1; ++i) {
+            if (val < fMapping[i+1].src_val) {
+                return map(val, fMapping[i].src_val, fMapping[i+1].src_val,
+                                fMapping[i].dst_val, fMapping[i+1].dst_val);
+            }
+        }
+
+        // From [n] to +Inf
+        // if (fcweight < Inf)
+        return fMapping[fMappingCount - 1].dst_val;
+    }
+
+    Mapping const * fMapping;
+    int fMappingCount;
+};
+
+struct RoundCGFloatToInt {
+    int operator()(CGFloat s) { return s + 0.5; }
+};
+struct CGFloatIdentity {
+    CGFloat operator()(CGFloat s) { return s; }
+};
+
+/** Returns the [-1, 1] CTFontDescriptor weights for the
+ *  <0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000> CSS weights.
+ *
+ *  It is assumed that the values will be interpolated linearly between these points.
+ *  NSFontWeightXXX were added in 10.11, appear in 10.10, but do not appear in 10.9.
+ *  The actual values appear to be stable, but they may change in the future without notice.
+ */
+static CGFloat(&get_NSFontWeight_mapping())[11] {
+
+    // Declarations in <AppKit/AppKit.h> on macOS, <UIKit/UIKit.h> on iOS
+#ifdef SK_BUILD_FOR_MAC
+#  define SK_KIT_FONT_WEIGHT_PREFIX "NS"
+#endif
+#ifdef SK_BUILD_FOR_IOS
+#  define SK_KIT_FONT_WEIGHT_PREFIX "UI"
+#endif
+    static constexpr struct {
+        CGFloat defaultValue;
+        const char* name;
+    } nsFontWeightLoaderInfos[] = {
+        { -0.80f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightUltraLight" },
+        { -0.60f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightThin" },
+        { -0.40f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightLight" },
+        {  0.00f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightRegular" },
+        {  0.23f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightMedium" },
+        {  0.30f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightSemibold" },
+        {  0.40f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightBold" },
+        {  0.56f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightHeavy" },
+        {  0.62f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightBlack" },
+    };
+
+    static_assert(SK_ARRAY_COUNT(nsFontWeightLoaderInfos) == 9, "");
+    static CGFloat nsFontWeights[11];
+    static SkOnce once;
+    once([&] {
+        size_t i = 0;
+        nsFontWeights[i++] = -1.00;
+        for (const auto& nsFontWeightLoaderInfo : nsFontWeightLoaderInfos) {
+            void* nsFontWeightValuePtr = dlsym(RTLD_DEFAULT, nsFontWeightLoaderInfo.name);
+            if (nsFontWeightValuePtr) {
+                nsFontWeights[i++] = *(static_cast<CGFloat*>(nsFontWeightValuePtr));
+            } else {
+                nsFontWeights[i++] = nsFontWeightLoaderInfo.defaultValue;
+            }
+        }
+        nsFontWeights[i++] = 1.00;
+    });
+    return nsFontWeights;
+}
+
+/** Convert the [0, 1000] CSS weight to [-1, 1] CTFontDescriptor weight (for system fonts).
+ *
+ *  The -1 to 1 weights reported by CTFontDescriptors have different mappings depending on if the
+ *  CTFont is native or created from a CGDataProvider.
+ */
+CGFloat SkCTFontCTWeightForCSSWeight(int fontstyleWeight) {
+    using Interpolator = LinearInterpolater<int, CGFloat, CGFloatIdentity>;
+
+    // Note that Mac supports the old OS2 version A so 0 through 10 are as if multiplied by 100.
+    // However, on this end we can't tell, so this is ignored.
+
+    static Interpolator::Mapping nativeWeightMappings[11];
+    static SkOnce once;
+    once([&] {
+        CGFloat(&nsFontWeights)[11] = get_NSFontWeight_mapping();
+        for (int i = 0; i < 11; ++i) {
+            nativeWeightMappings[i].src_val = i * 100;
+            nativeWeightMappings[i].dst_val = nsFontWeights[i];
+        }
+    });
+    static constexpr Interpolator nativeInterpolator(
+            nativeWeightMappings, SK_ARRAY_COUNT(nativeWeightMappings));
+
+    return nativeInterpolator.map(fontstyleWeight);
+}
+
+
+/** Convert the [-1, 1] CTFontDescriptor weight to [0, 1000] CSS weight.
+ *
+ *  The -1 to 1 weights reported by CTFontDescriptors have different mappings depending on if the
+ *  CTFont is native or created from a CGDataProvider.
+ */
+static int ct_weight_to_fontstyle(CGFloat cgWeight, bool fromDataProvider) {
+    using Interpolator = LinearInterpolater<CGFloat, int, RoundCGFloatToInt>;
+
+    // Note that Mac supports the old OS2 version A so 0 through 10 are as if multiplied by 100.
+    // However, on this end we can't tell, so this is ignored.
+
+    /** This mapping for CGDataProvider created fonts is determined by creating font data with every
+     *  weight, creating a CTFont, and asking the CTFont for its weight. See the TypefaceStyle test
+     *  in tests/TypefaceTest.cpp for the code used to determine these values.
+     */
+    static constexpr Interpolator::Mapping dataProviderWeightMappings[] = {
+        { -1.00,    0 },
+        { -0.70,  100 },
+        { -0.50,  200 },
+        { -0.23,  300 },
+        {  0.00,  400 },
+        {  0.20,  500 },
+        {  0.30,  600 },
+        {  0.40,  700 },
+        {  0.60,  800 },
+        {  0.80,  900 },
+        {  1.00, 1000 },
+    };
+    static constexpr Interpolator dataProviderInterpolator(
+            dataProviderWeightMappings, SK_ARRAY_COUNT(dataProviderWeightMappings));
+
+    static Interpolator::Mapping nativeWeightMappings[11];
+    static SkOnce once;
+    once([&] {
+        CGFloat(&nsFontWeights)[11] = get_NSFontWeight_mapping();
+        for (int i = 0; i < 11; ++i) {
+            nativeWeightMappings[i].src_val = nsFontWeights[i];
+            nativeWeightMappings[i].dst_val = i * 100;
+        }
+    });
+    static constexpr Interpolator nativeInterpolator(
+            nativeWeightMappings, SK_ARRAY_COUNT(nativeWeightMappings));
+
+    return fromDataProvider ? dataProviderInterpolator.map(cgWeight)
+                            : nativeInterpolator.map(cgWeight);
+}
+
+/** Convert the [0, 10] CSS weight to [-1, 1] CTFontDescriptor width. */
+CGFloat SkCTFontCTWidthForCSSWidth(int fontstyleWidth) {
+    using Interpolator = LinearInterpolater<int, CGFloat, CGFloatIdentity>;
+
+    // Values determined by creating font data with every width, creating a CTFont,
+    // and asking the CTFont for its width. See TypefaceStyle test for basics.
+    static constexpr Interpolator::Mapping widthMappings[] = {
+        {  0, -0.5 },
+        { 10,  0.5 },
+    };
+    static constexpr Interpolator interpolator(widthMappings, SK_ARRAY_COUNT(widthMappings));
+    return interpolator.map(fontstyleWidth);
+}
+
+/** Convert the [-1, 1] CTFontDescriptor width to [0, 10] CSS weight. */
+static int ct_width_to_fontstyle(CGFloat cgWidth) {
+    using Interpolator = LinearInterpolater<CGFloat, int, RoundCGFloatToInt>;
+
+    // Values determined by creating font data with every width, creating a CTFont,
+    // and asking the CTFont for its width. See TypefaceStyle test for basics.
+    static constexpr Interpolator::Mapping widthMappings[] = {
+        { -0.5,  0 },
+        {  0.5, 10 },
+    };
+    static constexpr Interpolator interpolator(widthMappings, SK_ARRAY_COUNT(widthMappings));
+    return interpolator.map(cgWidth);
+}
+
+SkFontStyle SkCTFontDescriptorGetSkFontStyle(CTFontDescriptorRef desc, bool fromDataProvider) {
+    SkUniqueCFRef<CFTypeRef> traits(CTFontDescriptorCopyAttribute(desc, kCTFontTraitsAttribute));
+    if (!traits || CFDictionaryGetTypeID() != CFGetTypeID(traits.get())) {
+        return SkFontStyle();
+    }
+    SkUniqueCFRef<CFDictionaryRef> fontTraitsDict(static_cast<CFDictionaryRef>(traits.release()));
+
+    CGFloat weight, width, slant;
+    if (!find_dict_CGFloat(fontTraitsDict.get(), kCTFontWeightTrait, &weight)) {
+        weight = 0;
+    }
+    if (!find_dict_CGFloat(fontTraitsDict.get(), kCTFontWidthTrait, &width)) {
+        width = 0;
+    }
+    if (!find_dict_CGFloat(fontTraitsDict.get(), kCTFontSlantTrait, &slant)) {
+        slant = 0;
+    }
+
+    return SkFontStyle(ct_weight_to_fontstyle(weight, fromDataProvider),
+                       ct_width_to_fontstyle(width),
+                       slant ? SkFontStyle::kItalic_Slant
+                             : SkFontStyle::kUpright_Slant);
+}
+
+
+// Web fonts added to the CTFont registry do not return their character set.
+// Iterate through the font in this case. The existing caller caches the result,
+// so the performance impact isn't too bad.
+static void populate_glyph_to_unicode_slow(CTFontRef ctFont, CFIndex glyphCount,
+                                           SkUnichar* out) {
+    sk_bzero(out, glyphCount * sizeof(SkUnichar));
+    UniChar unichar = 0;
+    while (glyphCount > 0) {
+        CGGlyph glyph;
+        if (CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
+            if (out[glyph] == 0) {
+                out[glyph] = unichar;
+                --glyphCount;
+            }
+        }
+        if (++unichar == 0) {
+            break;
+        }
+    }
+}
+
+static constexpr uint16_t kPlaneSize = 1 << 13;
+
+static void get_plane_glyph_map(const uint8_t* bits,
+                                CTFontRef ctFont,
+                                CFIndex glyphCount,
+                                SkUnichar* glyphToUnicode,
+                                uint8_t planeIndex) {
+    SkUnichar planeOrigin = (SkUnichar)planeIndex << 16; // top half of codepoint.
+    for (uint16_t i = 0; i < kPlaneSize; i++) {
+        uint8_t mask = bits[i];
+        if (!mask) {
+            continue;
+        }
+        for (uint8_t j = 0; j < 8; j++) {
+            if (0 == (mask & ((uint8_t)1 << j))) {
+                continue;
+            }
+            uint16_t planeOffset = (i << 3) | j;
+            SkUnichar codepoint = planeOrigin | (SkUnichar)planeOffset;
+            uint16_t utf16[2] = {planeOffset, 0};
+            size_t count = 1;
+            if (planeOrigin != 0) {
+                count = SkUTF::ToUTF16(codepoint, utf16);
+            }
+            CGGlyph glyphs[2] = {0, 0};
+            if (CTFontGetGlyphsForCharacters(ctFont, utf16, glyphs, count)) {
+                SkASSERT(glyphs[1] == 0);
+                SkASSERT(glyphs[0] < glyphCount);
+                // CTFontCopyCharacterSet and CTFontGetGlyphsForCharacters seem to add 'support'
+                // for characters 0x9, 0xA, and 0xD mapping them to the glyph for character 0x20?
+                // Prefer mappings to codepoints at or above 0x20.
+                if (glyphToUnicode[glyphs[0]] < 0x20) {
+                    glyphToUnicode[glyphs[0]] = codepoint;
+                }
+            }
+        }
+    }
+}
+// Construct Glyph to Unicode table.
+static void populate_glyph_to_unicode(CTFontRef ctFont, CFIndex glyphCount,
+                                      SkUnichar* glyphToUnicode) {
+    sk_bzero(glyphToUnicode, sizeof(SkUnichar) * glyphCount);
+    SkUniqueCFRef<CFCharacterSetRef> charSet(CTFontCopyCharacterSet(ctFont));
+    if (!charSet) {
+        populate_glyph_to_unicode_slow(ctFont, glyphCount, glyphToUnicode);
+        return;
+    }
+
+    SkUniqueCFRef<CFDataRef> bitmap(
+            CFCharacterSetCreateBitmapRepresentation(nullptr, charSet.get()));
+    if (!bitmap) {
+        return;
+    }
+    CFIndex dataLength = CFDataGetLength(bitmap.get());
+    if (!dataLength) {
+        return;
+    }
+    SkASSERT(dataLength >= kPlaneSize);
+    const UInt8* bits = CFDataGetBytePtr(bitmap.get());
+
+    get_plane_glyph_map(bits, ctFont, glyphCount, glyphToUnicode, 0);
+    /*
+    A CFData object that specifies the bitmap representation of the Unicode
+    character points the for the new character set. The bitmap representation could
+    contain all the Unicode character range starting from BMP to Plane 16. The
+    first 8KiB (8192 bytes) of the data represent the BMP range. The BMP range 8KiB
+    can be followed by zero to sixteen 8KiB bitmaps, each prepended with the plane
+    index byte. For example, the bitmap representing the BMP and Plane 2 has the
+    size of 16385 bytes (8KiB for BMP, 1 byte index, and a 8KiB bitmap for Plane
+    2). The plane index byte, in this case, contains the integer value two.
+    */
+
+    if (dataLength <= kPlaneSize) {
+        return;
+    }
+    int extraPlaneCount = (dataLength - kPlaneSize) / (1 + kPlaneSize);
+    SkASSERT(dataLength == kPlaneSize + extraPlaneCount * (1 + kPlaneSize));
+    while (extraPlaneCount-- > 0) {
+        bits += kPlaneSize;
+        uint8_t planeIndex = *bits++;
+        SkASSERT(planeIndex >= 1);
+        SkASSERT(planeIndex <= 16);
+        get_plane_glyph_map(bits, ctFont, glyphCount, glyphToUnicode, planeIndex);
+    }
+}
+
+/** Assumes src and dst are not nullptr. */
+void SkStringFromCFString(CFStringRef src, SkString* dst) {
+    // Reserve enough room for the worst-case string,
+    // plus 1 byte for the trailing null.
+    CFIndex length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(src),
+                                                       kCFStringEncodingUTF8) + 1;
+    dst->resize(length);
+    CFStringGetCString(src, dst->writable_str(), length, kCFStringEncodingUTF8);
+    // Resize to the actual UTF-8 length used, stripping the null character.
+    dst->resize(strlen(dst->c_str()));
+}
+
+void SkTypeface_Mac::getGlyphToUnicodeMap(SkUnichar* dstArray) const {
+    SkUniqueCFRef<CTFontRef> ctFont =
+            SkCTFontCreateExactCopy(fFontRef.get(), CTFontGetUnitsPerEm(fFontRef.get()),
+                                    fOpszVariation);
+    CFIndex glyphCount = CTFontGetGlyphCount(ctFont.get());
+    populate_glyph_to_unicode(ctFont.get(), glyphCount, dstArray);
+}
+
+std::unique_ptr<SkAdvancedTypefaceMetrics> SkTypeface_Mac::onGetAdvancedMetrics() const {
+
+    SkUniqueCFRef<CTFontRef> ctFont =
+            SkCTFontCreateExactCopy(fFontRef.get(), CTFontGetUnitsPerEm(fFontRef.get()),
+                                    fOpszVariation);
+
+    std::unique_ptr<SkAdvancedTypefaceMetrics> info(new SkAdvancedTypefaceMetrics);
+
+    {
+        SkUniqueCFRef<CFStringRef> fontName(CTFontCopyPostScriptName(ctFont.get()));
+        if (fontName.get()) {
+            SkStringFromCFString(fontName.get(), &info->fPostScriptName);
+            info->fFontName = info->fPostScriptName;
+        }
+    }
+
+    // In 10.10 and earlier, CTFontCopyVariationAxes and CTFontCopyVariation do not work when
+    // applied to fonts which started life with CGFontCreateWithDataProvider (they simply always
+    // return nullptr). As a result, we are limited to CGFontCopyVariationAxes and
+    // CGFontCopyVariations here until support for 10.10 and earlier is removed.
+    SkUniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont.get(), nullptr));
+    if (cgFont) {
+        SkUniqueCFRef<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cgFont.get()));
+        if (cgAxes && CFArrayGetCount(cgAxes.get()) > 0) {
+            info->fFlags |= SkAdvancedTypefaceMetrics::kMultiMaster_FontFlag;
+        }
+    }
+
+    SkOTTableOS2_V4::Type fsType;
+    if (sizeof(fsType) == this->getTableData(SkTEndian_SwapBE32(SkOTTableOS2::TAG),
+                                             offsetof(SkOTTableOS2_V4, fsType),
+                                             sizeof(fsType),
+                                             &fsType)) {
+        SkOTUtils::SetAdvancedTypefaceFlags(fsType, info.get());
+    }
+
+    // If it's not a truetype font, mark it as 'other'. Assume that TrueType
+    // fonts always have both glyf and loca tables. At the least, this is what
+    // sfntly needs to subset the font. CTFontCopyAttribute() does not always
+    // succeed in determining this directly.
+    if (!this->getTableSize('glyf') || !this->getTableSize('loca')) {
+        return info;
+    }
+
+    info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
+    CTFontSymbolicTraits symbolicTraits = CTFontGetSymbolicTraits(ctFont.get());
+    if (symbolicTraits & kCTFontMonoSpaceTrait) {
+        info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
+    }
+    if (symbolicTraits & kCTFontItalicTrait) {
+        info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
+    }
+    CTFontStylisticClass stylisticClass = symbolicTraits & kCTFontClassMaskTrait;
+    if (stylisticClass >= kCTFontOldStyleSerifsClass && stylisticClass <= kCTFontSlabSerifsClass) {
+        info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
+    } else if (stylisticClass & kCTFontScriptsClass) {
+        info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
+    }
+    info->fItalicAngle = (int16_t) CTFontGetSlantAngle(ctFont.get());
+    info->fAscent = (int16_t) CTFontGetAscent(ctFont.get());
+    info->fDescent = (int16_t) CTFontGetDescent(ctFont.get());
+    info->fCapHeight = (int16_t) CTFontGetCapHeight(ctFont.get());
+    CGRect bbox = CTFontGetBoundingBox(ctFont.get());
+
+    SkRect r;
+    r.setLTRB(SkScalarFromCGFloat(SkCGRectGetMinX(bbox)),   // Left
+              SkScalarFromCGFloat(SkCGRectGetMaxY(bbox)),   // Top
+              SkScalarFromCGFloat(SkCGRectGetMaxX(bbox)),   // Right
+              SkScalarFromCGFloat(SkCGRectGetMinY(bbox)));  // Bottom
+
+    r.roundOut(&(info->fBBox));
+
+    // Figure out a good guess for StemV - Min width of i, I, !, 1.
+    // This probably isn't very good with an italic font.
+    int16_t min_width = SHRT_MAX;
+    info->fStemV = 0;
+    static const UniChar stem_chars[] = {'i', 'I', '!', '1'};
+    const size_t count = sizeof(stem_chars) / sizeof(stem_chars[0]);
+    CGGlyph glyphs[count];
+    CGRect boundingRects[count];
+    if (CTFontGetGlyphsForCharacters(ctFont.get(), stem_chars, glyphs, count)) {
+        CTFontGetBoundingRectsForGlyphs(ctFont.get(), kCTFontOrientationHorizontal,
+                                        glyphs, boundingRects, count);
+        for (size_t i = 0; i < count; i++) {
+            int16_t width = (int16_t) boundingRects[i].size.width;
+            if (width > 0 && width < min_width) {
+                min_width = width;
+                info->fStemV = min_width;
+            }
+        }
+    }
+    return info;
+}
+
+static SK_SFNT_ULONG get_font_type_tag(CTFontRef ctFont) {
+    SkUniqueCFRef<CFNumberRef> fontFormatRef(
+            static_cast<CFNumberRef>(CTFontCopyAttribute(ctFont, kCTFontFormatAttribute)));
+    if (!fontFormatRef) {
+        return 0;
+    }
+
+    SInt32 fontFormatValue;
+    if (!CFNumberGetValue(fontFormatRef.get(), kCFNumberSInt32Type, &fontFormatValue)) {
+        return 0;
+    }
+
+    switch (fontFormatValue) {
+        case kCTFontFormatOpenTypePostScript:
+            return SkSFNTHeader::fontType_OpenTypeCFF::TAG;
+        case kCTFontFormatOpenTypeTrueType:
+            return SkSFNTHeader::fontType_WindowsTrueType::TAG;
+        case kCTFontFormatTrueType:
+            return SkSFNTHeader::fontType_MacTrueType::TAG;
+        case kCTFontFormatPostScript:
+            return SkSFNTHeader::fontType_PostScript::TAG;
+        case kCTFontFormatBitmap:
+            return SkSFNTHeader::fontType_MacTrueType::TAG;
+        case kCTFontFormatUnrecognized:
+        default:
+            return 0;
+    }
+}
+
+std::unique_ptr<SkStreamAsset> SkTypeface_Mac::onOpenStream(int* ttcIndex) const {
+    *ttcIndex = 0;
+
+    fInitStream([this]{
+    if (fStream) {
+        return;
+    }
+
+    SK_SFNT_ULONG fontType = get_font_type_tag(fFontRef.get());
+
+    // get table tags
+    int numTables = this->countTables();
+    SkTDArray<SkFontTableTag> tableTags;
+    tableTags.setCount(numTables);
+    this->getTableTags(tableTags.begin());
+
+    // CT seems to be unreliable in being able to obtain the type,
+    // even if all we want is the first four bytes of the font resource.
+    // Just the presence of the FontForge 'FFTM' table seems to throw it off.
+    if (fontType == 0) {
+        fontType = SkSFNTHeader::fontType_WindowsTrueType::TAG;
+
+        // see https://skbug.com/7630#c7
+        bool couldBeCFF = false;
+        constexpr SkFontTableTag CFFTag = SkSetFourByteTag('C', 'F', 'F', ' ');
+        constexpr SkFontTableTag CFF2Tag = SkSetFourByteTag('C', 'F', 'F', '2');
+        for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
+            if (CFFTag == tableTags[tableIndex] || CFF2Tag == tableTags[tableIndex]) {
+                couldBeCFF = true;
+            }
+        }
+        if (couldBeCFF) {
+            fontType = SkSFNTHeader::fontType_OpenTypeCFF::TAG;
+        }
+    }
+
+    // Sometimes CoreGraphics incorrectly thinks a font is kCTFontFormatPostScript.
+    // It is exceedingly unlikely that this is the case, so double check
+    // (see https://crbug.com/809763 ).
+    if (fontType == SkSFNTHeader::fontType_PostScript::TAG) {
+        // see if there are any required 'typ1' tables (see Adobe Technical Note #5180)
+        bool couldBeTyp1 = false;
+        constexpr SkFontTableTag TYPE1Tag = SkSetFourByteTag('T', 'Y', 'P', '1');
+        constexpr SkFontTableTag CIDTag = SkSetFourByteTag('C', 'I', 'D', ' ');
+        for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
+            if (TYPE1Tag == tableTags[tableIndex] || CIDTag == tableTags[tableIndex]) {
+                couldBeTyp1 = true;
+            }
+        }
+        if (!couldBeTyp1) {
+            fontType = SkSFNTHeader::fontType_OpenTypeCFF::TAG;
+        }
+    }
+
+    // get the table sizes and accumulate the total size of the font
+    SkTDArray<size_t> tableSizes;
+    size_t totalSize = sizeof(SkSFNTHeader) + sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
+    for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
+        size_t tableSize = this->getTableSize(tableTags[tableIndex]);
+        totalSize += (tableSize + 3) & ~3;
+        *tableSizes.append() = tableSize;
+    }
+
+    // reserve memory for stream, and zero it (tables must be zero padded)
+    fStream.reset(new SkMemoryStream(totalSize));
+    char* dataStart = (char*)fStream->getMemoryBase();
+    sk_bzero(dataStart, totalSize);
+    char* dataPtr = dataStart;
+
+    // compute font header entries
+    uint16_t entrySelector = 0;
+    uint16_t searchRange = 1;
+    while (searchRange < numTables >> 1) {
+        entrySelector++;
+        searchRange <<= 1;
+    }
+    searchRange <<= 4;
+    uint16_t rangeShift = (numTables << 4) - searchRange;
+
+    // write font header
+    SkSFNTHeader* header = (SkSFNTHeader*)dataPtr;
+    header->fontType = fontType;
+    header->numTables = SkEndian_SwapBE16(numTables);
+    header->searchRange = SkEndian_SwapBE16(searchRange);
+    header->entrySelector = SkEndian_SwapBE16(entrySelector);
+    header->rangeShift = SkEndian_SwapBE16(rangeShift);
+    dataPtr += sizeof(SkSFNTHeader);
+
+    // write tables
+    SkSFNTHeader::TableDirectoryEntry* entry = (SkSFNTHeader::TableDirectoryEntry*)dataPtr;
+    dataPtr += sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
+    for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
+        size_t tableSize = tableSizes[tableIndex];
+        this->getTableData(tableTags[tableIndex], 0, tableSize, dataPtr);
+        entry->tag = SkEndian_SwapBE32(tableTags[tableIndex]);
+        entry->checksum = SkEndian_SwapBE32(SkOTUtils::CalcTableChecksum((SK_OT_ULONG*)dataPtr,
+                                                                         tableSize));
+        entry->offset = SkEndian_SwapBE32(SkToU32(dataPtr - dataStart));
+        entry->logicalLength = SkEndian_SwapBE32(SkToU32(tableSize));
+
+        dataPtr += (tableSize + 3) & ~3;
+        ++entry;
+    }
+    });
+    return fStream->duplicate();
+}
+
+struct NonDefaultAxesContext {
+    SkFixed* axisValue;
+    CFArrayRef cgAxes;
+};
+static void set_non_default_axes(CFTypeRef key, CFTypeRef value, void* context) {
+    NonDefaultAxesContext* self = static_cast<NonDefaultAxesContext*>(context);
+
+    if (CFGetTypeID(key) != CFStringGetTypeID() || CFGetTypeID(value) != CFNumberGetTypeID()) {
+        return;
+    }
+
+    // The key is a CFString which is a string from the 'name' table.
+    // Search the cgAxes for an axis with this name, and use its index to store the value.
+    CFIndex keyIndex = -1;
+    CFStringRef keyString = static_cast<CFStringRef>(key);
+    for (CFIndex i = 0; i < CFArrayGetCount(self->cgAxes); ++i) {
+        CFTypeRef cgAxis = CFArrayGetValueAtIndex(self->cgAxes, i);
+        if (CFGetTypeID(cgAxis) != CFDictionaryGetTypeID()) {
+            continue;
+        }
+
+        CFDictionaryRef cgAxisDict = static_cast<CFDictionaryRef>(cgAxis);
+        CFTypeRef cgAxisName = CFDictionaryGetValue(cgAxisDict, kCGFontVariationAxisName);
+        if (!cgAxisName || CFGetTypeID(cgAxisName) != CFStringGetTypeID()) {
+            continue;
+        }
+        CFStringRef cgAxisNameString = static_cast<CFStringRef>(cgAxisName);
+        if (CFStringCompare(keyString, cgAxisNameString, 0) == kCFCompareEqualTo) {
+            keyIndex = i;
+            break;
+        }
+    }
+    if (keyIndex == -1) {
+        return;
+    }
+
+    CFNumberRef valueNumber = static_cast<CFNumberRef>(value);
+    double valueDouble;
+    if (!CFNumberGetValue(valueNumber, kCFNumberDoubleType, &valueDouble) ||
+        valueDouble < SkFixedToDouble(SK_FixedMin) || SkFixedToDouble(SK_FixedMax) < valueDouble)
+    {
+        return;
+    }
+    self->axisValue[keyIndex] = SkDoubleToFixed(valueDouble);
+}
+static bool get_variations(CTFontRef ctFont, CFIndex* cgAxisCount,
+                           SkAutoSTMalloc<4, SkFixed>* axisValues)
+{
+    // In 10.10 and earlier, CTFontCopyVariationAxes and CTFontCopyVariation do not work when
+    // applied to fonts which started life with CGFontCreateWithDataProvider (they simply always
+    // return nullptr). As a result, we are limited to CGFontCopyVariationAxes and
+    // CGFontCopyVariations here until support for 10.10 and earlier is removed.
+    SkUniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont, nullptr));
+    if (!cgFont) {
+        return false;
+    }
+
+    SkUniqueCFRef<CFDictionaryRef> cgVariations(CGFontCopyVariations(cgFont.get()));
+    // If a font has no variations CGFontCopyVariations returns nullptr (instead of an empty dict).
+    if (!cgVariations) {
+        return false;
+    }
+
+    SkUniqueCFRef<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cgFont.get()));
+    if (!cgAxes) {
+        return false;
+    }
+    *cgAxisCount = CFArrayGetCount(cgAxes.get());
+    axisValues->reset(*cgAxisCount);
+
+    // Set all of the axes to their default values.
+    // Fail if any default value cannot be determined.
+    for (CFIndex i = 0; i < *cgAxisCount; ++i) {
+        CFTypeRef cgAxis = CFArrayGetValueAtIndex(cgAxes.get(), i);
+        if (CFGetTypeID(cgAxis) != CFDictionaryGetTypeID()) {
+            return false;
+        }
+
+        CFDictionaryRef cgAxisDict = static_cast<CFDictionaryRef>(cgAxis);
+        CFTypeRef axisDefaultValue = CFDictionaryGetValue(cgAxisDict,
+                                                          kCGFontVariationAxisDefaultValue);
+        if (!axisDefaultValue || CFGetTypeID(axisDefaultValue) != CFNumberGetTypeID()) {
+            return false;
+        }
+        CFNumberRef axisDefaultValueNumber = static_cast<CFNumberRef>(axisDefaultValue);
+        double axisDefaultValueDouble;
+        if (!CFNumberGetValue(axisDefaultValueNumber, kCFNumberDoubleType, &axisDefaultValueDouble))
+        {
+            return false;
+        }
+        if (axisDefaultValueDouble < SkFixedToDouble(SK_FixedMin) ||
+                                     SkFixedToDouble(SK_FixedMax) < axisDefaultValueDouble)
+        {
+            return false;
+        }
+        (*axisValues)[(int)i] = SkDoubleToFixed(axisDefaultValueDouble);
+    }
+
+    // Override the default values with the given font's stated axis values.
+    NonDefaultAxesContext c = { axisValues->get(), cgAxes.get() };
+    CFDictionaryApplyFunction(cgVariations.get(), set_non_default_axes, &c);
+
+    return true;
+}
+std::unique_ptr<SkFontData> SkTypeface_Mac::onMakeFontData() const {
+    int index;
+    std::unique_ptr<SkStreamAsset> stream(this->onOpenStream(&index));
+
+    CFIndex cgAxisCount;
+    SkAutoSTMalloc<4, SkFixed> axisValues;
+    if (get_variations(fFontRef.get(), &cgAxisCount, &axisValues)) {
+        return std::make_unique<SkFontData>(std::move(stream), index,
+                                              axisValues.get(), cgAxisCount);
+    }
+    return std::make_unique<SkFontData>(std::move(stream), index, nullptr, 0);
+}
+
+/** Creates a CT variation dictionary {tag, value} from a CG variation dictionary {name, value}. */
+static SkUniqueCFRef<CFDictionaryRef> ct_variation_from_cg_variation(CFDictionaryRef cgVariations,
+                                                                     CFArrayRef ctAxes) {
+
+    SkUniqueCFRef<CFMutableDictionaryRef> ctVariation(
+            CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+                                      &kCFTypeDictionaryKeyCallBacks,
+                                      &kCFTypeDictionaryValueCallBacks));
+
+    CFIndex axisCount = CFArrayGetCount(ctAxes);
+    for (CFIndex i = 0; i < axisCount; ++i) {
+        CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes, i);
+        if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
+            return nullptr;
+        }
+        CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
+
+        // The assumption is that values produced by kCTFontVariationAxisNameKey and
+        // kCGFontVariationAxisName will always be equal.
+        CFTypeRef axisName = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisNameKey);
+        if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) {
+            return nullptr;
+        }
+
+        CFTypeRef axisValue = CFDictionaryGetValue(cgVariations, axisName);
+        if (!axisValue || CFGetTypeID(axisValue) != CFNumberGetTypeID()) {
+            return nullptr;
+        }
+
+        CFTypeRef axisTag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey);
+        if (!axisTag || CFGetTypeID(axisTag) != CFNumberGetTypeID()) {
+            return nullptr;
+        }
+
+        CFDictionaryAddValue(ctVariation.get(), axisTag, axisValue);
+    }
+    return std::move(ctVariation);
+}
+
+int SkTypeface_Mac::onGetVariationDesignPosition(
+        SkFontArguments::VariationPosition::Coordinate coordinates[], int coordinateCount) const
+{
+    // The CGFont variation data does not contain the tag.
+
+    // CTFontCopyVariationAxes returns nullptr for CGFontCreateWithDataProvider fonts with
+    // macOS 10.10 and iOS 9 or earlier. When this happens, there is no API to provide the tag.
+    SkUniqueCFRef<CFArrayRef> ctAxes(CTFontCopyVariationAxes(fFontRef.get()));
+    if (!ctAxes) {
+        return -1;
+    }
+    CFIndex axisCount = CFArrayGetCount(ctAxes.get());
+    if (!coordinates || coordinateCount < axisCount) {
+        return axisCount;
+    }
+
+    // This call always returns nullptr on 10.11 and under for CGFontCreateWithDataProvider fonts.
+    // When this happens, try converting the CG variation to a CT variation.
+    // On 10.12 and later, this only returns non-default variations.
+    SkUniqueCFRef<CFDictionaryRef> ctVariation(CTFontCopyVariation(fFontRef.get()));
+    if (!ctVariation) {
+        // When 10.11 and earlier are no longer supported, the following code can be replaced with
+        // return -1 and ct_variation_from_cg_variation can be removed.
+        SkUniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef.get(), nullptr));
+        if (!cgFont) {
+            return -1;
+        }
+        SkUniqueCFRef<CFDictionaryRef> cgVariations(CGFontCopyVariations(cgFont.get()));
+        if (!cgVariations) {
+            return -1;
+        }
+        ctVariation = ct_variation_from_cg_variation(cgVariations.get(), ctAxes.get());
+        if (!ctVariation) {
+            return -1;
+        }
+    }
+
+    for (int i = 0; i < axisCount; ++i) {
+        CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes.get(), i);
+        if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
+            return -1;
+        }
+        CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
+
+        CFTypeRef tag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey);
+        if (!tag || CFGetTypeID(tag) != CFNumberGetTypeID()) {
+            return -1;
+        }
+        CFNumberRef tagNumber = static_cast<CFNumberRef>(tag);
+        int64_t tagLong;
+        if (!CFNumberGetValue(tagNumber, kCFNumberSInt64Type, &tagLong)) {
+            return -1;
+        }
+        coordinates[i].axis = tagLong;
+
+        CGFloat variationCGFloat;
+        CFTypeRef variationValue = CFDictionaryGetValue(ctVariation.get(), tagNumber);
+        if (variationValue) {
+            if (CFGetTypeID(variationValue) != CFNumberGetTypeID()) {
+                return -1;
+            }
+            CFNumberRef variationNumber = static_cast<CFNumberRef>(variationValue);
+            if (!CFNumberGetValue(variationNumber, kCFNumberCGFloatType, &variationCGFloat)) {
+                return -1;
+            }
+        } else {
+            CFTypeRef def = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisDefaultValueKey);
+            if (!def || CFGetTypeID(def) != CFNumberGetTypeID()) {
+                return -1;
+            }
+            CFNumberRef defNumber = static_cast<CFNumberRef>(def);
+            if (!CFNumberGetValue(defNumber, kCFNumberCGFloatType, &variationCGFloat)) {
+                return -1;
+            }
+        }
+        coordinates[i].value = SkScalarFromCGFloat(variationCGFloat);
+
+    }
+    return axisCount;
+}
+
+int SkTypeface_Mac::onGetUPEM() const {
+    SkUniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef.get(), nullptr));
+    return CGFontGetUnitsPerEm(cgFont.get());
+}
+
+SkTypeface::LocalizedStrings* SkTypeface_Mac::onCreateFamilyNameIterator() const {
+    sk_sp<SkTypeface::LocalizedStrings> nameIter =
+            SkOTUtils::LocalizedStrings_NameTable::MakeForFamilyNames(*this);
+    if (!nameIter) {
+        CFStringRef cfLanguageRaw;
+        SkUniqueCFRef<CFStringRef> cfFamilyName(
+                CTFontCopyLocalizedName(fFontRef.get(), kCTFontFamilyNameKey, &cfLanguageRaw));
+        SkUniqueCFRef<CFStringRef> cfLanguage(cfLanguageRaw);
+
+        SkString skLanguage;
+        SkString skFamilyName;
+        if (cfLanguage) {
+            SkStringFromCFString(cfLanguage.get(), &skLanguage);
+        } else {
+            skLanguage = "und"; //undetermined
+        }
+        if (cfFamilyName) {
+            SkStringFromCFString(cfFamilyName.get(), &skFamilyName);
+        }
+
+        nameIter = sk_make_sp<SkOTUtils::LocalizedStrings_SingleName>(skFamilyName, skLanguage);
+    }
+    return nameIter.release();
+}
+
+int SkTypeface_Mac::onGetTableTags(SkFontTableTag tags[]) const {
+    SkUniqueCFRef<CFArrayRef> cfArray(
+            CTFontCopyAvailableTables(fFontRef.get(), kCTFontTableOptionNoOptions));
+    if (!cfArray) {
+        return 0;
+    }
+    int count = SkToInt(CFArrayGetCount(cfArray.get()));
+    if (tags) {
+        for (int i = 0; i < count; ++i) {
+            uintptr_t fontTag = reinterpret_cast<uintptr_t>(
+                CFArrayGetValueAtIndex(cfArray.get(), i));
+            tags[i] = static_cast<SkFontTableTag>(fontTag);
+        }
+    }
+    return count;
+}
+
+// If, as is the case with web fonts, the CTFont data isn't available,
+// the CGFont data may work. While the CGFont may always provide the
+// right result, leave the CTFont code path to minimize disruption.
+static SkUniqueCFRef<CFDataRef> copy_table_from_font(CTFontRef ctFont, SkFontTableTag tag) {
+    SkUniqueCFRef<CFDataRef> data(CTFontCopyTable(ctFont, (CTFontTableTag) tag,
+                                                  kCTFontTableOptionNoOptions));
+    if (!data) {
+        SkUniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont, nullptr));
+        data.reset(CGFontCopyTableForTag(cgFont.get(), tag));
+    }
+    return data;
+}
+
+size_t SkTypeface_Mac::onGetTableData(SkFontTableTag tag, size_t offset,
+                                      size_t length, void* dstData) const {
+    SkUniqueCFRef<CFDataRef> srcData = copy_table_from_font(fFontRef.get(), tag);
+    if (!srcData) {
+        return 0;
+    }
+
+    size_t srcSize = CFDataGetLength(srcData.get());
+    if (offset >= srcSize) {
+        return 0;
+    }
+    if (length > srcSize - offset) {
+        length = srcSize - offset;
+    }
+    if (dstData) {
+        memcpy(dstData, CFDataGetBytePtr(srcData.get()) + offset, length);
+    }
+    return length;
+}
+
+sk_sp<SkData> SkTypeface_Mac::onCopyTableData(SkFontTableTag tag) const {
+    SkUniqueCFRef<CFDataRef> srcData = copy_table_from_font(fFontRef.get(), tag);
+    if (!srcData) {
+        return nullptr;
+    }
+    const UInt8* data = CFDataGetBytePtr(srcData.get());
+    CFIndex length = CFDataGetLength(srcData.get());
+    return SkData::MakeWithProc(data, length,
+                                [](const void*, void* ctx) {
+                                    CFRelease((CFDataRef)ctx);
+                                }, (void*)srcData.release());
+}
+
+SkScalerContext* SkTypeface_Mac::onCreateScalerContext(const SkScalerContextEffects& effects,
+                                                       const SkDescriptor* desc) const {
+    return new SkScalerContext_Mac(sk_ref_sp(const_cast<SkTypeface_Mac*>(this)), effects, desc);
+}
+
+void SkTypeface_Mac::onFilterRec(SkScalerContextRec* rec) const {
+    if (rec->fFlags & SkScalerContext::kLCD_BGROrder_Flag ||
+        rec->fFlags & SkScalerContext::kLCD_Vertical_Flag)
+    {
+        rec->fMaskFormat = SkMask::kA8_Format;
+        // Render the glyphs as close as possible to what was requested.
+        // The above turns off subpixel rendering, but the user requested it.
+        // Normal hinting will cause the A8 masks to be generated from CoreGraphics subpixel masks.
+        // See comments below for more details.
+        rec->setHinting(SkFontHinting::kNormal);
+    }
+
+    unsigned flagsWeDontSupport = SkScalerContext::kForceAutohinting_Flag  |
+                                  SkScalerContext::kLCD_BGROrder_Flag |
+                                  SkScalerContext::kLCD_Vertical_Flag;
+
+    rec->fFlags &= ~flagsWeDontSupport;
+
+    const SkCTFontSmoothBehavior smoothBehavior = SkCTFontGetSmoothBehavior();
+
+    // Only two levels of hinting are supported.
+    // kNo_Hinting means avoid CoreGraphics outline dilation (smoothing).
+    // kNormal_Hinting means CoreGraphics outline dilation (smoothing) is allowed.
+    if (rec->getHinting() != SkFontHinting::kNone) {
+        rec->setHinting(SkFontHinting::kNormal);
+    }
+    // If smoothing has no effect, don't request it.
+    if (smoothBehavior == SkCTFontSmoothBehavior::none) {
+        rec->setHinting(SkFontHinting::kNone);
+    }
+
+    // FIXME: lcd smoothed un-hinted rasterization unsupported.
+    // Tracked by http://code.google.com/p/skia/issues/detail?id=915 .
+    // There is no current means to honor a request for unhinted lcd,
+    // so arbitrarilly ignore the hinting request and honor lcd.
+
+    // Hinting and smoothing should be orthogonal, but currently they are not.
+    // CoreGraphics has no API to influence hinting. However, its lcd smoothed
+    // output is drawn from auto-dilated outlines (the amount of which is
+    // determined by AppleFontSmoothing). Its regular anti-aliased output is
+    // drawn from un-dilated outlines.
+
+    // The behavior of Skia is as follows:
+    // [AA][no-hint]: generate AA using CoreGraphic's AA output.
+    // [AA][yes-hint]: use CoreGraphic's LCD output and reduce it to a single
+    // channel. This matches [LCD][yes-hint] in weight.
+    // [LCD][no-hint]: currently unable to honor, and must pick which to respect.
+    // Currently side with LCD, effectively ignoring the hinting setting.
+    // [LCD][yes-hint]: generate LCD using CoreGraphic's LCD output.
+    if (rec->fMaskFormat == SkMask::kLCD16_Format) {
+        if (smoothBehavior == SkCTFontSmoothBehavior::subpixel) {
+            //CoreGraphics creates 555 masks for smoothed text anyway.
+            rec->fMaskFormat = SkMask::kLCD16_Format;
+            rec->setHinting(SkFontHinting::kNormal);
+        } else {
+            rec->fMaskFormat = SkMask::kA8_Format;
+            if (smoothBehavior != SkCTFontSmoothBehavior::none) {
+                rec->setHinting(SkFontHinting::kNormal);
+            }
+        }
+    }
+
+    // CoreText provides no information as to whether a glyph will be color or not.
+    // Fonts may mix outlines and bitmaps, so information is needed on a glyph by glyph basis.
+    // If a font contains an 'sbix' table, consider it to be a color font, and disable lcd.
+    if (fHasColorGlyphs) {
+        rec->fMaskFormat = SkMask::kARGB32_Format;
+    }
+
+    // Unhinted A8 masks (those not derived from LCD masks) must respect SK_GAMMA_APPLY_TO_A8.
+    // All other masks can use regular gamma.
+    if (SkMask::kA8_Format == rec->fMaskFormat && SkFontHinting::kNone == rec->getHinting()) {
+#ifndef SK_GAMMA_APPLY_TO_A8
+        // SRGBTODO: Is this correct? Do we want contrast boost?
+        rec->ignorePreBlend();
+#endif
+    } else {
+        SkColor color = rec->getLuminanceColor();
+        if (smoothBehavior == SkCTFontSmoothBehavior::some) {
+            // CoreGraphics smoothed text without subpixel coverage blitting goes from a gamma of
+            // 2.0 for black foreground to a gamma of 1.0 for white foreground. Emulate this
+            // through the mask gamma by reducing the color values to 1/2.
+            color = SkColorSetRGB(SkColorGetR(color) * 1/2,
+                                  SkColorGetG(color) * 1/2,
+                                  SkColorGetB(color) * 1/2);
+        } else if (smoothBehavior == SkCTFontSmoothBehavior::subpixel) {
+            // CoreGraphics smoothed text with subpixel coverage blitting goes from a gamma of
+            // 2.0 for black foreground to a gamma of ~1.4? for white foreground. Emulate this
+            // through the mask gamma by reducing the color values to 3/4.
+            color = SkColorSetRGB(SkColorGetR(color) * 3/4,
+                                  SkColorGetG(color) * 3/4,
+                                  SkColorGetB(color) * 3/4);
+        }
+        rec->setLuminanceColor(color);
+
+        // CoreGraphics dialates smoothed text to provide contrast.
+        rec->setContrast(0);
+    }
+}
+
+/** Takes ownership of the CFStringRef. */
+static const char* get_str(CFStringRef ref, SkString* str) {
+    if (nullptr == ref) {
+        return nullptr;
+    }
+    SkStringFromCFString(ref, str);
+    CFRelease(ref);
+    return str->c_str();
+}
+
+void SkTypeface_Mac::onGetFamilyName(SkString* familyName) const {
+    get_str(CTFontCopyFamilyName(fFontRef.get()), familyName);
+}
+
+void SkTypeface_Mac::onGetFontDescriptor(SkFontDescriptor* desc,
+                                         bool* isLocalStream) const {
+    SkString tmpStr;
+
+    desc->setFamilyName(get_str(CTFontCopyFamilyName(fFontRef.get()), &tmpStr));
+    desc->setFullName(get_str(CTFontCopyFullName(fFontRef.get()), &tmpStr));
+    desc->setPostscriptName(get_str(CTFontCopyPostScriptName(fFontRef.get()), &tmpStr));
+    desc->setStyle(this->fontStyle());
+    *isLocalStream = fIsFromStream;
+}
+
+void SkTypeface_Mac::onCharsToGlyphs(const SkUnichar uni[], int count, SkGlyphID glyphs[]) const {
+    // Undocumented behavior of CTFontGetGlyphsForCharacters with non-bmp code points:
+    // When a surrogate pair is detected, the glyph index used is the index of the high surrogate.
+    // It is documented that if a mapping is unavailable, the glyph will be set to 0.
+
+    SkAutoSTMalloc<1024, UniChar> charStorage;
+    const UniChar* src; // UniChar is a UTF-16 16-bit code unit.
+    int srcCount;
+    const SkUnichar* utf32 = reinterpret_cast<const SkUnichar*>(uni);
+    UniChar* utf16 = charStorage.reset(2 * count);
+    src = utf16;
+    for (int i = 0; i < count; ++i) {
+        utf16 += SkUTF::ToUTF16(utf32[i], utf16);
+    }
+    srcCount = SkToInt(utf16 - src);
+
+    // If there are any non-bmp code points, the provided 'glyphs' storage will be inadequate.
+    SkAutoSTMalloc<1024, uint16_t> glyphStorage;
+    uint16_t* macGlyphs = glyphs;
+    if (srcCount > count) {
+        macGlyphs = glyphStorage.reset(srcCount);
+    }
+
+    CTFontGetGlyphsForCharacters(fFontRef.get(), src, macGlyphs, srcCount);
+
+    // If there were any non-bmp, then copy and compact.
+    // If all are bmp, 'glyphs' already contains the compact glyphs.
+    // If some are non-bmp, copy and compact into 'glyphs'.
+    if (srcCount > count) {
+        SkASSERT(glyphs != macGlyphs);
+        int extra = 0;
+        for (int i = 0; i < count; ++i) {
+            glyphs[i] = macGlyphs[i + extra];
+            if (SkUTF16_IsLeadingSurrogate(src[i + extra])) {
+                ++extra;
+            }
+        }
+    } else {
+        SkASSERT(glyphs == macGlyphs);
+    }
+}
+
+int SkTypeface_Mac::onCountGlyphs() const {
+    return SkToInt(CTFontGetGlyphCount(fFontRef.get()));
+}
+
+/** Creates a dictionary suitable for setting the axes on a CTFont. */
+CTFontVariation SkCTVariationFromSkFontArguments(CTFontRef ct, const SkFontArguments& args) {
+    OpszVariation opsz;
+    constexpr const SkFourByteTag opszTag = SkSetFourByteTag('o','p','s','z');
+
+    SkUniqueCFRef<CFArrayRef> ctAxes(CTFontCopyVariationAxes(ct));
+    if (!ctAxes) {
+        return CTFontVariation();
+    }
+    CFIndex axisCount = CFArrayGetCount(ctAxes.get());
+
+    const SkFontArguments::VariationPosition position = args.getVariationDesignPosition();
+
+    SkUniqueCFRef<CFMutableDictionaryRef> dict(
+            CFDictionaryCreateMutable(kCFAllocatorDefault, axisCount,
+                                      &kCFTypeDictionaryKeyCallBacks,
+                                      &kCFTypeDictionaryValueCallBacks));
+
+    for (int i = 0; i < axisCount; ++i) {
+        CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes.get(), i);
+        if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
+            return CTFontVariation();
+        }
+        CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
+
+        CFTypeRef tag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey);
+        if (!tag || CFGetTypeID(tag) != CFNumberGetTypeID()) {
+            return CTFontVariation();
+        }
+        CFNumberRef tagNumber = static_cast<CFNumberRef>(tag);
+        int64_t tagLong;
+        if (!CFNumberGetValue(tagNumber, kCFNumberSInt64Type, &tagLong)) {
+            return CTFontVariation();
+        }
+
+        // The variation axes can be set to any value, but cg will effectively pin them.
+        // Pin them here to normalize.
+        CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMinimumValueKey);
+        CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMaximumValueKey);
+        CFTypeRef def = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisDefaultValueKey);
+        if (!min || CFGetTypeID(min) != CFNumberGetTypeID() ||
+            !max || CFGetTypeID(max) != CFNumberGetTypeID() ||
+            !def || CFGetTypeID(def) != CFNumberGetTypeID())
+        {
+            return CTFontVariation();
+        }
+        CFNumberRef minNumber = static_cast<CFNumberRef>(min);
+        CFNumberRef maxNumber = static_cast<CFNumberRef>(max);
+        CFNumberRef defNumber = static_cast<CFNumberRef>(def);
+        double minDouble;
+        double maxDouble;
+        double defDouble;
+        if (!CFNumberGetValue(minNumber, kCFNumberDoubleType, &minDouble) ||
+            !CFNumberGetValue(maxNumber, kCFNumberDoubleType, &maxDouble) ||
+            !CFNumberGetValue(defNumber, kCFNumberDoubleType, &defDouble))
+        {
+            return CTFontVariation();
+        }
+
+        double value = defDouble;
+        // The position may be over specified. If there are multiple values for a given axis,
+        // use the last one since that's what css-fonts-4 requires.
+        for (int j = position.coordinateCount; j --> 0;) {
+            if (position.coordinates[j].axis == tagLong) {
+                value = SkTPin(SkScalarToDouble(position.coordinates[j].value),
+                               minDouble, maxDouble);
+                if (tagLong == opszTag) {
+                    opsz.isSet = true;
+                }
+                break;
+            }
+        }
+        if (tagLong == opszTag) {
+            opsz.value = value;
+        }
+        SkUniqueCFRef<CFNumberRef> valueNumber(
+            CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &value));
+        CFDictionaryAddValue(dict.get(), tagNumber, valueNumber.get());
+    }
+    return { SkUniqueCFRef<CFDictionaryRef>(std::move(dict)), opsz };
+}
+
+sk_sp<SkTypeface> SkTypeface_Mac::onMakeClone(const SkFontArguments& args) const {
+    CTFontVariation ctVariation = SkCTVariationFromSkFontArguments(fFontRef.get(), args);
+
+    SkUniqueCFRef<CTFontRef> ctVariant;
+    if (ctVariation.dict) {
+        SkUniqueCFRef<CFMutableDictionaryRef> attributes(
+                CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+                                          &kCFTypeDictionaryKeyCallBacks,
+                                          &kCFTypeDictionaryValueCallBacks));
+        CFDictionaryAddValue(attributes.get(),
+                             kCTFontVariationAttribute, ctVariation.dict.get());
+        SkUniqueCFRef<CTFontDescriptorRef> varDesc(
+                CTFontDescriptorCreateWithAttributes(attributes.get()));
+        ctVariant.reset(CTFontCreateCopyWithAttributes(fFontRef.get(), 0, nullptr, varDesc.get()));
+    } else {
+        ctVariant.reset((CTFontRef)CFRetain(fFontRef.get()));
+    }
+    if (!ctVariant) {
+        return nullptr;
+    }
+
+    return SkTypeface_Mac::Make(std::move(ctVariant), ctVariation.opsz,
+                                fStream ? fStream->duplicate() : nullptr);
+}
+
+int SkTypeface_Mac::onGetVariationDesignParameters(SkFontParameters::Variation::Axis parameters[],
+                                                   int parameterCount) const
+{
+    SkUniqueCFRef<CFArrayRef> ctAxes(CTFontCopyVariationAxes(fFontRef.get()));
+    if (!ctAxes) {
+        return -1;
+    }
+    CFIndex axisCount = CFArrayGetCount(ctAxes.get());
+
+    if (!parameters || parameterCount < axisCount) {
+        return axisCount;
+    }
+
+    // Added in 10.13
+    CFStringRef* kCTFontVariationAxisHiddenKeyPtr =
+            static_cast<CFStringRef*>(dlsym(RTLD_DEFAULT, "kCTFontVariationAxisHiddenKey"));
+
+    for (int i = 0; i < axisCount; ++i) {
+        CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes.get(), i);
+        if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
+            return -1;
+        }
+        CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
+
+        CFTypeRef tag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey);
+        if (!tag || CFGetTypeID(tag) != CFNumberGetTypeID()) {
+            return -1;
+        }
+        CFNumberRef tagNumber = static_cast<CFNumberRef>(tag);
+        int64_t tagLong;
+        if (!CFNumberGetValue(tagNumber, kCFNumberSInt64Type, &tagLong)) {
+            return -1;
+        }
+
+        CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMinimumValueKey);
+        CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMaximumValueKey);
+        CFTypeRef def = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisDefaultValueKey);
+        if (!min || CFGetTypeID(min) != CFNumberGetTypeID() ||
+            !max || CFGetTypeID(max) != CFNumberGetTypeID() ||
+            !def || CFGetTypeID(def) != CFNumberGetTypeID())
+        {
+            return -1;
+        }
+        CFNumberRef minNumber = static_cast<CFNumberRef>(min);
+        CFNumberRef maxNumber = static_cast<CFNumberRef>(max);
+        CFNumberRef defNumber = static_cast<CFNumberRef>(def);
+        double minDouble;
+        double maxDouble;
+        double defDouble;
+        if (!CFNumberGetValue(minNumber, kCFNumberDoubleType, &minDouble) ||
+            !CFNumberGetValue(maxNumber, kCFNumberDoubleType, &maxDouble) ||
+            !CFNumberGetValue(defNumber, kCFNumberDoubleType, &defDouble))
+        {
+            return -1;
+        }
+
+        SkFontParameters::Variation::Axis& skAxis = parameters[i];
+        skAxis.tag = tagLong;
+        skAxis.min = minDouble;
+        skAxis.max = maxDouble;
+        skAxis.def = defDouble;
+        skAxis.setHidden(false);
+        if (kCTFontVariationAxisHiddenKeyPtr) {
+            CFTypeRef hidden = CFDictionaryGetValue(axisInfoDict,*kCTFontVariationAxisHiddenKeyPtr);
+            if (hidden) {
+                if (CFGetTypeID(hidden) != CFBooleanGetTypeID()) {
+                    return -1;
+                }
+                CFBooleanRef hiddenBoolean = static_cast<CFBooleanRef>(hidden);
+                skAxis.setHidden(CFBooleanGetValue(hiddenBoolean));
+            }
+        }
+    }
+    return axisCount;
+}
+
+#endif
diff --git a/src/ports/SkTypeface_mac_ct.h b/src/ports/SkTypeface_mac_ct.h
new file mode 100644
index 0000000..49f55b3
--- /dev/null
+++ b/src/ports/SkTypeface_mac_ct.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2020 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkTypeface_mac_ct_DEFINED
+#define SkTypeface_mac_ct_DEFINED
+
+#include "include/core/SkTypes.h"
+#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
+
+#include "include/core/SkFontArguments.h"
+#include "include/core/SkFontParameters.h"
+#include "include/core/SkFontStyle.h"
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkScalar.h"
+#include "include/core/SkStream.h"
+#include "include/core/SkTypeface.h"
+#include "include/private/SkOnce.h"
+#include "src/utils/mac/SkUniqueCFRef.h"
+
+#ifdef SK_BUILD_FOR_MAC
+#import <ApplicationServices/ApplicationServices.h>
+#endif
+
+#ifdef SK_BUILD_FOR_IOS
+#include <CoreText/CoreText.h>
+#include <CoreText/CTFontManager.h>
+#include <CoreGraphics/CoreGraphics.h>
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
+#include <memory>
+
+class SkData;
+class SkDescriptor;
+class SkFontData;
+class SkFontDescriptor;
+class SkScalerContext;
+class SkString;
+struct SkAdvancedTypefaceMetrics;
+struct SkScalerContextEffects;
+struct SkScalerContextRec;
+
+struct OpszVariation {
+    bool isSet = false;
+    double value = 0;
+};
+
+struct CTFontVariation {
+    SkUniqueCFRef<CFDictionaryRef> dict;
+    OpszVariation opsz;
+};
+
+CTFontVariation SkCTVariationFromSkFontArguments(CTFontRef ct, const SkFontArguments& args);
+
+SkUniqueCFRef<CTFontRef> SkCTFontCreateExactCopy(CTFontRef baseFont, CGFloat textSize,
+                                                 OpszVariation opsz);
+
+SkFontStyle SkCTFontDescriptorGetSkFontStyle(CTFontDescriptorRef desc, bool fromDataProvider);
+
+CGFloat SkCTFontCTWeightForCSSWeight(int fontstyleWeight);
+CGFloat SkCTFontCTWidthForCSSWidth(int fontstyleWidth);
+
+void SkStringFromCFString(CFStringRef src, SkString* dst);
+
+class SkTypeface_Mac : public SkTypeface {
+private:
+    SkTypeface_Mac(SkUniqueCFRef<CTFontRef> fontRef, const SkFontStyle& fs, bool isFixedPitch,
+                   OpszVariation opszVariation, std::unique_ptr<SkStreamAsset> providedData)
+        : SkTypeface(fs, isFixedPitch)
+        , fFontRef(std::move(fontRef))
+        , fOpszVariation(opszVariation)
+        , fHasColorGlyphs(
+                SkToBool(CTFontGetSymbolicTraits(fFontRef.get()) & kCTFontColorGlyphsTrait))
+        , fStream(std::move(providedData))
+        , fIsFromStream(fStream)
+    {
+        SkASSERT(fFontRef);
+    }
+
+public:
+    static sk_sp<SkTypeface> Make(SkUniqueCFRef<CTFontRef> font,
+                                  OpszVariation opszVariation,
+                                  std::unique_ptr<SkStreamAsset> providedData);
+
+    SkUniqueCFRef<CTFontRef> fFontRef;
+    const OpszVariation fOpszVariation;
+    const bool fHasColorGlyphs;
+
+protected:
+    int onGetUPEM() const override;
+    std::unique_ptr<SkStreamAsset> onOpenStream(int* ttcIndex) const override;
+    std::unique_ptr<SkFontData> onMakeFontData() const override;
+    int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[],
+                                     int coordinateCount) const override;
+    void onGetFamilyName(SkString* familyName) const override;
+    SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override;
+    int onGetTableTags(SkFontTableTag tags[]) const override;
+    size_t onGetTableData(SkFontTableTag, size_t offset, size_t length, void* data) const override;
+    sk_sp<SkData> onCopyTableData(SkFontTableTag) const override;
+    SkScalerContext* onCreateScalerContext(const SkScalerContextEffects&,
+                                           const SkDescriptor*) const override;
+    void onFilterRec(SkScalerContextRec*) const override;
+    void onGetFontDescriptor(SkFontDescriptor*, bool*) const override;
+    void getGlyphToUnicodeMap(SkUnichar*) const override;
+    std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override;
+    void onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const override;
+    int onCountGlyphs() const override;
+    void getPostScriptGlyphNames(SkString*) const override {}
+    int onGetVariationDesignParameters(SkFontParameters::Variation::Axis parameters[],
+                                       int parameterCount) const override;
+    sk_sp<SkTypeface> onMakeClone(const SkFontArguments&) const override;
+
+    void* onGetCTFontRef() const override { return (void*)fFontRef.get(); }
+
+private:
+    mutable std::unique_ptr<SkStreamAsset> fStream;
+    bool fIsFromStream;
+    mutable SkOnce fInitStream;
+
+    typedef SkTypeface INHERITED;
+};
+
+#endif
+#endif //SkTypeface_mac_ct_DEFINED
diff --git a/src/utils/mac/SkCGBase.h b/src/utils/mac/SkCGBase.h
new file mode 100644
index 0000000..4874a72f
--- /dev/null
+++ b/src/utils/mac/SkCGBase.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2020 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkCGBase_DEFINED
+#define SkCGBase_DEFINED
+
+#include "include/core/SkTypes.h"
+#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
+
+#ifdef SK_BUILD_FOR_MAC
+#import <ApplicationServices/ApplicationServices.h>
+#endif
+
+#ifdef SK_BUILD_FOR_IOS
+#include <CoreGraphics/CoreGraphics.h>
+#endif
+
+// Skia extensions for types in CGBase.h
+
+static inline CGFloat SkScalarToCGFloat(SkScalar scalar) {
+    if (sizeof(CGFloat) == sizeof(float)) {
+        return SkScalarToFloat(scalar);
+    } else {
+        SkASSERT(sizeof(CGFloat) == sizeof(double));
+        return (CGFloat) SkScalarToDouble(scalar);
+    }
+}
+
+static inline SkScalar SkScalarFromCGFloat(CGFloat cgFloat) {
+    if (sizeof(CGFloat) == sizeof(float)) {
+        return SkFloatToScalar(cgFloat);
+    } else {
+        SkASSERT(sizeof(CGFloat) == sizeof(double));
+        return SkDoubleToScalar(cgFloat);
+    }
+}
+
+static inline float SkFloatFromCGFloat(CGFloat cgFloat) {
+    if (sizeof(CGFloat) == sizeof(float)) {
+        return cgFloat;
+    } else {
+        SkASSERT(sizeof(CGFloat) == sizeof(double));
+        return static_cast<float>(cgFloat);
+    }
+}
+
+#endif
+#endif //SkCGBase_DEFINED
diff --git a/src/utils/mac/SkCGGeometry.h b/src/utils/mac/SkCGGeometry.h
new file mode 100644
index 0000000..04b1263
--- /dev/null
+++ b/src/utils/mac/SkCGGeometry.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2020 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkCGGeometry_DEFINED
+#define SkCGGeometry_DEFINED
+
+#include "include/core/SkTypes.h"
+#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
+
+#ifdef SK_BUILD_FOR_MAC
+#import <ApplicationServices/ApplicationServices.h>
+#endif
+
+#ifdef SK_BUILD_FOR_IOS
+#include <CoreGraphics/CoreGraphics.h>
+#endif
+
+// Skia extensions for types in CGGeometry.h
+
+// Inline versions of these CGRect helpers.
+// The CG versions require making a call and a copy of the CGRect on the stack.
+
+static inline bool SkCGRectIsEmpty(const CGRect& rect) {
+    return rect.size.width <= 0 || rect.size.height <= 0;
+}
+
+static inline CGFloat SkCGRectGetMinX(const CGRect& rect) {
+    return rect.origin.x;
+}
+
+static inline CGFloat SkCGRectGetMaxX(const CGRect& rect) {
+    return rect.origin.x + rect.size.width;
+}
+
+static inline CGFloat SkCGRectGetMinY(const CGRect& rect) {
+    return rect.origin.y;
+}
+
+static inline CGFloat SkCGRectGetMaxY(const CGRect& rect) {
+    return rect.origin.y + rect.size.height;
+}
+
+static inline CGFloat SkCGRectGetWidth(const CGRect& rect) {
+    return rect.size.width;
+}
+
+#endif
+#endif //SkCGGeometry_DEFINED
diff --git a/src/utils/mac/SkCTFontSmoothBehavior.cpp b/src/utils/mac/SkCTFontSmoothBehavior.cpp
new file mode 100644
index 0000000..7836706
--- /dev/null
+++ b/src/utils/mac/SkCTFontSmoothBehavior.cpp
@@ -0,0 +1,270 @@
+/*
+ * Copyright 2020 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "include/core/SkTypes.h"
+
+#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
+
+#include "src/utils/mac/SkCTFontSmoothBehavior.h"
+#include "src/utils/mac/SkUniqueCFRef.h"
+
+#ifdef SK_BUILD_FOR_MAC
+#import <ApplicationServices/ApplicationServices.h>
+#endif
+
+#ifdef SK_BUILD_FOR_IOS
+#include <CoreText/CoreText.h>
+#include <CoreText/CTFontManager.h>
+#include <CoreGraphics/CoreGraphics.h>
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
+#define BITMAP_INFO_RGB (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host)
+
+/** Drawn in FontForge, reduced with fonttools ttx, converted by xxd -i,
+ *  this TrueType font contains a glyph of the spider.
+ *
+ *  To re-forge the original bytes of the TrueType font file,
+ *  remove all ',|( +0x)' from this definition,
+ *  copy the data to the clipboard,
+ *  run 'pbpaste | xxd -p -r - spider.ttf'.
+ */
+static constexpr const uint8_t kSpiderSymbol_ttf[] = {
+    0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x80, 0x00, 0x03, 0x00, 0x40,
+    0x47, 0x44, 0x45, 0x46, 0x00, 0x14, 0x00, 0x14, 0x00, 0x00, 0x07, 0xa8,
+    0x00, 0x00, 0x00, 0x18, 0x4f, 0x53, 0x2f, 0x32, 0x8a, 0xf4, 0xfb, 0xdb,
+    0x00, 0x00, 0x01, 0x48, 0x00, 0x00, 0x00, 0x60, 0x63, 0x6d, 0x61, 0x70,
+    0xe0, 0x7f, 0x10, 0x7e, 0x00, 0x00, 0x01, 0xb8, 0x00, 0x00, 0x00, 0x54,
+    0x67, 0x61, 0x73, 0x70, 0xff, 0xff, 0x00, 0x03, 0x00, 0x00, 0x07, 0xa0,
+    0x00, 0x00, 0x00, 0x08, 0x67, 0x6c, 0x79, 0x66, 0x97, 0x0b, 0x6a, 0xf6,
+    0x00, 0x00, 0x02, 0x18, 0x00, 0x00, 0x03, 0x40, 0x68, 0x65, 0x61, 0x64,
+    0x0f, 0xa2, 0x24, 0x1a, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x36,
+    0x68, 0x68, 0x65, 0x61, 0x0e, 0xd3, 0x07, 0x3f, 0x00, 0x00, 0x01, 0x04,
+    0x00, 0x00, 0x00, 0x24, 0x68, 0x6d, 0x74, 0x78, 0x10, 0x03, 0x00, 0x44,
+    0x00, 0x00, 0x01, 0xa8, 0x00, 0x00, 0x00, 0x0e, 0x6c, 0x6f, 0x63, 0x61,
+    0x01, 0xb4, 0x00, 0x28, 0x00, 0x00, 0x02, 0x0c, 0x00, 0x00, 0x00, 0x0a,
+    0x6d, 0x61, 0x78, 0x70, 0x00, 0x4a, 0x01, 0x4d, 0x00, 0x00, 0x01, 0x28,
+    0x00, 0x00, 0x00, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0xc3, 0xe5, 0x39, 0xd4,
+    0x00, 0x00, 0x05, 0x58, 0x00, 0x00, 0x02, 0x28, 0x70, 0x6f, 0x73, 0x74,
+    0xff, 0x03, 0x00, 0x67, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, 0x20,
+    0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0b, 0x0f, 0x08, 0x1d,
+    0x5f, 0x0f, 0x3c, 0xf5, 0x00, 0x0b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0xd1, 0x97, 0xa8, 0x5a, 0x00, 0x00, 0x00, 0x00, 0xd6, 0xe8, 0x32, 0x33,
+    0x00, 0x03, 0xff, 0x3b, 0x08, 0x00, 0x05, 0x55, 0x00, 0x00, 0x00, 0x08,
+    0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+    0x05, 0x55, 0xff, 0x3b, 0x01, 0x79, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00,
+    0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00,
+    0x00, 0x04, 0x01, 0x1c, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+    0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x2e,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x08, 0x00, 0x01, 0x90, 0x00, 0x05,
+    0x00, 0x00, 0x05, 0x33, 0x05, 0x99, 0x00, 0x00, 0x01, 0x1e, 0x05, 0x33,
+    0x05, 0x99, 0x00, 0x00, 0x03, 0xd7, 0x00, 0x66, 0x02, 0x12, 0x00, 0x00,
+    0x05, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x73, 0x6b, 0x69, 0x61, 0x00, 0xc0, 0x00, 0x00, 0xf0, 0x21,
+    0x06, 0x66, 0xfe, 0x66, 0x01, 0x79, 0x05, 0x55, 0x00, 0xc5, 0x80, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x20, 0x00, 0x01, 0x08, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00,
+    0x08, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+    0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x04, 0x00, 0x48,
+    0x00, 0x00, 0x00, 0x0e, 0x00, 0x08, 0x00, 0x02, 0x00, 0x06, 0x00, 0x00,
+    0x00, 0x09, 0x00, 0x0d, 0x00, 0x1d, 0x00, 0x21, 0xf0, 0x21, 0xff, 0xff,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0d, 0x00, 0x1d, 0x00, 0x21,
+    0xf0, 0x21, 0xff, 0xff, 0x00, 0x01, 0xff, 0xf9, 0xff, 0xf5, 0xff, 0xe4,
+    0xff, 0xe2, 0x0f, 0xe2, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14,
+    0x00, 0x14, 0x00, 0x14, 0x01, 0xa0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x44,
+    0x00, 0x00, 0x02, 0x64, 0x05, 0x55, 0x00, 0x03, 0x00, 0x07, 0x00, 0x00,
+    0x33, 0x11, 0x21, 0x11, 0x25, 0x21, 0x11, 0x21, 0x44, 0x02, 0x20, 0xfe,
+    0x24, 0x01, 0x98, 0xfe, 0x68, 0x05, 0x55, 0xfa, 0xab, 0x44, 0x04, 0xcd,
+    0x00, 0x04, 0x00, 0x03, 0xff, 0x3b, 0x08, 0x00, 0x05, 0x4c, 0x00, 0x15,
+    0x00, 0x1d, 0x00, 0x25, 0x01, 0x1b, 0x00, 0x00, 0x01, 0x36, 0x37, 0x36,
+    0x27, 0x26, 0x07, 0x06, 0x06, 0x23, 0x22, 0x27, 0x26, 0x27, 0x26, 0x07,
+    0x06, 0x17, 0x16, 0x17, 0x16, 0x32, 0x37, 0x32, 0x35, 0x34, 0x23, 0x22,
+    0x15, 0x14, 0x27, 0x32, 0x35, 0x34, 0x23, 0x22, 0x15, 0x14, 0x03, 0x32,
+    0x17, 0x30, 0x17, 0x31, 0x36, 0x37, 0x36, 0x37, 0x36, 0x37, 0x36, 0x33,
+    0x32, 0x33, 0x16, 0x33, 0x32, 0x17, 0x16, 0x07, 0x06, 0x23, 0x22, 0x27,
+    0x26, 0x27, 0x26, 0x23, 0x22, 0x07, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06,
+    0x1f, 0x02, 0x37, 0x36, 0x37, 0x36, 0x33, 0x32, 0x17, 0x17, 0x16, 0x33,
+    0x16, 0x17, 0x16, 0x07, 0x06, 0x23, 0x22, 0x27, 0x27, 0x26, 0x23, 0x22,
+    0x07, 0x06, 0x07, 0x06, 0x17, 0x16, 0x17, 0x16, 0x33, 0x32, 0x33, 0x32,
+    0x37, 0x36, 0x37, 0x36, 0x17, 0x16, 0x1f, 0x02, 0x16, 0x17, 0x16, 0x15,
+    0x14, 0x23, 0x22, 0x27, 0x27, 0x26, 0x27, 0x27, 0x26, 0x27, 0x26, 0x07,
+    0x06, 0x07, 0x06, 0x17, 0x16, 0x17, 0x16, 0x15, 0x14, 0x07, 0x06, 0x07,
+    0x06, 0x23, 0x22, 0x27, 0x26, 0x07, 0x06, 0x07, 0x06, 0x15, 0x14, 0x17,
+    0x16, 0x17, 0x16, 0x15, 0x14, 0x07, 0x06, 0x23, 0x22, 0x27, 0x26, 0x27,
+    0x26, 0x35, 0x34, 0x37, 0x36, 0x37, 0x36, 0x37, 0x34, 0x27, 0x26, 0x07,
+    0x06, 0x07, 0x06, 0x0f, 0x02, 0x06, 0x23, 0x22, 0x27, 0x26, 0x35, 0x34,
+    0x37, 0x37, 0x36, 0x37, 0x36, 0x37, 0x36, 0x37, 0x36, 0x27, 0x26, 0x27,
+    0x26, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x07, 0x06, 0x23, 0x22,
+    0x27, 0x26, 0x35, 0x34, 0x37, 0x36, 0x37, 0x37, 0x36, 0x37, 0x37, 0x36,
+    0x37, 0x36, 0x37, 0x36, 0x35, 0x34, 0x27, 0x26, 0x27, 0x26, 0x27, 0x26,
+    0x23, 0x22, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x27, 0x26, 0x27, 0x26,
+    0x27, 0x26, 0x35, 0x34, 0x37, 0x36, 0x37, 0x36, 0x37, 0x36, 0x33, 0x32,
+    0x17, 0x16, 0x33, 0x32, 0x37, 0x36, 0x35, 0x34, 0x37, 0x36, 0x37, 0x36,
+    0x33, 0x04, 0xf5, 0x23, 0x13, 0x11, 0x14, 0x16, 0x1d, 0x1b, 0x4c, 0x1f,
+    0x0e, 0x2d, 0x23, 0x14, 0x2c, 0x13, 0x18, 0x25, 0x2c, 0x10, 0x3c, 0x71,
+    0x1d, 0x5c, 0x5c, 0x3f, 0xae, 0x5c, 0x5c, 0x3f, 0x6a, 0x27, 0x31, 0x5b,
+    0x09, 0x27, 0x36, 0x03, 0x0a, 0x26, 0x35, 0x2e, 0x09, 0x08, 0xc6, 0x13,
+    0x81, 0x17, 0x20, 0x18, 0x21, 0x1e, 0x04, 0x04, 0x15, 0x5c, 0x22, 0x26,
+    0x48, 0x56, 0x3b, 0x10, 0x21, 0x01, 0x0c, 0x06, 0x06, 0x0f, 0x31, 0x44,
+    0x3c, 0x52, 0x4a, 0x1d, 0x11, 0x3f, 0xb4, 0x71, 0x01, 0x26, 0x06, 0x0d,
+    0x15, 0x1a, 0x2a, 0x13, 0x53, 0xaa, 0x42, 0x1d, 0x0a, 0x33, 0x20, 0x21,
+    0x2b, 0x01, 0x02, 0x3e, 0x21, 0x09, 0x02, 0x02, 0x0f, 0x2d, 0x4b, 0x0a,
+    0x22, 0x15, 0x20, 0x1f, 0x72, 0x8b, 0x2d, 0x2f, 0x1d, 0x1f, 0x0e, 0x25,
+    0x3f, 0x4d, 0x1b, 0x63, 0x2a, 0x2c, 0x14, 0x22, 0x18, 0x1c, 0x0f, 0x08,
+    0x2a, 0x08, 0x08, 0x0d, 0x3b, 0x4c, 0x52, 0x74, 0x27, 0x71, 0x2e, 0x01,
+    0x0c, 0x10, 0x15, 0x0d, 0x06, 0x0d, 0x05, 0x01, 0x06, 0x2c, 0x28, 0x14,
+    0x1b, 0x05, 0x04, 0x10, 0x06, 0x12, 0x08, 0x0a, 0x16, 0x27, 0x03, 0x0d,
+    0x30, 0x4c, 0x4c, 0x4b, 0x1f, 0x0b, 0x22, 0x26, 0x0d, 0x15, 0x0d, 0x2d,
+    0x68, 0x34, 0x14, 0x3c, 0x25, 0x12, 0x04, 0x10, 0x18, 0x0b, 0x09, 0x30,
+    0x2b, 0x44, 0x66, 0x14, 0x47, 0x47, 0x59, 0x73, 0x25, 0x05, 0x03, 0x1f,
+    0x01, 0x08, 0x3f, 0x48, 0x4b, 0x4b, 0x76, 0x2f, 0x49, 0x2d, 0x22, 0x24,
+    0x0c, 0x15, 0x08, 0x0e, 0x33, 0x03, 0x44, 0x4c, 0x10, 0x46, 0x13, 0x1f,
+    0x27, 0x1b, 0x1d, 0x13, 0x02, 0x24, 0x08, 0x02, 0x42, 0x0e, 0x4d, 0x3c,
+    0x19, 0x1b, 0x40, 0x2b, 0x2b, 0x1e, 0x16, 0x11, 0x04, 0x1f, 0x11, 0x04,
+    0x18, 0x11, 0x35, 0x01, 0xa3, 0x13, 0x24, 0x1f, 0x0b, 0x0c, 0x19, 0x19,
+    0x18, 0x13, 0x0f, 0x0c, 0x1a, 0x18, 0x1f, 0x19, 0x1e, 0x07, 0x1a, 0xc3,
+    0x54, 0x51, 0x54, 0x51, 0x04, 0x53, 0x51, 0x54, 0x50, 0x02, 0x48, 0x1a,
+    0x31, 0x18, 0x55, 0x74, 0x04, 0x0e, 0x09, 0x0d, 0x06, 0x10, 0x16, 0x1b,
+    0x24, 0x01, 0x04, 0x0b, 0x04, 0x10, 0x3f, 0x0a, 0x41, 0x02, 0x41, 0x20,
+    0x06, 0x12, 0x16, 0x21, 0x17, 0x2a, 0x1e, 0x15, 0x40, 0x27, 0x11, 0x0e,
+    0x1e, 0x11, 0x15, 0x1f, 0x43, 0x13, 0x1a, 0x10, 0x15, 0x1b, 0x04, 0x09,
+    0x4d, 0x2a, 0x0f, 0x19, 0x0a, 0x0a, 0x03, 0x05, 0x15, 0x3c, 0x64, 0x21,
+    0x4b, 0x2e, 0x21, 0x28, 0x13, 0x47, 0x44, 0x19, 0x3f, 0x11, 0x18, 0x0b,
+    0x0a, 0x07, 0x18, 0x0d, 0x07, 0x24, 0x2c, 0x2b, 0x21, 0x32, 0x10, 0x48,
+    0x2a, 0x2d, 0x1e, 0x1a, 0x01, 0x0c, 0x43, 0x59, 0x28, 0x4e, 0x1c, 0x0d,
+    0x5d, 0x24, 0x14, 0x0a, 0x05, 0x1f, 0x24, 0x32, 0x46, 0x3e, 0x5f, 0x3e,
+    0x44, 0x1a, 0x30, 0x15, 0x0d, 0x07, 0x18, 0x2b, 0x03, 0x0d, 0x1a, 0x28,
+    0x28, 0x57, 0xb2, 0x29, 0x27, 0x40, 0x2c, 0x23, 0x16, 0x63, 0x58, 0x1a,
+    0x0a, 0x18, 0x11, 0x23, 0x08, 0x1b, 0x29, 0x05, 0x04, 0x0b, 0x15, 0x0d,
+    0x14, 0x0b, 0x2a, 0x29, 0x5a, 0x62, 0x01, 0x19, 0x1e, 0x05, 0x05, 0x26,
+    0x42, 0x42, 0x2a, 0x2a, 0x3f, 0x0d, 0x0f, 0x09, 0x05, 0x07, 0x01, 0x0b,
+    0x25, 0x3e, 0x0d, 0x17, 0x11, 0x01, 0x03, 0x0d, 0x13, 0x20, 0x19, 0x11,
+    0x03, 0x02, 0x01, 0x04, 0x11, 0x04, 0x05, 0x1b, 0x3d, 0x10, 0x29, 0x20,
+    0x04, 0x04, 0x0a, 0x07, 0x04, 0x1f, 0x15, 0x20, 0x3e, 0x0f, 0x2a, 0x1e,
+    0x00, 0x00, 0x00, 0x1b, 0x01, 0x4a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x01, 0x00, 0x0c, 0x00, 0x1b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x02, 0x00, 0x07, 0x00, 0x27, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x03, 0x00, 0x0c, 0x00, 0x1b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x04, 0x00, 0x0c, 0x00, 0x1b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x05, 0x00, 0x02, 0x00, 0x2e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x06, 0x00, 0x0c, 0x00, 0x1b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x0d, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x0e, 0x00, 0x1a, 0x00, 0x30, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
+    0x00, 0x00, 0x00, 0x36, 0x00, 0x4a, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
+    0x00, 0x01, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
+    0x00, 0x02, 0x00, 0x0e, 0x00, 0x98, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
+    0x00, 0x03, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
+    0x00, 0x04, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
+    0x00, 0x05, 0x00, 0x04, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
+    0x00, 0x06, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
+    0x00, 0x0d, 0x00, 0x36, 0x00, 0x4a, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
+    0x00, 0x0e, 0x00, 0x34, 0x00, 0xaa, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
+    0x00, 0x00, 0x00, 0x36, 0x00, 0x4a, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
+    0x00, 0x01, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
+    0x00, 0x02, 0x00, 0x0e, 0x00, 0x98, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
+    0x00, 0x03, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
+    0x00, 0x04, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
+    0x00, 0x05, 0x00, 0x04, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
+    0x00, 0x06, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
+    0x00, 0x0d, 0x00, 0x36, 0x00, 0x4a, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
+    0x00, 0x0e, 0x00, 0x34, 0x00, 0xaa, 0x43, 0x6f, 0x70, 0x79, 0x72, 0x69,
+    0x67, 0x68, 0x74, 0x20, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x31, 0x35,
+    0x2c, 0x20, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x53, 0x70, 0x69,
+    0x64, 0x65, 0x72, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x52, 0x65, 0x67,
+    0x75, 0x6c, 0x61, 0x72, 0x56, 0x31, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+    0x2f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, 0x2e, 0x73, 0x69, 0x6c,
+    0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x4f, 0x46, 0x4c, 0x00, 0x43, 0x00, 0x6f,
+    0x00, 0x70, 0x00, 0x79, 0x00, 0x72, 0x00, 0x69, 0x00, 0x67, 0x00, 0x68,
+    0x00, 0x74, 0x00, 0x20, 0x00, 0x28, 0x00, 0x63, 0x00, 0x29, 0x00, 0x20,
+    0x00, 0x32, 0x00, 0x30, 0x00, 0x31, 0x00, 0x35, 0x00, 0x2c, 0x00, 0x20,
+    0x00, 0x47, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x67, 0x00, 0x6c, 0x00, 0x65,
+    0x00, 0x2e, 0x00, 0x53, 0x00, 0x70, 0x00, 0x69, 0x00, 0x64, 0x00, 0x65,
+    0x00, 0x72, 0x00, 0x53, 0x00, 0x79, 0x00, 0x6d, 0x00, 0x62, 0x00, 0x6f,
+    0x00, 0x6c, 0x00, 0x52, 0x00, 0x65, 0x00, 0x67, 0x00, 0x75, 0x00, 0x6c,
+    0x00, 0x61, 0x00, 0x72, 0x00, 0x56, 0x00, 0x31, 0x00, 0x68, 0x00, 0x74,
+    0x00, 0x74, 0x00, 0x70, 0x00, 0x3a, 0x00, 0x2f, 0x00, 0x2f, 0x00, 0x73,
+    0x00, 0x63, 0x00, 0x72, 0x00, 0x69, 0x00, 0x70, 0x00, 0x74, 0x00, 0x73,
+    0x00, 0x2e, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x2e, 0x00, 0x6f,
+    0x00, 0x72, 0x00, 0x67, 0x00, 0x2f, 0x00, 0x4f, 0x00, 0x46, 0x00, 0x4c,
+    0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x66,
+    0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+    0xff, 0xff, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x0c, 0x00, 0x14, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+    0x00, 0x02, 0x00, 0x00
+};
+
+/**
+ * There does not appear to be a publicly accessible API for determining if lcd
+ * font smoothing will be applied if we request it. The main issue is that if
+ * smoothing is applied a gamma of 2.0 will be used, if not a gamma of 1.0.
+ */
+SkCTFontSmoothBehavior SkCTFontGetSmoothBehavior() {
+    static SkCTFontSmoothBehavior gSmoothBehavior = []{
+        uint32_t noSmoothBitmap[16][16] = {};
+        uint32_t smoothBitmap[16][16] = {};
+
+        SkUniqueCFRef<CGColorSpaceRef> colorspace(CGColorSpaceCreateDeviceRGB());
+        SkUniqueCFRef<CGContextRef> noSmoothContext(
+                CGBitmapContextCreate(&noSmoothBitmap, 16, 16, 8, 16*4,
+                                      colorspace.get(), BITMAP_INFO_RGB));
+        SkUniqueCFRef<CGContextRef> smoothContext(
+                CGBitmapContextCreate(&smoothBitmap, 16, 16, 8, 16*4,
+                                      colorspace.get(), BITMAP_INFO_RGB));
+
+        SkUniqueCFRef<CFDataRef> data(CFDataCreateWithBytesNoCopy(
+                kCFAllocatorDefault, kSpiderSymbol_ttf, SK_ARRAY_COUNT(kSpiderSymbol_ttf),
+                kCFAllocatorNull));
+        SkUniqueCFRef<CTFontDescriptorRef> desc(
+                CTFontManagerCreateFontDescriptorFromData(data.get()));
+        SkUniqueCFRef<CTFontRef> ctFont(CTFontCreateWithFontDescriptor(desc.get(), 16, nullptr));
+        SkASSERT(ctFont);
+
+        CGContextSetShouldSmoothFonts(noSmoothContext.get(), false);
+        CGContextSetShouldAntialias(noSmoothContext.get(), true);
+        CGContextSetTextDrawingMode(noSmoothContext.get(), kCGTextFill);
+        CGContextSetGrayFillColor(noSmoothContext.get(), 1, 1);
+
+        CGContextSetShouldSmoothFonts(smoothContext.get(), true);
+        CGContextSetShouldAntialias(smoothContext.get(), true);
+        CGContextSetTextDrawingMode(smoothContext.get(), kCGTextFill);
+        CGContextSetGrayFillColor(smoothContext.get(), 1, 1);
+
+        CGPoint point = CGPointMake(0, 3);
+        CGGlyph spiderGlyph = 3;
+        CTFontDrawGlyphs(ctFont.get(), &spiderGlyph, &point, 1, noSmoothContext.get());
+        CTFontDrawGlyphs(ctFont.get(), &spiderGlyph, &point, 1, smoothContext.get());
+
+        // For debugging.
+        //SkUniqueCFRef<CGImageRef> image(CGBitmapContextCreateImage(noSmoothContext()));
+        //SkUniqueCFRef<CGImageRef> image(CGBitmapContextCreateImage(smoothContext()));
+
+        SkCTFontSmoothBehavior smoothBehavior = SkCTFontSmoothBehavior::none;
+        for (int x = 0; x < 16; ++x) {
+            for (int y = 0; y < 16; ++y) {
+                uint32_t smoothPixel = smoothBitmap[x][y];
+                uint32_t r = (smoothPixel >> 16) & 0xFF;
+                uint32_t g = (smoothPixel >>  8) & 0xFF;
+                uint32_t b = (smoothPixel >>  0) & 0xFF;
+                if (r != g || r != b) {
+                    return SkCTFontSmoothBehavior::subpixel;
+                }
+                if (noSmoothBitmap[x][y] != smoothPixel) {
+                    smoothBehavior = SkCTFontSmoothBehavior::some;
+                }
+            }
+        }
+        return smoothBehavior;
+    }();
+    return gSmoothBehavior;
+}
+
+#endif
diff --git a/src/utils/mac/SkCTFontSmoothBehavior.h b/src/utils/mac/SkCTFontSmoothBehavior.h
new file mode 100644
index 0000000..126cdb3
--- /dev/null
+++ b/src/utils/mac/SkCTFontSmoothBehavior.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2020 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkCTFontSmoothBehavior_DEFINED
+#define SkCTFontSmoothBehavior_DEFINED
+
+#include "include/core/SkTypes.h"
+#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
+
+enum class SkCTFontSmoothBehavior {
+    none, // SmoothFonts produces no effect.
+    some, // SmoothFonts produces some effect, but not subpixel coverage.
+    subpixel, // SmoothFonts produces some effect and provides subpixel coverage.
+};
+
+SkCTFontSmoothBehavior SkCTFontGetSmoothBehavior();
+
+#endif
+#endif // SkCTFontSmoothBehavior_DEFINED