CreateTypefaceFromStream for GDI.
authorbungeman@google.com <bungeman@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Fri, 18 May 2012 19:06:41 +0000 (19:06 +0000)
committerbungeman@google.com <bungeman@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Fri, 18 May 2012 19:06:41 +0000 (19:06 +0000)
http://codereview.appspot.com/5616047/

git-svn-id: http://skia.googlecode.com/svn/trunk@4001 2bbb7eff-a529-9590-31e7-b0007b416f81

gyp/tests.gyp
src/ports/SkFontHost_win.cpp
src/sfnt/SkOTUtils.cpp
src/sfnt/SkOTUtils.h
src/sfnt/SkSFNTHeader.h
tests/FontHostStreamTest.cpp [new file with mode: 0644]

index ec850a4..631d280 100644 (file)
@@ -38,6 +38,7 @@
         '../tests/EmptyPathTest.cpp',
         '../tests/FillPathTest.cpp',
         '../tests/FlateTest.cpp',
+        '../tests/FontHostStreamTest.cpp',
         '../tests/FontHostTest.cpp',
         '../tests/GeometryTest.cpp',
         '../tests/GLInterfaceValidation.cpp',
         'pdf.gyp:pdf',
         'utils.gyp:utils',
       ],
+      'conditions': [
+        [ 'skia_os == "mac"', {
+          'sources!': [
+            #mac port currently does not support fonts from streams.
+            '../tests/FontHostStreamTest.cpp',
+          ],
+        }],
+      ],
     },
   ],
 }
index 20c54a3..cf23e93 100755 (executable)
@@ -6,23 +6,23 @@
  * found in the LICENSE file.
  */
 
-
-#include "SkColorFilter.h"
-#include "SkString.h"
-#include "SkEndian.h"
-#include "SkFontHost.h"
-#include "SkDescriptor.h"
 #include "SkAdvancedTypefaceMetrics.h"
+#include "SkBase64.h"
+#include "SkData.h"
+#include "SkDescriptor.h"
+#include "SkFontHost.h"
+#include "SkOTUtils.h"
 #include "SkStream.h"
+#include "SkString.h"
 #include "SkThread.h"
 #include "SkTypeface_win.h"
 #include "SkTypefaceCache.h"
 #include "SkUtils.h"
 
-#ifdef WIN32
-#include "windows.h"
-#include "tchar.h"
-#include "usp10.h"
+#include "SkTypes.h"
+#include <tchar.h>
+#include <usp10.h>
+#include <objbase.h>
 
 // always packed xxRRGGBB
 typedef uint32_t SkGdiRGB;
@@ -158,16 +158,47 @@ public:
     }
 };
 
+class FontMemResourceTypeface : public LogFontTypeface {
+public:
+    /**
+     *  Takes ownership of fontMemResource.
+     */
+    FontMemResourceTypeface(SkTypeface::Style style, SkFontID fontID, const LOGFONT& lf, HANDLE fontMemResource) :
+      LogFontTypeface(style, fontID, lf), fFontMemResource(fontMemResource) {}
+
+    HANDLE fFontMemResource;
+
+    /**
+     *  The created FontMemResourceTypeface takes ownership of fontMemResource.
+     */
+    static FontMemResourceTypeface* Create(const LOGFONT& lf, HANDLE fontMemResource) {
+        SkTypeface::Style style = get_style(lf);
+        SkFontID fontID = SkTypefaceCache::NewFontID();
+        return new FontMemResourceTypeface(style, fontID, lf, fontMemResource);
+    }
+
+protected:
+    virtual void weak_dispose() const SK_OVERRIDE {
+        RemoveFontMemResourceEx(fFontMemResource);
+        //SkTypefaceCache::Remove(this);
+        INHERITED::weak_dispose();
+    }
+
+private:
+    typedef LogFontTypeface INHERITED;
+};
+
 static const LOGFONT& get_default_font() {
     static LOGFONT gDefaultFont;
     return gDefaultFont;
 }
 
 static bool FindByLogFont(SkTypeface* face, SkTypeface::Style requestedStyle, void* ctx) {
-    LogFontTypeface* lface = reinterpret_cast<LogFontTypeface*>(face);
+    LogFontTypeface* lface = static_cast<LogFontTypeface*>(face);
     const LOGFONT* lf = reinterpret_cast<const LOGFONT*>(ctx);
 
-    return get_style(lface->fLogFont) == requestedStyle &&
+    return lface &&
+           get_style(lface->fLogFont) == requestedStyle &&
            !memcmp(&lface->fLogFont, lf, sizeof(LOGFONT));
 }
 
@@ -187,13 +218,24 @@ SkTypeface* SkCreateTypefaceFromLOGFONT(const LOGFONT& origLF) {
 }
 
 /**
+ *  The created SkTypeface takes ownership of fontMemResource.
+ */
+SkTypeface* SkCreateFontMemResourceTypefaceFromLOGFONT(const LOGFONT& origLF, HANDLE fontMemResource) {
+    LOGFONT lf = origLF;
+    make_canonical(&lf);
+    FontMemResourceTypeface* face = FontMemResourceTypeface::Create(lf, fontMemResource);
+    SkTypefaceCache::Add(face, get_style(lf), false);
+    return face;
+}
+
+/**
  *  This guy is public
  */
 void SkLOGFONTFromTypeface(const SkTypeface* face, LOGFONT* lf) {
     if (NULL == face) {
         *lf = get_default_font();
     } else {
-        *lf = ((const LogFontTypeface*)face)->fLogFont;
+        *lf = static_cast<const LogFontTypeface*>(face)->fLogFont;
     }
 }
 
@@ -205,14 +247,14 @@ SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) {
 }
 
 static void ensure_typeface_accessible(SkFontID fontID) {
-    LogFontTypeface* face = (LogFontTypeface*)SkTypefaceCache::FindByID(fontID);
+    LogFontTypeface* face = static_cast<LogFontTypeface*>(SkTypefaceCache::FindByID(fontID));
     if (face) {
         SkFontHost::EnsureTypefaceAccessible(*face);
     }
 }
 
 static void GetLogFontByID(SkFontID fontID, LOGFONT* lf) {
-    LogFontTypeface* face = (LogFontTypeface*)SkTypefaceCache::FindByID(fontID);
+    LogFontTypeface* face = static_cast<LogFontTypeface*>(SkTypefaceCache::FindByID(fontID));
     if (face) {
         *lf = face->fLogFont;
     } else {
@@ -1291,11 +1333,122 @@ Error:
     return info;
 }
 
+//Dummy representation of a Base64 encoded GUID from create_unique_font_name.
+#define BASE64_GUID_ID "XXXXXXXXXXXXXXXXXXXXXXXX"
+//Length of GUID representation from create_id, including NULL terminator.
+#define BASE64_GUID_ID_LEN SK_ARRAY_COUNT(BASE64_GUID_ID)
+
+SK_COMPILE_ASSERT(BASE64_GUID_ID_LEN < LF_FACESIZE, GUID_longer_than_facesize);
+
+/**
+   NameID 6 Postscript names cannot have the character '/'.
+   It would be easier to hex encode the GUID, but that is 32 bytes,
+   and many systems have issues with names longer than 28 bytes.
+   The following need not be any standard base64 encoding.
+   The encoded value is never decoded.
+*/
+static const char postscript_safe_base64_encode[] = 
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+    "abcdefghijklmnopqrstuvwxyz"
+    "0123456789-_=";
+
+/**
+   Formats a GUID into Base64 and places it into buffer.
+   buffer should have space for at least BASE64_GUID_ID_LEN characters.
+   The string will always be null terminated.
+   XXXXXXXXXXXXXXXXXXXXXXXX0
+ */
+static void format_guid_b64(const GUID& guid, char* buffer, size_t bufferSize) {
+    SkASSERT(bufferSize >= BASE64_GUID_ID_LEN);
+    size_t written = SkBase64::Encode(&guid, sizeof(guid), buffer, postscript_safe_base64_encode);
+    SkASSERT(written < LF_FACESIZE);
+    buffer[written] = '\0';
+}
+
+/**
+   Creates a Base64 encoded GUID and places it into buffer.
+   buffer should have space for at least BASE64_GUID_ID_LEN characters.
+   The string will always be null terminated.
+   XXXXXXXXXXXXXXXXXXXXXXXX0
+ */
+static HRESULT create_unique_font_name(char* buffer, size_t bufferSize) {
+    GUID guid = {};
+    if (FAILED(CoCreateGuid(&guid))) {
+        return E_UNEXPECTED;
+    }
+    format_guid_b64(guid, buffer, bufferSize);
+
+    return S_OK;
+}
+
+/**
+   Introduces a font to GDI. On failure will return NULL. The returned handle
+   should eventually be passed to RemoveFontMemResourceEx.
+*/
+static HANDLE activate_font(SkData* fontData) {
+    DWORD numFonts = 0;
+    //AddFontMemResourceEx just copies the data, but does not specify const.
+    HANDLE fontHandle = AddFontMemResourceEx(const_cast<void*>(fontData->data()),
+                                             fontData->size(),
+                                             0,
+                                             &numFonts);
+
+    if (fontHandle != NULL && numFonts < 1) {
+        RemoveFontMemResourceEx(fontHandle);
+        return NULL;
+    }
+
+    return fontHandle;
+}
+
+static void logfont_for_name(const char* familyName, LOGFONT& lf) {
+        memset(&lf, 0, sizeof(LOGFONT));
+#ifdef UNICODE
+        // Get the buffer size needed first.
+        size_t str_len = ::MultiByteToWideChar(CP_UTF8, 0, familyName,
+                                                -1, NULL, 0);
+        // Allocate a buffer (str_len already has terminating null
+        // accounted for).
+        wchar_t *wideFamilyName = new wchar_t[str_len];
+        // Now actually convert the string.
+        ::MultiByteToWideChar(CP_UTF8, 0, familyName, -1,
+                                wideFamilyName, str_len);
+        ::wcsncpy(lf.lfFaceName, wideFamilyName, LF_FACESIZE);
+        delete [] wideFamilyName;
+        lf.lfFaceName[LF_FACESIZE-1] = L'\0';
+#else
+        ::strncpy(lf.lfFaceName, familyName, LF_FACESIZE);
+        lf.lfFaceName[LF_FACESIZE-1] = '\0';
+#endif
+}
+
 SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
+    // Create a unique and unpredictable font name.
+    // Avoids collisions and access from CSS.
+    char familyName[BASE64_GUID_ID_LEN];
+    const int familyNameSize = SK_ARRAY_COUNT(familyName);
+    if (FAILED(create_unique_font_name(familyName, familyNameSize))) {
+        return NULL;
+    }
+    
+    // Change the name of the font.
+    SkData* rewrittenFontData = SkOTUtils::RenameFont(stream, familyName, familyNameSize-1);
+    if (NULL == rewrittenFontData) {
+        return NULL;
+    }
+    SkAutoUnref aur = SkAutoUnref(rewrittenFontData);
 
-    //Should not be used on Windows, keep linker happy
-    SkASSERT(false);
-    return SkCreateTypefaceFromLOGFONT(get_default_font());
+    // Register the font with GDI.
+    HANDLE fontReference = activate_font(rewrittenFontData);
+    if (NULL == fontReference) {
+        return NULL;
+    }
+
+    // Create the typeface.
+    LOGFONT lf;
+    logfont_for_name(familyName, lf);
+
+    return SkCreateFontMemResourceTypefaceFromLOGFONT(lf, fontReference);
 }
 
 SkStream* SkFontHost::OpenStream(SkFontID uniqueID) {
@@ -1358,23 +1511,7 @@ SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
         LogFontTypeface* face = (LogFontTypeface*)familyFace;
         lf = face->fLogFont;
     } else {
-        memset(&lf, 0, sizeof(LOGFONT));
-#ifdef UNICODE
-        // Get the buffer size needed first.
-        size_t str_len = ::MultiByteToWideChar(CP_UTF8, 0, familyName,
-                                                -1, NULL, 0);
-        // Allocate a buffer (str_len already has terminating null
-        // accounted for).
-        wchar_t *wideFamilyName = new wchar_t[str_len];
-        // Now actually convert the string.
-        ::MultiByteToWideChar(CP_UTF8, 0, familyName, -1,
-                                wideFamilyName, str_len);
-        ::wcsncpy(lf.lfFaceName, wideFamilyName, LF_FACESIZE);
-        delete [] wideFamilyName;
-#else
-        ::strncpy(lf.lfFaceName, familyName, LF_FACESIZE);
-#endif
-        lf.lfFaceName[LF_FACESIZE-1] = '\0';
+        logfont_for_name(familyName, lf);
     }
     setStyle(&lf, style);
     return SkCreateTypefaceFromLOGFONT(lf);
@@ -1446,5 +1583,3 @@ void SkFontHost::FilterRec(SkScalerContext::Rec* rec) {
     }
 #endif
 }
-
-#endif // WIN32
index 1301a06..3e7992f 100644 (file)
@@ -5,6 +5,12 @@
  * found in the LICENSE file.
  */
 
+#include "SkData.h"
+#include "SkEndian.h"
+#include "SkSFNTHeader.h"
+#include "SkStream.h"
+#include "SkOTTable_head.h"
+#include "SkOTTable_name.h"
 #include "SkOTTableTypes.h"
 #include "SkOTUtils.h"
 
@@ -16,3 +22,131 @@ uint32_t SkOTUtils::CalcTableChecksum(SK_OT_ULONG *data, size_t length) {
     }
     return sum;
 }
+
+SkData* SkOTUtils::RenameFont(SkStream* fontData,
+                              const char* fontName, int fontNameLen) {
+
+    // Get the sfnt header.
+    SkSFNTHeader sfntHeader;
+    if (fontData->read(&sfntHeader, sizeof(sfntHeader)) < sizeof(sfntHeader)) {
+        return NULL;
+    }
+
+    // Find the existing 'name' table.
+    int tableIndex;
+    SkSFNTTableDirectoryEntry tableEntry;
+    int numTables = SkEndian_SwapBE16(sfntHeader.numTables);
+    for (tableIndex = 0; tableIndex < numTables; ++tableIndex) {
+        if (fontData->read(&tableEntry, sizeof(tableEntry)) < sizeof(tableEntry)) {
+            return NULL;
+        }
+        if ('name' == SkEndian_SwapBE32(tableEntry.tag)) {
+            break;
+        }
+    }
+    if (tableIndex == numTables) {
+        return NULL;
+    }
+
+    if (!fontData->rewind()) {
+        return NULL;
+    }
+
+    // The required 'name' record types: Family, Style, Unique, Full and PostScript.
+    const SkOTTableNameRecord::NameID::Predefined::Value namesToCreate[] = {
+        SkOTTableNameRecord::NameID::Predefined::FontFamilyName,
+        SkOTTableNameRecord::NameID::Predefined::FontSubfamilyName,
+        SkOTTableNameRecord::NameID::Predefined::UniqueFontIdentifier,
+        SkOTTableNameRecord::NameID::Predefined::FullFontName,
+        SkOTTableNameRecord::NameID::Predefined::PostscriptName,
+    };
+    const int namesCount = SK_ARRAY_COUNT(namesToCreate);
+
+    // Copy the data, leaving out the old name table.
+    // In theory, we could also remove the DSIG table if it exists.
+    size_t nameTableLogicalSize = sizeof(SkOTTableName) + (namesCount * sizeof(SkOTTableNameRecord)) + (fontNameLen * sizeof(wchar_t));
+    size_t nameTablePhysicalSize = (nameTableLogicalSize + 3) & ~3; // Rounded up to a multiple of 4.
+
+    size_t oldNameTablePhysicalSize = (SkEndian_SwapBE32(tableEntry.logicalLength) + 3) & ~3; // Rounded up to a multiple of 4.
+    size_t oldNameTableOffset = SkEndian_SwapBE32(tableEntry.offset);
+
+    //originalDataSize is the size of the original data without the name table.
+    size_t originalDataSize = fontData->getLength() - oldNameTablePhysicalSize;
+    size_t newDataSize = originalDataSize + nameTablePhysicalSize;
+    
+    SK_OT_BYTE* data = static_cast<SK_OT_BYTE*>(sk_malloc_throw(newDataSize));
+    SkAutoTUnref<SkData> rewrittenFontData = SkAutoTUnref<SkData>(SkData::NewFromMalloc(data, newDataSize));
+
+    if (fontData->read(data, oldNameTableOffset) < oldNameTableOffset) {
+        return NULL;
+    }
+    if (fontData->skip(oldNameTablePhysicalSize) < oldNameTablePhysicalSize) {
+        return NULL;
+    }
+    if (fontData->read(data + oldNameTableOffset, originalDataSize - oldNameTableOffset) < originalDataSize - oldNameTableOffset) {
+        return NULL;
+    }
+
+    //Fix up the offsets of the directory entries after the old 'name' table entry.
+    SkSFNTTableDirectoryEntry* currentEntry = reinterpret_cast<SkSFNTTableDirectoryEntry*>(data + sizeof(SkSFNTHeader));
+    SkSFNTTableDirectoryEntry* endEntry = currentEntry + numTables;
+    SkSFNTTableDirectoryEntry* headTableEntry = NULL;
+    for (; currentEntry < endEntry; ++currentEntry) {
+        uint32_t oldOffset = SkEndian_SwapBE32(currentEntry->offset);
+        if (oldOffset > oldNameTableOffset) {
+            currentEntry->offset = SkEndian_SwapBE32(oldOffset - oldNameTablePhysicalSize);
+        }
+        if ('head' == SkEndian_SwapBE32(tableEntry.tag)) {
+            headTableEntry = currentEntry;
+        }
+    }
+
+    // Make the table directory entry point to the new 'name' table.
+    SkSFNTTableDirectoryEntry* nameTableEntry = reinterpret_cast<SkSFNTTableDirectoryEntry*>(data + sizeof(SkSFNTHeader)) + tableIndex;
+    nameTableEntry->logicalLength = SkEndian_SwapBE32(nameTableLogicalSize);
+    nameTableEntry->offset = SkEndian_SwapBE32(originalDataSize);
+
+    // Write the new 'name' table after the original font data.
+    SkOTTableName* nameTable = reinterpret_cast<SkOTTableName*>(data + originalDataSize);
+    unsigned short stringOffset = sizeof(SkOTTableName) + (namesCount * sizeof(SkOTTableNameRecord));
+    nameTable->format = SkOTTableName::format_0;
+    nameTable->count = SkEndian_SwapBE16(namesCount);
+    nameTable->stringOffset = SkEndian_SwapBE16(stringOffset);
+
+    SkOTTableNameRecord* nameRecords = reinterpret_cast<SkOTTableNameRecord*>(data + originalDataSize + sizeof(SkOTTableName));
+    for (int i = 0; i < namesCount; ++i) {
+        nameRecords[i].platformID.value = SkOTTableNameRecord::PlatformID::Windows;
+        nameRecords[i].encodingID.windows.value = SkOTTableNameRecord::EncodingID::Windows::UnicodeBMPUCS2;
+        nameRecords[i].languageID.windows.value = SkOTTableNameRecord::LanguageID::Windows::English_UnitedStates;
+        nameRecords[i].nameID.predefined.value = namesToCreate[i];
+        nameRecords[i].offset = SkEndian_SwapBE16(0);
+        nameRecords[i].length = SkEndian_SwapBE16(fontNameLen * sizeof(wchar_t));
+    }
+
+    SK_OT_USHORT* nameString = reinterpret_cast<SK_OT_USHORT*>(data + originalDataSize + stringOffset);
+    for (int i = 0; i < fontNameLen; ++i) {
+        nameString[i] = SkEndian_SwapBE16(fontName[i]);
+    }
+
+    unsigned char* logical = data + originalDataSize + nameTableLogicalSize;
+    unsigned char* physical = data + originalDataSize + nameTablePhysicalSize;
+    for (; logical < physical; ++logical) {
+        *logical = 0;
+    }
+
+    // Update the table checksum in the directory entry.
+    nameTableEntry->checksum = SkEndian_SwapBE32(SkOTUtils::CalcTableChecksum(reinterpret_cast<SK_OT_ULONG*>(nameTable), nameTableLogicalSize));
+
+    // Update the checksum adjustment in the head table.
+    if (headTableEntry) {
+        size_t headTableOffset = SkEndian_SwapBE32(headTableEntry->offset);
+        if (headTableOffset + sizeof(SkOTTableHead) < originalDataSize) {
+            SkOTTableHead* headTable = reinterpret_cast<SkOTTableHead*>(data + headTableOffset);
+            headTable->checksumAdjustment = SkEndian_SwapBE32(0);
+            uint32_t unadjustedFontChecksum = SkOTUtils::CalcTableChecksum(reinterpret_cast<SK_OT_ULONG*>(data), originalDataSize + nameTablePhysicalSize);
+            headTable->checksumAdjustment = SkEndian_SwapBE32(SkOTTableHead::fontChecksum - unadjustedFontChecksum);
+        }
+    }
+
+    return rewrittenFontData.detach();
+}
index 7a58b4d..1398de6 100644 (file)
@@ -9,9 +9,29 @@
 #define SkOTUtils_DEFINED
 
 #include "SkOTTableTypes.h"
+class SkStream;
 
 struct SkOTUtils {
+    /**
+      *  Calculates the OpenType checksum for data.
+      */
     static uint32_t CalcTableChecksum(SK_OT_ULONG *data, size_t length);
+
+    /**
+      *  Renames an sfnt font. On failure (invalid data or not an sfnt font)
+      *  returns NULL.
+      *
+      *  Essentially, this removes any existing 'name' table and replaces it
+      *  with a new one in which FontFamilyName, FontSubfamilyName,
+      *  UniqueFontIdentifier, FullFontName, and PostscriptName are fontName.
+      *
+      *  The new 'name' table records will be written with the Windows,
+      *  UnicodeBMPUCS2, and English_UnitedStates settings.
+      *
+      *  fontName and fontNameLen must be specified in terms of ASCII chars.
+      */
+    static SkData* RenameFont(SkStream* fontData,
+                              const char* fontName, int fontNameLen);
 };
 
 #endif
index a36dfa4..bf01ac9 100644 (file)
@@ -9,6 +9,7 @@
 #define SkSFNTHeader_DEFINED
 
 #include "SkEndian.h"
+#include "SkOTTableTypes.h"
 
 //All SK_SFNT_ prefixed types should be considered as big endian.
 typedef uint16_t SK_SFNT_USHORT;
diff --git a/tests/FontHostStreamTest.cpp b/tests/FontHostStreamTest.cpp
new file mode 100644 (file)
index 0000000..b1c49f8
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SkTypes.h"
+
+#include "Test.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkColor.h"
+#include "SkFontHost.h"
+#include "SkGraphics.h"
+#include "SkPaint.h"
+#include "SkPoint.h"
+#include "SkRect.h"
+#include "SkTypeface.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const SkColor bgColor = SK_ColorWHITE;
+
+static void create(SkBitmap* bm, SkIRect bound, SkBitmap::Config config) {
+    bm->setConfig(config, bound.width(), bound.height());
+    bm->allocPixels();
+}
+
+static void drawBG(SkCanvas* canvas) {
+    canvas->drawColor(bgColor);
+}
+
+/** Assumes that the ref draw was completely inside ref canvas --
+    implies that everything outside is "bgColor".
+    Checks that all overlap is the same and that all non-overlap on the
+    ref is "bgColor".
+ */
+static bool compare(const SkBitmap& ref, const SkIRect& iref,
+                    const SkBitmap& test, const SkIRect& itest)
+{
+    const int xOff = itest.fLeft - iref.fLeft;
+    const int yOff = itest.fTop - iref.fTop;
+
+    SkAutoLockPixels alpRef(ref);
+    SkAutoLockPixels alpTest(test);
+
+    for (int y = 0; y < test.height(); ++y) {
+        for (int x = 0; x < test.width(); ++x) {
+            SkColor testColor = test.getColor(x, y);
+            int refX = x + xOff;
+            int refY = y + yOff;
+            SkColor refColor;
+            if (refX >= 0 && refX < ref.width() &&
+                refY >= 0 && refY < ref.height())
+            {
+                refColor = ref.getColor(refX, refY);
+            } else {
+                refColor = bgColor;
+            }
+            if (refColor != testColor) {
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+static void test_fontHostStream(skiatest::Reporter* reporter) {
+
+    {
+        SkPaint paint;
+        paint.setColor(SK_ColorGRAY);
+        paint.setTextSize(SkIntToScalar(30));
+    
+        paint.setTypeface(SkTypeface::CreateFromName("Georgia", SkTypeface::kNormal))->unref();
+
+        SkIRect origRect = SkIRect::MakeWH(64, 64);
+        SkBitmap origBitmap;
+        create(&origBitmap, origRect, SkBitmap::kARGB_8888_Config);
+        SkCanvas origCanvas(origBitmap);
+
+        SkIRect streamRect = SkIRect::MakeWH(64, 64);
+        SkBitmap streamBitmap;
+        create(&streamBitmap, streamRect, SkBitmap::kARGB_8888_Config);
+        SkCanvas streamCanvas(streamBitmap);
+
+        SkPoint point = SkPoint::Make(24, 32);
+
+        // Test: origTypeface and streamTypeface from orig data draw the same
+        drawBG(&origCanvas);
+        origCanvas.drawText("A", 1, point.fX, point.fY, paint);
+
+        SkTypeface* origTypeface = paint.getTypeface();
+        const SkFontID typefaceID = SkTypeface::UniqueID(origTypeface);
+        SkStream* fontData = SkFontHost::OpenStream(typefaceID);
+        SkTypeface* streamTypeface = SkTypeface::CreateFromStream(fontData);
+        paint.setTypeface(streamTypeface)->unref();
+        drawBG(&streamCanvas);
+        streamCanvas.drawPosText("A", 1, &point, paint);
+
+        REPORTER_ASSERT(reporter,
+                        compare(origBitmap, origRect, streamBitmap, streamRect));
+    }
+    //Make sure the typeface is deleted and removed.
+    SkGraphics::PurgeFontCache();
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("FontHost::CreateTypefaceFromStream", FontHostStreamTestClass, test_fontHostStream)