Bug 1156742 Part 3: Add support for FontType::CAIRO to CreateScaledFontForTrueTypeData on Windows. r=bas
☠☠ backed out by 9bdfae920430 ☠ ☠
authorBob Owen <bobowencode@gmail.com>
Mon, 21 Dec 2015 20:33:13 +0000
changeset 277215 99a8859afcdbe7966b10954fbfc5665dbc78af30
parent 277214 4934e88b2d7a7274d9513c81c9ef3e03a29333f4
child 277216 a40eea9145190230dbaad585924215d4b173fa60
push id29819
push usercbook@mozilla.com
push dateTue, 22 Dec 2015 10:47:17 +0000
treeherdermozilla-central@ad16863d1d45 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbas
bugs1156742
milestone46.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1156742 Part 3: Add support for FontType::CAIRO to CreateScaledFontForTrueTypeData on Windows. r=bas Parts of this change and related code get moved around in Part 24.
gfx/2d/BigEndianInts.h
gfx/2d/Factory.cpp
gfx/2d/SFNTData.cpp
gfx/2d/SFNTData.h
gfx/2d/SFNTNameTable.cpp
gfx/2d/SFNTNameTable.h
gfx/2d/ScaledFontBase.cpp
gfx/2d/ScaledFontBase.h
gfx/2d/ScaledFontDWrite.cpp
gfx/2d/ScaledFontDWrite.h
gfx/2d/ScaledFontWin.cpp
gfx/2d/ScaledFontWin.h
gfx/2d/moz.build
gfx/2d/u16string.h
new file mode 100644
--- /dev/null
+++ b/gfx/2d/BigEndianInts.h
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_BigEndianInts_h
+#define mozilla_BigEndianInts_h
+
+#include "mozilla/Endian.h"
+
+namespace mozilla {
+
+#pragma pack(push, 1)
+
+struct BigEndianUint16
+{
+#ifdef __SUNPRO_CC
+  BigEndianUint16& operator=(const uint16_t aValue)
+  {
+    value = NativeEndian::swapToBigEndian(aValue);
+    return *this;
+  }
+#else
+  MOZ_IMPLICIT BigEndianUint16(const uint16_t aValue)
+  {
+    value = NativeEndian::swapToBigEndian(aValue);
+  }
+#endif
+
+  operator uint16_t() const
+  {
+    return NativeEndian::swapFromBigEndian(value);
+  }
+
+  friend inline bool
+  operator==(const BigEndianUint16& lhs, const BigEndianUint16& rhs)
+  {
+    return lhs.value == rhs.value;
+  }
+
+  friend inline bool
+  operator!=(const BigEndianUint16& lhs, const BigEndianUint16& rhs)
+  {
+    return !(lhs == rhs);
+  }
+
+private:
+  uint16_t value;
+};
+
+struct BigEndianUint32
+{
+#ifdef __SUNPRO_CC
+  BigEndianUint32& operator=(const uint32_t aValue)
+  {
+    value = NativeEndian::swapToBigEndian(aValue);
+    return *this;
+  }
+#else
+  MOZ_IMPLICIT BigEndianUint32(const uint32_t aValue)
+  {
+    value = NativeEndian::swapToBigEndian(aValue);
+  }
+#endif
+
+  operator uint32_t() const
+  {
+    return NativeEndian::swapFromBigEndian(value);
+  }
+
+private:
+  uint32_t  value;
+};
+
+#pragma pack(pop)
+
+} // mozilla
+
+#endif // mozilla_BigEndianInts_h
\ No newline at end of file
--- a/gfx/2d/Factory.cpp
+++ b/gfx/2d/Factory.cpp
@@ -568,16 +568,37 @@ Factory::CreateScaledFontForTrueTypeData
 {
   switch (aType) {
 #ifdef WIN32
   case FontType::DWRITE:
     {
       return MakeAndAddRef<ScaledFontDWrite>(aData, aSize, aFaceIndex, aGlyphSize);
     }
 #endif
+  case FontType::CAIRO:
+    {
+      RefPtr<ScaledFontBase> scaledFont;
+
+#ifdef WIN32
+      if (DrawTargetD2D::GetDWriteFactory()) {
+        scaledFont = new ScaledFontDWrite(aData, aSize, aFaceIndex, aGlyphSize);
+      } else {
+        scaledFont = new ScaledFontWin(aData, aSize, aFaceIndex, aGlyphSize);
+      }
+
+      if (!scaledFont->PopulateCairoScaledFont()) {
+        gfxWarning() << "Unable to create cairo scaled font from truetype data";
+        return nullptr;
+      }
+#else
+      gfxWarning() << "Unable to create cairo scaled font from truetype data";
+#endif
+
+      return scaledFont.forget();
+    }
   default:
     gfxWarning() << "Unable to create requested font type from truetype data";
     return nullptr;
   }
 }
 
 already_AddRefed<ScaledFont>
 Factory::CreateScaledFontWithCairo(const NativeFont& aNativeFont, Float aSize, cairo_scaled_font_t* aScaledFont)
new file mode 100644
--- /dev/null
+++ b/gfx/2d/SFNTData.cpp
@@ -0,0 +1,207 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SFNTData.h"
+
+#include <algorithm>
+
+#include "BigEndianInts.h"
+#include "Logging.h"
+#include "SFNTNameTable.h"
+
+namespace mozilla {
+namespace gfx {
+
+#define TRUETYPE_TAG(a, b, c, d) ((a) << 24 | (b) << 16 | (c) << 8 | (d))
+
+#pragma pack(push, 1)
+
+struct TTCHeader
+{
+  BigEndianUint32 ttcTag;    // Always 'ttcf'
+  BigEndianUint32 version;   // Fixed, 0x00010000
+  BigEndianUint32 numFonts;
+};
+
+struct OffsetTable
+{
+  BigEndianUint32 sfntVersion;   // Fixed, 0x00010000 for version 1.0.
+  BigEndianUint16 numTables;
+  BigEndianUint16 searchRange;   // (Maximum power of 2 <= numTables) x 16.
+  BigEndianUint16 entrySelector; // Log2(maximum power of 2 <= numTables).
+  BigEndianUint16 rangeShift;    // NumTables x 16-searchRange.
+};
+
+struct TableDirEntry
+{
+  BigEndianUint32 tag;      // 4 -byte identifier.
+  BigEndianUint32 checkSum; // CheckSum for this table.
+  BigEndianUint32 offset;   // Offset from beginning of TrueType font file.
+  BigEndianUint32 length;   // Length of this table.
+
+  friend bool operator<(const TableDirEntry& lhs, const uint32_t aTag)
+  {
+    return lhs.tag < aTag;
+  }
+};
+
+#pragma pack(pop)
+
+class SFNTData::Font
+{
+public:
+  Font(const OffsetTable *aOffsetTable, const uint8_t *aFontData,
+       uint32_t aDataLength)
+    : mFontData(aFontData)
+    , mFirstDirEntry(reinterpret_cast<const TableDirEntry*>(aOffsetTable + 1))
+    , mEndOfDirEntries(mFirstDirEntry + aOffsetTable->numTables)
+    , mDataLength(aDataLength)
+  {
+  }
+
+  bool GetU16FullName(mozilla::u16string& aU16FullName)
+  {
+    const TableDirEntry* dirEntry =
+      GetDirEntry(TRUETYPE_TAG('n', 'a', 'm', 'e'));
+    if (!dirEntry) {
+      gfxWarning() << "Name table entry not found.";
+      return false;
+    }
+
+    UniquePtr<SFNTNameTable> nameTable =
+      SFNTNameTable::Create((mFontData + dirEntry->offset), dirEntry->length);
+    if (!nameTable) {
+      return false;
+    }
+
+    return nameTable->GetU16FullName(aU16FullName);
+  }
+
+private:
+
+  const TableDirEntry*
+  GetDirEntry(const uint32_t aTag)
+  {
+    const TableDirEntry* foundDirEntry =
+      std::lower_bound(mFirstDirEntry, mEndOfDirEntries, aTag);
+
+    if (foundDirEntry == mEndOfDirEntries || foundDirEntry->tag != aTag) {
+      gfxWarning() << "Font data does not contain tag.";
+      return nullptr;
+    }
+
+    if (mDataLength < (foundDirEntry->offset + foundDirEntry->length)) {
+      gfxWarning() << "Font data too short to contain table.";
+      return nullptr;
+    }
+
+    return foundDirEntry;
+  }
+
+  const uint8_t *mFontData;
+  const TableDirEntry *mFirstDirEntry;
+  const TableDirEntry *mEndOfDirEntries;
+  uint32_t mDataLength;
+};
+
+/* static */
+UniquePtr<SFNTData>
+SFNTData::Create(const uint8_t *aFontData, uint32_t aDataLength)
+{
+  MOZ_ASSERT(aFontData);
+
+  // Check to see if this is a font collection.
+  if (aDataLength < sizeof(TTCHeader)) {
+    gfxWarning() << "Font data too short.";
+    return false;
+  }
+
+  const TTCHeader *ttcHeader = reinterpret_cast<const TTCHeader*>(aFontData);
+  if (ttcHeader->ttcTag == TRUETYPE_TAG('t', 't', 'c', 'f')) {
+    uint32_t numFonts = ttcHeader->numFonts;
+    if (aDataLength < sizeof(TTCHeader) + (numFonts * sizeof(BigEndianUint32))) {
+      gfxWarning() << "Font data too short to contain full TTC Header.";
+      return false;
+    }
+
+    UniquePtr<SFNTData> sfntData(new SFNTData);
+    const BigEndianUint32* offset =
+      reinterpret_cast<const BigEndianUint32*>(aFontData + sizeof(TTCHeader));
+    const BigEndianUint32* endOfOffsets = offset + numFonts;
+    while (offset != endOfOffsets) {
+      if (!sfntData->AddFont(aFontData, aDataLength, *offset)) {
+        return nullptr;
+      }
+      ++offset;
+    }
+
+    return Move(sfntData);
+  }
+
+  UniquePtr<SFNTData> sfntData(new SFNTData);
+  if (!sfntData->AddFont(aFontData, aDataLength, 0)) {
+    return nullptr;
+  }
+
+  return Move(sfntData);
+}
+
+SFNTData::~SFNTData()
+{
+  for (size_t i = 0; i < mFonts.length(); ++i) {
+    delete mFonts[i];
+  }
+}
+
+bool
+SFNTData::GetU16FullName(uint32_t aIndex, mozilla::u16string& aU16FullName)
+{
+  if (aIndex >= mFonts.length()) {
+    gfxWarning() << "aIndex to font data too high.";
+    return false;
+  }
+
+  return mFonts[aIndex]->GetU16FullName(aU16FullName);
+}
+
+bool
+SFNTData::GetIndexForU16Name(const mozilla::u16string& aU16FullName,
+                             uint32_t* aIndex)
+{
+  for (size_t i = 0; i < mFonts.length(); ++i) {
+    mozilla::u16string name;
+    if (mFonts[i]->GetU16FullName(name) && name == aU16FullName) {
+      *aIndex = i;
+      return true;
+    }
+  }
+
+  return false;
+}
+
+bool
+SFNTData::AddFont(const uint8_t *aFontData, uint32_t aDataLength,
+                  uint32_t aOffset)
+{
+  uint32_t remainingLength = aDataLength - aOffset;
+  if (remainingLength < sizeof(OffsetTable)) {
+    gfxWarning() << "Font data too short to contain OffsetTable " << aOffset;
+    return false;
+  }
+
+  const OffsetTable *offsetTable =
+    reinterpret_cast<const OffsetTable*>(aFontData + aOffset);
+  if (remainingLength <
+      sizeof(OffsetTable) + (offsetTable->numTables * sizeof(TableDirEntry))) {
+    gfxWarning() << "Font data too short to contain tables.";
+    return false;
+  }
+
+  return mFonts.append(new Font(offsetTable, aFontData, aDataLength));
+}
+
+} // gfx
+} // mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/2d/SFNTData.h
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_gfx_SFNTData_h
+#define mozilla_gfx_SFNTData_h
+
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Vector.h"
+#include "u16string.h"
+
+namespace mozilla {
+namespace gfx {
+
+class SFNTData final
+{
+public:
+
+  /**
+   * Creates an SFNTData if the header is a format that we understand and
+   * aDataLength is sufficient for the length information in the header data.
+   * Note that the data is NOT copied, so must exist the SFNTData's lifetime.
+   *
+   * @param aFontData the SFNT data.
+   * @param aDataLength length
+   * @return UniquePtr to a SFNTData or nullptr if the header is invalid.
+   */
+  static UniquePtr<SFNTData> Create(const uint8_t *aFontData,
+                                    uint32_t aDataLength);
+
+  ~SFNTData();
+
+  /**
+   * Gets the full name from the name table of the font corresponding to the
+   * index. If the full name string is not present it will use the family space
+   * concatenated with the style.
+   * This will only read names that are already UTF16.
+   *
+   * @param aFontData SFNT data.
+   * @param aDataLength length of aFontData.
+   * @param aU16FullName string to be populated with the full name.
+   * @return true if the full name is successfully read.
+   */
+  bool GetU16FullName(uint32_t aIndex, mozilla::u16string& aU16FullName);
+
+  /**
+   * Returns the index for the first UTF16 name matching aU16FullName.
+   *
+   * @param aU16FullName full name to find.
+   * @param aIndex out param for the index if found.
+   * @return true if the full name is successfully read.
+   */
+  bool GetIndexForU16Name(const mozilla::u16string& aU16FullName, uint32_t* aIndex);
+
+private:
+
+  SFNTData() {}
+
+  bool AddFont(const uint8_t *aFontData, uint32_t aDataLength,
+               uint32_t aOffset);
+
+  // Internal representation of single font in font file.
+  class Font;
+
+  Vector<Font*> mFonts;
+};
+
+} // gfx
+} // mozilla
+
+#endif // mozilla_gfx_SFNTData_h
new file mode 100644
--- /dev/null
+++ b/gfx/2d/SFNTNameTable.cpp
@@ -0,0 +1,257 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SFNTNameTable.h"
+
+#include "BigEndianInts.h"
+#include "Logging.h"
+#include "mozilla/Move.h"
+
+namespace mozilla {
+namespace gfx {
+
+static const BigEndianUint16 FORMAT_0 = 0;
+
+static const BigEndianUint16 NAME_ID_FAMILY = 1;
+static const BigEndianUint16 NAME_ID_STYLE = 2;
+static const BigEndianUint16 NAME_ID_FULL = 4;
+
+static const BigEndianUint16 PLATFORM_ID_UNICODE = 0;
+static const BigEndianUint16 PLATFORM_ID_MAC = 1;
+static const BigEndianUint16 PLATFORM_ID_MICROSOFT = 3;
+
+static const BigEndianUint16 ENCODING_ID_MICROSOFT_SYMBOL = 0;
+static const BigEndianUint16 ENCODING_ID_MICROSOFT_UNICODEBMP = 1;
+static const BigEndianUint16 ENCODING_ID_MICROSOFT_UNICODEFULL = 10;
+
+static const BigEndianUint16 LANG_ID_MAC_ENGLISH = 0;
+
+static const BigEndianUint16 LANG_ID_MICROSOFT_EN_US = 0x0409;
+
+#pragma pack(push, 1)
+
+// Name table has a header, followed by name records, followed by string data.
+struct NameHeader
+{
+  BigEndianUint16 format;       // Format selector (=0).
+  BigEndianUint16 count;        // Number of name records.
+  BigEndianUint16 stringOffset; // Offset to string storage from start of table.
+};
+
+struct NameRecord
+{
+  BigEndianUint16 platformID;
+  BigEndianUint16 encodingID; // Platform-specific encoding ID
+  BigEndianUint16 languageID;
+  BigEndianUint16 nameID;
+  BigEndianUint16 length;     // String length in bytes.
+  BigEndianUint16 offset;     // String offset from start of storage in bytes.
+};
+
+#pragma pack(pop)
+
+/* static */
+UniquePtr<SFNTNameTable>
+SFNTNameTable::Create(const uint8_t *aNameData, uint32_t aDataLength)
+{
+  MOZ_ASSERT(aNameData);
+
+  if (aDataLength < sizeof(NameHeader)) {
+    gfxWarning() << "Name data too short to contain NameHeader.";
+    return nullptr;
+  }
+
+  const NameHeader *nameHeader = reinterpret_cast<const NameHeader*>(aNameData);
+  if (nameHeader->format != FORMAT_0) {
+    gfxWarning() << "Only Name Table Format 0 is supported.";
+    return nullptr;
+  }
+
+  uint16_t stringOffset = nameHeader->stringOffset;
+
+  if (stringOffset !=
+      sizeof(NameHeader) + (nameHeader->count * sizeof(NameRecord))) {
+    gfxWarning() << "Name table string offset is incorrect.";
+    return nullptr;
+  }
+
+  if (aDataLength < stringOffset) {
+    gfxWarning() << "Name data too short to contain name records.";
+    return nullptr;
+  }
+
+  return UniquePtr<SFNTNameTable>(
+    new SFNTNameTable(nameHeader, aNameData, aDataLength));
+}
+
+SFNTNameTable::SFNTNameTable(const NameHeader *aNameHeader,
+                             const uint8_t *aNameData, uint32_t aDataLength)
+  : mFirstRecord(reinterpret_cast<const NameRecord*>(aNameData
+                                                     + sizeof(NameHeader)))
+  , mEndOfRecords(mFirstRecord + aNameHeader->count)
+  , mStringData(aNameData + aNameHeader->stringOffset)
+  , mStringDataLength(aDataLength - aNameHeader->stringOffset)
+{
+  MOZ_ASSERT(reinterpret_cast<const uint8_t*>(aNameHeader) == aNameData);
+}
+
+#if defined(XP_MACOSX)
+static const BigEndianUint16 CANONICAL_LANG_ID = LANG_ID_MAC_ENGLISH;
+static const BigEndianUint16 PLATFORM_ID = PLATFORM_ID_MAC;
+#else
+static const BigEndianUint16 CANONICAL_LANG_ID = LANG_ID_MICROSOFT_EN_US;
+static const BigEndianUint16 PLATFORM_ID = PLATFORM_ID_MICROSOFT;
+#endif
+
+static bool
+IsUTF16Encoding(const NameRecord *aNameRecord)
+{
+  if (aNameRecord->platformID == PLATFORM_ID_MICROSOFT &&
+      (aNameRecord->encodingID == ENCODING_ID_MICROSOFT_UNICODEBMP ||
+       aNameRecord->encodingID == ENCODING_ID_MICROSOFT_SYMBOL)) {
+    return true;
+  }
+
+  if (aNameRecord->platformID == PLATFORM_ID_UNICODE) {
+    return true;
+  }
+
+  return false;
+}
+
+static NameRecordMatchers*
+CreateCanonicalU16Matchers(const BigEndianUint16& aNameID)
+{
+  NameRecordMatchers *matchers = new NameRecordMatchers();
+
+  // First, look for the English name (this will normally succeed).
+  matchers->append(
+    [=](const NameRecord *aNameRecord) {
+        return aNameRecord->nameID == aNameID &&
+               aNameRecord->languageID == CANONICAL_LANG_ID &&
+               aNameRecord->platformID == PLATFORM_ID &&
+               IsUTF16Encoding(aNameRecord);
+    });
+
+  // Second, look for all languages.
+  matchers->append(
+    [=](const NameRecord *aNameRecord) {
+        return aNameRecord->nameID == aNameID &&
+               aNameRecord->platformID == PLATFORM_ID &&
+               IsUTF16Encoding(aNameRecord);
+    });
+
+#if defined(XP_MACOSX)
+  // On Mac may be dealing with font that only has Microsoft name entries.
+  matchers->append(
+    [=](const NameRecord *aNameRecord) {
+        return aNameRecord->nameID == aNameID &&
+               aNameRecord->languageID == LANG_ID_MICROSOFT_EN_US &&
+               aNameRecord->platformID == PLATFORM_ID_MICROSOFT &&
+               IsUTF16Encoding(aNameRecord);
+    });
+  matchers->append(
+    [=](const NameRecord *aNameRecord) {
+        return aNameRecord->nameID == aNameID &&
+               aNameRecord->platformID == PLATFORM_ID_MICROSOFT &&
+               IsUTF16Encoding(aNameRecord);
+    });
+#endif
+
+  return matchers;
+}
+
+static const NameRecordMatchers&
+FullNameMatchers()
+{
+  static const NameRecordMatchers *sFullNameMatchers =
+    CreateCanonicalU16Matchers(NAME_ID_FULL);
+  return *sFullNameMatchers;
+}
+
+static const NameRecordMatchers&
+FamilyMatchers()
+{
+  static const NameRecordMatchers *sFamilyMatchers =
+    CreateCanonicalU16Matchers(NAME_ID_FAMILY);
+  return *sFamilyMatchers;
+}
+
+static const NameRecordMatchers&
+StyleMatchers()
+{
+  static const NameRecordMatchers *sStyleMatchers =
+    CreateCanonicalU16Matchers(NAME_ID_STYLE);
+  return *sStyleMatchers;
+}
+
+bool
+SFNTNameTable::GetU16FullName(mozilla::u16string& aU16FullName)
+{
+  if (ReadU16Name(FullNameMatchers(), aU16FullName)) {
+    return true;
+  }
+
+  // If the full name record doesn't exist create the name from the family space
+  // concatenated with the style.
+  mozilla::u16string familyName;
+  if (!ReadU16Name(FamilyMatchers(), familyName)) {
+    return false;
+  }
+
+  mozilla::u16string styleName;
+  if (!ReadU16Name(StyleMatchers(), styleName)) {
+    return false;
+  }
+
+  aU16FullName.assign(Move(familyName));
+  aU16FullName.append(MOZ_UTF16(" "));
+  aU16FullName.append(styleName);
+  return true;
+}
+
+bool
+SFNTNameTable::ReadU16Name(const NameRecordMatchers& aMatchers,
+                           mozilla::u16string& aU16Name)
+{
+  MOZ_ASSERT(!aMatchers.empty());
+
+  for (size_t i = 0; i < aMatchers.length(); ++i) {
+    const NameRecord* record = mFirstRecord;
+    while (record != mEndOfRecords) {
+      if (aMatchers[i](record)) {
+        return ReadU16NameFromRecord(record, aU16Name);
+      }
+      ++record;
+    }
+  }
+
+  return false;
+}
+
+bool
+SFNTNameTable::ReadU16NameFromRecord(const NameRecord *aNameRecord,
+                                     mozilla::u16string& aU16Name)
+{
+  uint32_t offset = aNameRecord->offset;
+  uint32_t length = aNameRecord->length;
+  if (mStringDataLength < offset + length) {
+    gfxWarning() << "Name data too short to contain name string.";
+    return false;
+  }
+
+  const uint8_t *startOfName = mStringData + offset;
+  size_t actualLength = length / sizeof(char16_t);
+  UniquePtr<char16_t[]> nameData(new char16_t[actualLength]);
+  NativeEndian::copyAndSwapFromBigEndian(nameData.get(), startOfName,
+                                         actualLength);
+
+  aU16Name.assign(nameData.get(), actualLength);
+  return true;
+}
+
+} // gfx
+} // mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/2d/SFNTNameTable.h
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_gfx_SFNTNameTable_h
+#define mozilla_gfx_SFNTNameTable_h
+
+#include "mozilla/Function.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Vector.h"
+#include "u16string.h"
+
+namespace mozilla {
+namespace gfx {
+
+struct NameHeader;
+struct NameRecord;
+
+typedef Vector<Function<bool(const NameRecord*)>> NameRecordMatchers;
+
+class SFNTNameTable final
+{
+public:
+
+  /**
+   * Creates a SFNTNameTable if the header data is valid. Note that the data is
+   * NOT copied, so must exist for the lifetime of the table.
+   *
+   * @param aNameData the Name Table data.
+   * @param aDataLength length
+   * @return UniquePtr to a SFNTNameTable or nullptr if the header is invalid.
+   */
+  static UniquePtr<SFNTNameTable> Create(const uint8_t *aNameData,
+                                         uint32_t aDataLength);
+
+  /**
+   * Gets the full name from the name table. If the full name string is not
+   * present it will use the family space concatenated with the style.
+   * This will only read names that are already UTF16.
+   *
+   * @param aU16FullName string to be populated with the full name.
+   * @return true if the full name is successfully read.
+   */
+  bool GetU16FullName(mozilla::u16string& aU16FullName);
+
+private:
+
+  SFNTNameTable(const NameHeader *aNameHeader, const uint8_t *aNameData,
+                uint32_t aDataLength);
+
+  bool ReadU16Name(const NameRecordMatchers& aMatchers, mozilla::u16string& aU16Name);
+
+  bool ReadU16NameFromRecord(const NameRecord *aNameRecord,
+                             mozilla::u16string& aU16Name);
+
+  const NameRecord *mFirstRecord;
+  const NameRecord *mEndOfRecords;
+  const uint8_t *mStringData;
+  const uint32_t mStringDataLength;
+};
+
+} // gfx
+} // mozilla
+
+#endif // mozilla_gfx_SFNTNameTable_h
--- a/gfx/2d/ScaledFontBase.cpp
+++ b/gfx/2d/ScaledFontBase.cpp
@@ -40,16 +40,43 @@ ScaledFontBase::ScaledFontBase(Float aSi
 #ifdef USE_SKIA
   mTypeface = nullptr;
 #endif
 #ifdef USE_CAIRO_SCALED_FONT
   mScaledFont = nullptr;
 #endif
 }
 
+#ifdef USE_CAIRO_SCALED_FONT
+bool
+ScaledFontBase::PopulateCairoScaledFont()
+{
+  cairo_font_face_t* cairoFontFace = GetCairoFontFace();
+  if (!cairoFontFace) {
+    return false;
+  }
+
+  cairo_matrix_t sizeMatrix;
+  cairo_matrix_t identityMatrix;
+
+  cairo_matrix_init_scale(&sizeMatrix, mSize, mSize);
+  cairo_matrix_init_identity(&identityMatrix);
+
+  cairo_font_options_t *fontOptions = cairo_font_options_create();
+
+  mScaledFont = cairo_scaled_font_create(cairoFontFace, &sizeMatrix,
+    &identityMatrix, fontOptions);
+
+  cairo_font_options_destroy(fontOptions);
+  cairo_font_face_destroy(cairoFontFace);
+
+  return (cairo_scaled_font_status(mScaledFont) == CAIRO_STATUS_SUCCESS);
+}
+#endif
+
 #ifdef USE_SKIA
 SkPath
 ScaledFontBase::GetSkiaPathForGlyphs(const GlyphBuffer &aBuffer)
 {
   SkTypeface *typeFace = GetSkTypeface();
   MOZ_ASSERT(typeFace);
 
   SkPaint paint;
--- a/gfx/2d/ScaledFontBase.h
+++ b/gfx/2d/ScaledFontBase.h
@@ -40,27 +40,30 @@ public:
 #ifdef USE_SKIA
   virtual SkTypeface* GetSkTypeface() { return mTypeface; }
 #endif
 
   // Not true, but required to instantiate a ScaledFontBase.
   virtual FontType GetType() const { return FontType::SKIA; }
 
 #ifdef USE_CAIRO_SCALED_FONT
+  bool PopulateCairoScaledFont();
   cairo_scaled_font_t* GetCairoScaledFont() { return mScaledFont; }
   void SetCairoScaledFont(cairo_scaled_font_t* font);
 #endif
 
 protected:
   friend class DrawTargetSkia;
 #ifdef USE_SKIA
   SkTypeface* mTypeface;
   SkPath GetSkiaPathForGlyphs(const GlyphBuffer &aBuffer);
 #endif
 #ifdef USE_CAIRO_SCALED_FONT
+  // Overridders should ensure the cairo_font_face_t has been addrefed.
+  virtual cairo_font_face_t* GetCairoFontFace() { return nullptr; }
   cairo_scaled_font_t* mScaledFont;
 #endif
   Float mSize;
 };
 
 } // namespace gfx
 } // namespace mozilla
 
--- a/gfx/2d/ScaledFontDWrite.cpp
+++ b/gfx/2d/ScaledFontDWrite.cpp
@@ -5,16 +5,20 @@
 
 #include "ScaledFontDWrite.h"
 #include "PathD2D.h"
 #include "DrawTargetD2D.h"
 #include "Logging.h"
 
 #include <vector>
 
+#ifdef USE_CAIRO_SCALED_FONT
+#include "cairo-win32.h"
+#endif
+
 namespace mozilla {
 namespace gfx {
 
 struct ffReferenceKey
 {
     uint8_t *mData;
     uint32_t mSize;
 };
@@ -426,10 +430,22 @@ ScaledFontDWrite::GetDefaultAAMode()
   if (defaultMode == AntialiasMode::GRAY) {
     if (!DoGrayscale(mFontFace, mSize)) {
       defaultMode = AntialiasMode::NONE;
     }
   }
   return defaultMode;
 }
 
+#ifdef USE_CAIRO_SCALED_FONT
+cairo_font_face_t*
+ScaledFontDWrite::GetCairoFontFace()
+{
+  if (!mFontFace) {
+    return nullptr;
+  }
+
+  return cairo_dwrite_font_face_create_for_dwrite_fontface(nullptr, mFontFace);
+}
+#endif
+
 }
 }
--- a/gfx/2d/ScaledFontDWrite.h
+++ b/gfx/2d/ScaledFontDWrite.h
@@ -39,16 +39,21 @@ public:
   virtual SkTypeface* GetSkTypeface()
   {
     MOZ_ASSERT(false, "Skia and DirectWrite do not mix");
     return nullptr;
   }
 #endif
 
   RefPtr<IDWriteFontFace> mFontFace;
+
+protected:
+#ifdef USE_CAIRO_SCALED_FONT
+  cairo_font_face_t* GetCairoFontFace() override;
+#endif
 };
 
 class GlyphRenderingOptionsDWrite : public GlyphRenderingOptions
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GlyphRenderingOptionsDWrite)
   GlyphRenderingOptionsDWrite(IDWriteRenderingParams *aParams)
     : mParams(aParams)
--- a/gfx/2d/ScaledFontWin.cpp
+++ b/gfx/2d/ScaledFontWin.cpp
@@ -1,34 +1,102 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ScaledFontWin.h"
 #include "ScaledFontBase.h"
 
+#include "Logging.h"
+#include "SFNTData.h"
+
 #ifdef USE_SKIA
 #include "skia/include/ports/SkTypeface_win.h"
 #endif
 
+#ifdef USE_CAIRO_SCALED_FONT
+#include "cairo-win32.h"
+#endif
+
 namespace mozilla {
 namespace gfx {
 
 ScaledFontWin::ScaledFontWin(LOGFONT* aFont, Float aSize)
   : ScaledFontBase(aSize)
   , mLogFont(*aFont)
 {
 }
 
+ScaledFontWin::ScaledFontWin(uint8_t* aFontData, uint32_t aFontDataLength,
+                             uint32_t aIndex, Float aGlyphSize)
+  : ScaledFontBase(aGlyphSize)
+{
+  mLogFont.lfHeight = 0;
+  mLogFont.lfWidth = 0;
+  mLogFont.lfEscapement = 0;
+  mLogFont.lfOrientation = 0;
+  mLogFont.lfWeight = FW_DONTCARE;
+  mLogFont.lfItalic = FALSE;
+  mLogFont.lfUnderline = FALSE;
+  mLogFont.lfStrikeOut = FALSE;
+  mLogFont.lfCharSet = DEFAULT_CHARSET;
+  mLogFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
+  mLogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+  mLogFont.lfQuality = DEFAULT_QUALITY;
+  mLogFont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
+  mLogFont.lfFaceName[0] = 0;
+
+  UniquePtr<SFNTData> sfntData = SFNTData::Create(aFontData, aFontDataLength);
+  if (!sfntData) {
+    gfxWarning() << "Failed to create SFNTData for ScaledFontWin.";
+    return;
+  }
+
+  mozilla::u16string fontName;
+  if (!sfntData->GetU16FullName(aIndex, fontName)) {
+    gfxWarning() << "Failed to get font name from font.";
+    return;
+  }
+
+  // Copy name to mLogFont and add null to end.
+  // lfFaceName has a maximum length including null.
+  if (fontName.size() > LF_FACESIZE - 1) {
+    fontName.resize(LF_FACESIZE - 1);
+  }
+  // We cast here because for VS2015 char16_t != wchar_t, even though they are
+  // both 16 bit.
+  fontName.copy(reinterpret_cast<char16_t*>(mLogFont.lfFaceName),
+                fontName.length());
+  mLogFont.lfFaceName[fontName.length()] = 0;
+
+  DWORD numberOfFontsAdded;
+  HANDLE fontHandle = ::AddFontMemResourceEx(aFontData, aFontDataLength, 0,
+                                             &numberOfFontsAdded);
+  if (fontHandle) {
+    mMemoryFontRemover.reset(new MemoryFontRemover(fontHandle));
+  }
+
+}
+
 #ifdef USE_SKIA
 SkTypeface* ScaledFontWin::GetSkTypeface()
 {
   if (!mTypeface) {
     mTypeface = SkCreateTypefaceFromLOGFONT(mLogFont);
   }
   return mTypeface;
 }
 #endif
 
+#ifdef USE_CAIRO_SCALED_FONT
+cairo_font_face_t*
+ScaledFontWin::GetCairoFontFace()
+{
+  if (mLogFont.lfFaceName[0] == 0) {
+    return nullptr;
+  }
+  return cairo_win32_font_face_create_for_logfontw(&mLogFont);
+}
+#endif
 
 }
 }
--- a/gfx/2d/ScaledFontWin.h
+++ b/gfx/2d/ScaledFontWin.h
@@ -4,32 +4,52 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_GFX_SCALEDFONTWIN_H_
 #define MOZILLA_GFX_SCALEDFONTWIN_H_
 
 #include "ScaledFontBase.h"
 #include <windows.h>
 
+#include "mozilla/UniquePtr.h"
+
 namespace mozilla {
 namespace gfx {
 
 class ScaledFontWin : public ScaledFontBase
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFontWin)
   ScaledFontWin(LOGFONT* aFont, Float aSize);
 
+  ScaledFontWin(uint8_t* aFontData, uint32_t aFontDataLength, uint32_t aIndex,
+                Float aGlyphSize);
+
   virtual FontType GetType() const { return FontType::GDI; }
 #ifdef USE_SKIA
   virtual SkTypeface* GetSkTypeface();
 #endif
+
+protected:
+#ifdef USE_CAIRO_SCALED_FONT
+  cairo_font_face_t* GetCairoFontFace() override;
+#endif
+
 private:
 #ifdef USE_SKIA
   friend class DrawTargetSkia;
 #endif
   LOGFONT mLogFont;
+
+  struct MemoryFontRemover
+  {
+    HANDLE memFontHandle;
+    MemoryFontRemover(HANDLE aMemFontHandle) : memFontHandle(aMemFontHandle) {}
+    ~MemoryFontRemover() { ::RemoveFontMemResourceEx(memFontHandle); }
+  };
+
+  UniquePtr<MemoryFontRemover> mMemoryFontRemover;
 };
 
 }
 }
 
 #endif /* MOZILLA_GFX_SCALEDFONTWIN_H_ */
--- a/gfx/2d/moz.build
+++ b/gfx/2d/moz.build
@@ -148,16 +148,18 @@ UNIFIED_SOURCES += [
     'PathCairo.cpp',
     'PathHelpers.cpp',
     'PathRecording.cpp',
     'Quaternion.cpp',
     'RecordedEvent.cpp',
     'Scale.cpp',
     'ScaledFontBase.cpp',
     'ScaledFontCairo.cpp',
+    'SFNTData.cpp',
+    'SFNTNameTable.cpp',
     'SourceSurfaceCairo.cpp',
     'SourceSurfaceRawData.cpp',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
     EXPORTS.mozilla.gfx += [
         'QuartzSupport.h',
     ]
new file mode 100644
--- /dev/null
+++ b/gfx/2d/u16string.h
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_gfx_u16string_h
+#define mozilla_gfx_u16string_h
+
+#include <string>
+
+#include "mozilla/Char16.h"
+
+namespace mozilla {
+
+#if defined(_MSC_VER)
+typedef std::u16string u16string;
+#else
+typedef std::basic_string<char16_t> u16string;
+#endif
+
+} // mozilla
+
+#endif // mozilla_gfx_u16string_h