Bug 1156742 Part 3: Add support for FontType::CAIRO to CreateScaledFontForTrueTypeData on Windows. r=bas
authorBob Owen <bobowencode@gmail.com>
Tue, 05 Jan 2016 10:08:56 +0000
changeset 300706 ef3078a358d8072dc4c04a9fb6880928884b5bae
parent 300705 e9837ebf5d48f1f7a6f881d4120a47262d5b84c8
child 300707 00b3ae3147bc1eb7f68dc1c412b41069fe41f9ae
push id8978
push userraliiev@mozilla.com
push dateMon, 25 Jan 2016 14:05:32 +0000
treeherdermozilla-aurora@b9a803752a2c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbas
bugs1156742
milestone46.0a1
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