Add getFamilyNames to SkTypeface.
authorbungeman@google.com <bungeman@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 7 Aug 2013 02:45:25 +0000 (02:45 +0000)
committerbungeman@google.com <bungeman@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 7 Aug 2013 02:45:25 +0000 (02:45 +0000)
Committed: https://code.google.com/p/skia/source/detail?r=10589

Review URL: https://codereview.chromium.org/21716005

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

14 files changed:
include/core/SkTypeface.h
src/core/SkTypeface.cpp
src/fonts/SkGScalerContext.cpp
src/fonts/SkGScalerContext.h
src/ports/SkFontHost_FreeType.cpp
src/ports/SkFontHost_FreeType_common.h
src/ports/SkFontHost_mac.cpp
src/ports/SkFontHost_win.cpp
src/ports/SkFontHost_win_dw.cpp
src/sfnt/SkOTTable_name.cpp
src/sfnt/SkOTTable_name.h
src/sfnt/SkOTUtils.cpp
src/sfnt/SkOTUtils.h
tests/FontNamesTest.cpp

index 3cfa6ac..a2bef4b 100644 (file)
@@ -225,6 +225,22 @@ public:
      */
     int getUnitsPerEm() const;
 
+    struct LocalizedString {
+        SkString fString;
+        SkString fLanguage;
+    };
+    class LocalizedStrings : ::SkNoncopyable {
+    public:
+        virtual ~LocalizedStrings() { }
+        virtual bool next(LocalizedString* localizedString) = 0;
+    };
+    /**
+     *  Returns an iterator which will attempt to enumerate all of the
+     *  family names specified by the font.
+     *  It is the caller's responsibility to SK_DELETE the returned pointer.
+     */
+    LocalizedStrings* getFamilyNames() const;
+
     /**
      *  Return the family name for this typeface. It will always be returned
      *  encoded as UTF8, but the language of the name is whatever the host
@@ -295,6 +311,8 @@ protected:
 
     virtual int onGetUPEM() const = 0;
 
+    virtual LocalizedStrings* onGetFamilyNames() const = 0;
+
     virtual int onGetTableTags(SkFontTableTag tags[]) const = 0;
     virtual size_t onGetTableData(SkFontTableTag, size_t offset,
                                   size_t length, void* data) const = 0;
index 34ff853..507c1bf 100644 (file)
@@ -194,6 +194,10 @@ int SkTypeface::getUnitsPerEm() const {
     return this->onGetUPEM();
 }
 
+SkTypeface::LocalizedStrings* SkTypeface::getFamilyNames() const {
+    return this->onGetFamilyNames();
+}
+
 void SkTypeface::getFamilyName(SkString* name) const {
     bool isLocal = false;
     SkFontDescriptor desc(this->style());
index 882c70b..34a55e6 100644 (file)
@@ -202,6 +202,10 @@ int SkGTypeface::onGetUPEM() const {
     return fProxy->getUnitsPerEm();
 }
 
+SkTypeface::LocalizedStrings* SkGTypeface::onGetFamilyNames() const {
+    return fProxy->getFamilyNames();
+}
+
 int SkGTypeface::onGetTableTags(SkFontTableTag tags[]) const {
     return fProxy->getTableTags(tags);
 }
index 1aa1df3..0a06b7c 100644 (file)
@@ -32,6 +32,8 @@ protected:
     virtual int onCountGlyphs() const SK_OVERRIDE;
     virtual int onGetUPEM() const SK_OVERRIDE;
 
+    virtual SkTypeface::LocalizedStrings* onGetFamilyNames() const SK_OVERRIDE;
+
     virtual int onGetTableTags(SkFontTableTag tags[]) const SK_OVERRIDE;
     virtual size_t onGetTableData(SkFontTableTag, size_t offset,
                                   size_t length, void* data) const SK_OVERRIDE;
index 3a32ea4..c5542a9 100644 (file)
@@ -17,6 +17,7 @@
 #include "SkGlyph.h"
 #include "SkMask.h"
 #include "SkMaskGamma.h"
+#include "SkOTUtils.h"
 #include "SkAdvancedTypefaceMetrics.h"
 #include "SkScalerContext.h"
 #include "SkStream.h"
@@ -1402,6 +1403,18 @@ int SkTypeface_FreeType::onCountGlyphs() const {
     return fGlyphCount;
 }
 
+SkTypeface::LocalizedStrings* SkTypeface_FreeType::onGetFamilyNames() const {
+    SkTypeface::LocalizedStrings* nameIter =
+        SkOTUtils::LocalizedStrings_NameTable::CreateForFamilyNames(*this);
+    if (NULL == nameIter) {
+        SkString familyName;
+        this->getFamilyName(&familyName);
+        SkString language("und"); //undetermined
+        nameIter = new SkOTUtils::LocalizedStrings_SingleName(familyName, language);
+    }
+    return nameIter;
+}
+
 int SkTypeface_FreeType::onGetTableTags(SkFontTableTag tags[]) const {
     AutoFTAccess fta(this);
     FT_Face face = fta.face();
index d3df9ce..5c069d0 100644 (file)
@@ -64,6 +64,8 @@ protected:
                                 int glyphCount) const SK_OVERRIDE;
     virtual int onCountGlyphs() const SK_OVERRIDE;
 
+    virtual LocalizedStrings* onGetFamilyNames() const SK_OVERRIDE;
+
     virtual int onGetTableTags(SkFontTableTag tags[]) const SK_OVERRIDE;
     virtual size_t onGetTableData(SkFontTableTag, size_t offset,
                                   size_t length, void* data) const SK_OVERRIDE;
index 71fa5eb..9600730 100755 (executable)
@@ -95,6 +95,7 @@ public:
     operator CFRef() const { return fCFRef; }
     CFRef get() const { return fCFRef; }
 
+    CFRef* operator&() { SkASSERT(fCFRef == NULL); return &fCFRef; }
 private:
     CFRef fCFRef;
 };
@@ -453,6 +454,7 @@ protected:
 
     virtual int onGetUPEM() const SK_OVERRIDE;
     virtual SkStream* onOpenStream(int* ttcIndex) const SK_OVERRIDE;
+    virtual SkTypeface::LocalizedStrings* onGetFamilyNames() const SK_OVERRIDE;
     virtual int onGetTableTags(SkFontTableTag tags[]) const SK_OVERRIDE;
     virtual size_t onGetTableData(SkFontTableTag, size_t offset,
                                   size_t length, void* data) const SK_OVERRIDE;
@@ -1748,6 +1750,30 @@ int SkTypeface_Mac::onGetUPEM() const {
     return CGFontGetUnitsPerEm(cgFont);
 }
 
+SkTypeface::LocalizedStrings* SkTypeface_Mac::onGetFamilyNames() const {
+    SkTypeface::LocalizedStrings* nameIter =
+        SkOTUtils::LocalizedStrings_NameTable::CreateForFamilyNames(*this);
+    if (NULL == nameIter) {
+        AutoCFRelease<CFStringRef> cfLanguage;
+        AutoCFRelease<CFStringRef> cfFamilyName(
+            CTFontCopyLocalizedName(fFontRef, kCTFontFamilyNameKey, &cfLanguage));
+
+        SkString skLanguage;
+        SkString skFamilyName;
+        if (cfLanguage.get()) {
+            CFStringToSkString(cfLanguage.get(), &skLanguage);
+        } else {
+            skLanguage = "und"; //undetermined
+        }
+        if (cfFamilyName.get()) {
+            CFStringToSkString(cfFamilyName.get(), &skFamilyName);
+        }
+
+        nameIter = new SkOTUtils::LocalizedStrings_SingleName(skFamilyName, skLanguage);
+    }
+    return nameIter;
+}
+
 // 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.
index fe646d7..d52774c 100755 (executable)
@@ -16,6 +16,7 @@
 #include "SkGlyph.h"
 #include "SkMaskGamma.h"
 #include "SkOTTable_maxp.h"
+#include "SkOTTable_name.h"
 #include "SkOTUtils.h"
 #include "SkPath.h"
 #include "SkSFNTHeader.h"
@@ -96,6 +97,26 @@ static void tchar_to_skstring(const TCHAR t[], SkString* s) {
 #endif
 }
 
+static void dcfontname_to_skstring(HDC deviceContext, const LOGFONT& lf, SkString* familyName) {
+    int fontNameLen; //length of fontName in TCHARS.
+    if (0 == (fontNameLen = GetTextFace(deviceContext, 0, NULL))) {
+        call_ensure_accessible(lf);
+        if (0 == (fontNameLen = GetTextFace(deviceContext, 0, NULL))) {
+            fontNameLen = 0;
+        }
+    }
+
+    SkAutoSTArray<LF_FULLFACESIZE, TCHAR> fontName(fontNameLen+1);
+    if (0 == GetTextFace(deviceContext, fontNameLen, fontName.get())) {
+        call_ensure_accessible(lf);
+        if (0 == GetTextFace(deviceContext, fontNameLen, fontName.get())) {
+            fontName[0] = 0;
+        }
+    }
+
+    tchar_to_skstring(fontName.get(), familyName);
+}
+
 static void make_canonical(LOGFONT* lf) {
     lf->lfHeight = -2048;
     lf->lfQuality = CLEARTYPE_QUALITY;//PROOF_QUALITY;
@@ -223,10 +244,6 @@ public:
                       (textMetric.tmPitchAndFamily & TMPF_DEVICE));
     }
 
-    void getFamilyName(SkString* name) const {
-        tchar_to_skstring(fLogFont.lfFaceName, name);
-    }
-
     LOGFONT fLogFont;
     bool fSerializeAsStream;
     bool fCanBeLCD;
@@ -251,6 +268,7 @@ protected:
     virtual void onGetFontDescriptor(SkFontDescriptor*, bool*) const SK_OVERRIDE;
     virtual int onCountGlyphs() const SK_OVERRIDE;
     virtual int onGetUPEM() const SK_OVERRIDE;
+    virtual SkTypeface::LocalizedStrings* onGetFamilyNames() const SK_OVERRIDE;
     virtual int onGetTableTags(SkFontTableTag tags[]) const SK_OVERRIDE;
     virtual size_t onGetTableData(SkFontTableTag, size_t offset,
                                   size_t length, void* data) const SK_OVERRIDE;
@@ -1715,21 +1733,8 @@ void LogFontTypeface::onGetFontDescriptor(SkFontDescriptor* desc,
     HDC deviceContext = ::CreateCompatibleDC(NULL);
     HFONT savefont = (HFONT)SelectObject(deviceContext, font);
 
-    int fontNameLen; //length of fontName in TCHARS.
-    if (0 == (fontNameLen = GetTextFace(deviceContext, 0, NULL))) {
-        call_ensure_accessible(fLogFont);
-        if (0 == (fontNameLen = GetTextFace(deviceContext, 0, NULL))) {
-            fontNameLen = 0;
-        }
-    }
-
-    SkAutoSTArray<LF_FULLFACESIZE, TCHAR> fontName(fontNameLen+1);
-    if (0 == GetTextFace(deviceContext, fontNameLen, fontName.get())) {
-        call_ensure_accessible(fLogFont);
-        if (0 == GetTextFace(deviceContext, fontNameLen, fontName.get())) {
-            fontName[0] = 0;
-        }
-    }
+    SkString familyName;
+    dcfontname_to_skstring(deviceContext, fLogFont, &familyName);
 
     if (deviceContext) {
         ::SelectObject(deviceContext, savefont);
@@ -1739,9 +1744,6 @@ void LogFontTypeface::onGetFontDescriptor(SkFontDescriptor* desc,
         ::DeleteObject(font);
     }
 
-    SkString familyName;
-    tchar_to_skstring(fontName.get(), &familyName);
-
     desc->setFamilyName(familyName.c_str());
     *isLocalStream = this->fSerializeAsStream;
 }
@@ -2054,6 +2056,18 @@ int LogFontTypeface::onGetUPEM() const {
     return upem;
 }
 
+SkTypeface::LocalizedStrings* LogFontTypeface::onGetFamilyNames() const {
+    SkTypeface::LocalizedStrings* nameIter =
+        SkOTUtils::LocalizedStrings_NameTable::CreateForFamilyNames(*this);
+    if (NULL == nameIter) {
+        SkString familyName;
+        this->getFamilyName(&familyName);
+        SkString language("und"); //undetermined
+        nameIter = new SkOTUtils::LocalizedStrings_SingleName(familyName, language);
+    }
+    return nameIter;
+}
+
 int LogFontTypeface::onGetTableTags(SkFontTableTag tags[]) const {
     SkSFNTHeader header;
     if (sizeof(header) != this->onGetTableData(0, 0, sizeof(header), &header)) {
@@ -2191,21 +2205,25 @@ SkTypeface* LogFontTypeface::onRefMatchingStyle(Style style) const {
 #include "SkFontMgr.h"
 #include "SkDataTable.h"
 
-static bool valid_logfont_for_enum(const LOGFONT& lf, DWORD fontType) {
-    return TRUETYPE_FONTTYPE == fontType
-        && lf.lfFaceName[0]
-        && lf.lfFaceName[0] != '@'
-        && OUT_STROKE_PRECIS == lf.lfOutPrecision
-        // without the chraset check, we got LOTS of dups of the same font
-        // is there a better check (other than searching the array for
-        // the same name?
-        && 0 == lf.lfCharSet
-        ;
-}
-
-static int CALLBACK enum_fonts_proc(const LOGFONT* lf, const TEXTMETRIC*,
-                                    DWORD fontType, LPARAM builderParam) {
-    if (valid_logfont_for_enum(*lf, fontType)) {
+static bool valid_logfont_for_enum(const LOGFONT& lf) {
+    // TODO: Vector FON is unsupported and should not be listed.
+    return
+        // Ignore implicit vertical variants.
+        lf.lfFaceName[0] && lf.lfFaceName[0] != '@'
+
+        // DEFAULT_CHARSET is used to get all fonts, but also implies all
+        // character sets. Filter assuming all fonts support ANSI_CHARSET.
+        && ANSI_CHARSET == lf.lfCharSet
+    ;
+}
+
+/** An EnumFontFamExProc implementation which interprets builderParam as
+ *  an SkTDArray<ENUMLOGFONTEX>* and appends logfonts which
+ *  pass the valid_logfont_for_enum predicate.
+ */
+static int CALLBACK enum_family_proc(const LOGFONT* lf, const TEXTMETRIC*,
+                                     DWORD fontType, LPARAM builderParam) {
+    if (valid_logfont_for_enum(*lf)) {
         SkTDArray<ENUMLOGFONTEX>* array = (SkTDArray<ENUMLOGFONTEX>*)builderParam;
         *array->append() = *(ENUMLOGFONTEX*)lf;
     }
@@ -2221,8 +2239,13 @@ static SkFontStyle compute_fontstyle(const LOGFONT& lf) {
 class SkFontStyleSetGDI : public SkFontStyleSet {
 public:
     SkFontStyleSetGDI(const TCHAR familyName[]) {
+        LOGFONT lf;
+        sk_bzero(&lf, sizeof(lf));
+        lf.lfCharSet = DEFAULT_CHARSET;
+        _tcscpy_s(lf.lfFaceName, familyName);
+
         HDC hdc = ::CreateCompatibleDC(NULL);
-        ::EnumFonts(hdc, familyName, enum_fonts_proc, (LPARAM)&fArray);
+        ::EnumFontFamiliesEx(hdc, &lf, enum_family_proc, (LPARAM)&fArray, 0);
         ::DeleteDC(hdc);
     }
 
@@ -2260,29 +2283,9 @@ private:
     SkTDArray<ENUMLOGFONTEX> fArray;
 };
 
-static int CALLBACK enum_family_proc(const LOGFONT* lf, const TEXTMETRIC* tm,
-                                     DWORD fontType, LPARAM builderParam) {
-    if (valid_logfont_for_enum(*lf, fontType)) {
-        SkTDArray<ENUMLOGFONTEX>* array = (SkTDArray<ENUMLOGFONTEX>*)builderParam;
-        *array->append() = *(ENUMLOGFONTEX*)lf;
-#if 0
-        SkString str;
-        tchar_to_skstring(lf->lfFaceName, &str);
-        SkDebugf("fam:%s height:%d width:%d esc:%d orien:%d weight:%d ital:%d char:%d clip:%d qual:%d pitch:%d\n",
-                 str.c_str(), lf->lfHeight, lf->lfWidth, lf->lfEscapement, lf->lfOrientation,
-                 lf->lfWeight, lf->lfItalic, lf->lfCharSet, lf->lfClipPrecision, lf->lfQuality,
-                 lf->lfPitchAndFamily);
-#endif
-    }
-    return 1; // non-zero means continue
-}
-
 class SkFontMgrGDI : public SkFontMgr {
-    void init() {
-        if (!fLogFontArray.isEmpty()) {
-            return;
-        }
-
+public:
+    SkFontMgrGDI() {
         LOGFONT lf;
         sk_bzero(&lf, sizeof(lf));
         lf.lfCharSet = DEFAULT_CHARSET;
@@ -2292,23 +2295,17 @@ class SkFontMgrGDI : public SkFontMgr {
         ::DeleteDC(hdc);
     }
 
-public:
-    SkFontMgrGDI() {}
-
 protected:
     virtual int onCountFamilies() SK_OVERRIDE {
-        this->init();
         return fLogFontArray.count();
     }
 
     virtual void onGetFamilyName(int index, SkString* familyName) SK_OVERRIDE {
-        this->init();
         SkASSERT((unsigned)index < (unsigned)fLogFontArray.count());
         tchar_to_skstring(fLogFontArray[index].elfLogFont.lfFaceName, familyName);
     }
 
     virtual SkFontStyleSet* onCreateStyleSet(int index) SK_OVERRIDE {
-        this->init();
         SkASSERT((unsigned)index < (unsigned)fLogFontArray.count());
         return SkNEW_ARGS(SkFontStyleSetGDI, (fLogFontArray[index].elfLogFont.lfFaceName));
     }
index beec056..cee8b36 100644 (file)
@@ -495,6 +495,7 @@ protected:
     virtual void onGetFontDescriptor(SkFontDescriptor*, bool*) const SK_OVERRIDE;
     virtual int onCountGlyphs() const SK_OVERRIDE;
     virtual int onGetUPEM() const SK_OVERRIDE;
+    virtual SkTypeface::LocalizedStrings* onGetFamilyNames() const SK_OVERRIDE;
     virtual int onGetTableTags(SkFontTableTag tags[]) const SK_OVERRIDE;
     virtual size_t onGetTableData(SkFontTableTag, size_t offset,
                                   size_t length, void* data) const SK_OVERRIDE;
@@ -1088,6 +1089,54 @@ int DWriteFontTypeface::onGetUPEM() const {
     return metrics.designUnitsPerEm;
 }
 
+class LocalizedStrings_IDWriteLocalizedStrings : public SkTypeface::LocalizedStrings {
+public:
+    /** Takes ownership of the IDWriteLocalizedStrings. */
+    explicit LocalizedStrings_IDWriteLocalizedStrings(IDWriteLocalizedStrings* strings)
+        : fIndex(0), fStrings(strings)
+    { }
+
+    virtual bool next(SkTypeface::LocalizedString* localizedString) SK_OVERRIDE {
+        if (fIndex >= fStrings->GetCount()) {
+            return false;
+        }
+
+        // String
+        UINT32 stringLength;
+        HRBM(fStrings->GetStringLength(fIndex, &stringLength), "Could not get string length.");
+        stringLength += 1;
+
+        SkSMallocWCHAR wString(stringLength);
+        HRBM(fStrings->GetString(fIndex, wString.get(), stringLength), "Could not get string.");
+
+        HRB(wchar_to_skstring(wString.get(), &localizedString->fString));
+
+        // Locale
+        UINT32 localeLength;
+        HRBM(fStrings->GetLocaleNameLength(fIndex, &localeLength), "Could not get locale length.");
+        localeLength += 1;
+
+        SkSMallocWCHAR wLocale(localeLength);
+        HRBM(fStrings->GetLocaleName(fIndex, wLocale.get(), localeLength), "Could not get locale.");
+
+        HRB(wchar_to_skstring(wLocale.get(), &localizedString->fLanguage));
+
+        ++fIndex;
+        return true;
+    }
+
+private:
+    UINT32 fIndex;
+    SkTScopedComPtr<IDWriteLocalizedStrings> fStrings;
+};
+
+SkTypeface::LocalizedStrings* DWriteFontTypeface::onGetFamilyNames() const {
+    SkTScopedComPtr<IDWriteLocalizedStrings> familyNames;
+    HRNM(fDWriteFontFamily->GetFamilyNames(&familyNames), "Could not obtain family names.");
+
+    return new LocalizedStrings_IDWriteLocalizedStrings(familyNames.release());
+}
+
 int DWriteFontTypeface::onGetTableTags(SkFontTableTag tags[]) const {
     DWRITE_FONT_FACE_TYPE type = fDWriteFontFace->GetType();
     if (type != DWRITE_FONT_FACE_TYPE_CFF &&
@@ -1656,6 +1705,7 @@ public:
         DWRITE_FONT_STRETCH width = (DWRITE_FONT_STRETCH)pattern.width();
 
         SkTScopedComPtr<IDWriteFont> font;
+        // TODO: perhaps use GetMatchingFonts and get the least simulated?
         HRNM(fFontFamily->GetFirstMatchingFont(weight, width, slant, &font),
              "Could not match font in family.");
 
index e044075..b536c0a 100644 (file)
@@ -451,6 +451,8 @@ bool SkOTTableName::Iterator::next(SkOTTableName::Iterator::Record& record) {
         ++fIndex;
     } while (fType != -1 && nameRecord->nameID.fontSpecific != fType);
 
+    record.type = nameRecord->nameID.fontSpecific;
+
     const uint16_t stringTableOffset = SkEndian_SwapBE16(fName.stringOffset);
     const char* stringTable = SkTAddOffset<const char>(&fName, stringTableOffset);
 
@@ -460,20 +462,29 @@ bool SkOTTableName::Iterator::next(SkOTTableName::Iterator::Record& record) {
     const char* nameString = SkTAddOffset<const char>(stringTable, nameOffset);
     switch (nameRecord->platformID.value) {
         case SkOTTableName::Record::PlatformID::Windows:
-            SkASSERT(SkOTTableName::Record::EncodingID::Windows::UnicodeBMPUCS2
-                  == nameRecord->encodingID.windows.value
-                  || SkOTTableName::Record::EncodingID::Windows::UnicodeUCS4
-                  == nameRecord->encodingID.windows.value
-                  || SkOTTableName::Record::EncodingID::Windows::Symbol
-                  == nameRecord->encodingID.windows.value);
+            if (SkOTTableName::Record::EncodingID::Windows::UnicodeBMPUCS2
+                   != nameRecord->encodingID.windows.value
+                && SkOTTableName::Record::EncodingID::Windows::UnicodeUCS4
+                   != nameRecord->encodingID.windows.value
+                && SkOTTableName::Record::EncodingID::Windows::Symbol
+                   != nameRecord->encodingID.windows.value)
+            {
+                record.name.reset();
+                break;
+            }
         case SkOTTableName::Record::PlatformID::Unicode:
         case SkOTTableName::Record::PlatformID::ISO:
             SkStringFromUTF16BE((const uint16_t*)nameString, nameLength, record.name);
             break;
 
         case SkOTTableName::Record::PlatformID::Macintosh:
-            SkASSERT(SkOTTableName::Record::EncodingID::Macintosh::Roman
-                  == nameRecord->encodingID.macintosh.value);
+            // TODO: need better decoding, especially on Mac.
+            if (SkOTTableName::Record::EncodingID::Macintosh::Roman
+                != nameRecord->encodingID.macintosh.value)
+            {
+                record.name.reset();
+                break;
+            }
             SkStringFromMacRoman((const uint8_t*)nameString, nameLength, record.name);
             break;
 
index 8dde1a4..f3dae40 100644 (file)
@@ -554,7 +554,13 @@ struct SkOTTableName {
             : fName(name), fIndex(0), fType(type)
         { }
 
+        void reset(SkOTTableName::Record::NameID::Predefined::Value type) {
+            fIndex = 0;
+            fType = type;
+        }
+
         struct Record {
+            SK_OT_USHORT type;
             SkString name;
             SkString language;
         };
index c7716ff..004a888 100644 (file)
@@ -159,3 +159,45 @@ SkData* SkOTUtils::RenameFont(SkStream* fontData, const char* fontName, int font
 
     return rewrittenFontData.detach();
 }
+
+
+SkOTUtils::LocalizedStrings_NameTable*
+SkOTUtils::LocalizedStrings_NameTable::CreateForFamilyNames(const SkTypeface& typeface) {
+    static const SkFontTableTag nameTag = SkSetFourByteTag('n','a','m','e');
+    size_t nameTableSize = typeface.getTableSize(nameTag);
+    if (0 == nameTableSize) {
+        return NULL;
+    }
+    SkAutoTDeleteArray<uint8_t> nameTableData(new uint8_t[nameTableSize]);
+    size_t copied = typeface.getTableData(nameTag, 0, nameTableSize, nameTableData.get());
+    if (copied != nameTableSize) {
+        return NULL;
+    }
+
+    return new SkOTUtils::LocalizedStrings_NameTable((SkOTTableName*)nameTableData.detach(),
+        SkOTUtils::LocalizedStrings_NameTable::familyNameTypes,
+        SK_ARRAY_COUNT(SkOTUtils::LocalizedStrings_NameTable::familyNameTypes));
+}
+
+bool SkOTUtils::LocalizedStrings_NameTable::next(SkTypeface::LocalizedString* localizedString) {
+    do {
+        SkOTTableName::Iterator::Record record;
+        if (fFamilyNameIter.next(record)) {
+            localizedString->fString = record.name;
+            localizedString->fLanguage = record.language;
+            return true;
+        }
+        if (fTypesCount == fTypesIndex + 1) {
+            return false;
+        }
+        ++fTypesIndex;
+        fFamilyNameIter.reset(fTypes[fTypesIndex]);
+    } while (true);
+}
+
+SkOTTableName::Record::NameID::Predefined::Value
+SkOTUtils::LocalizedStrings_NameTable::familyNameTypes[3] = {
+    SkOTTableName::Record::NameID::Predefined::FontFamilyName,
+    SkOTTableName::Record::NameID::Predefined::PreferredFamily,
+    SkOTTableName::Record::NameID::Predefined::WWSFamilyName,
+};
index 3c5ada2..4825fbe 100644 (file)
@@ -9,6 +9,9 @@
 #define SkOTUtils_DEFINED
 
 #include "SkOTTableTypes.h"
+#include "SkOTTable_name.h"
+#include "SkTypeface.h"
+
 class SkData;
 class SkStream;
 
@@ -32,6 +35,55 @@ struct SkOTUtils {
       *  fontName and fontNameLen must be specified in terms of ASCII chars.
       */
     static SkData* RenameFont(SkStream* fontData, const char* fontName, int fontNameLen);
+
+    /** An implementation of LocalizedStrings which obtains it's data from a 'name' table. */
+    class LocalizedStrings_NameTable : public SkTypeface::LocalizedStrings {
+    public:
+        /** Takes ownership of the nameTableData and will free it with SK_DELETE. */
+        LocalizedStrings_NameTable(SkOTTableName* nameTableData,
+                                   SkOTTableName::Record::NameID::Predefined::Value types[],
+                                   int typesCount)
+            : fTypes(types), fTypesCount(typesCount), fTypesIndex(0)
+            , fNameTableData(nameTableData), fFamilyNameIter(*nameTableData, fTypes[fTypesIndex])
+        { }
+
+        /** Creates an iterator over all the family names in the 'name' table of a typeface.
+         *  If no valid 'name' table can be found, returns NULL.
+         */
+        static LocalizedStrings_NameTable* CreateForFamilyNames(const SkTypeface& typeface);
+
+        virtual bool next(SkTypeface::LocalizedString* localizedString) SK_OVERRIDE;
+    private:
+        static SkOTTableName::Record::NameID::Predefined::Value familyNameTypes[3];
+
+        SkOTTableName::Record::NameID::Predefined::Value* fTypes;
+        int fTypesCount;
+        int fTypesIndex;
+        SkAutoTDeleteArray<SkOTTableName> fNameTableData;
+        SkOTTableName::Iterator fFamilyNameIter;
+    };
+
+    /** An implementation of LocalizedStrings which has one name. */
+    class LocalizedStrings_SingleName : public SkTypeface::LocalizedStrings {
+    public:
+        LocalizedStrings_SingleName(SkString name, SkString language)
+            : fName(name), fLanguage(language), fHasNext(true)
+        { }
+
+        virtual bool next(SkTypeface::LocalizedString* localizedString) SK_OVERRIDE {
+            localizedString->fString = fName;
+            localizedString->fLanguage = fLanguage;
+
+            bool hadNext = fHasNext;
+            fHasNext = false;
+            return hadNext;
+        }
+
+    private:
+        SkString fName;
+        SkString fLanguage;
+        bool fHasNext;
+    };
 };
 
 #endif
index 3845c3b..18ba356 100644 (file)
@@ -7,6 +7,8 @@
 
 #include "Test.h"
 
+#include "SkCommandLineFlags.h"
+#include "SkFontMgr.h"
 #include "SkOTTable_name.h"
 #include "SkTypeface.h"
 
@@ -108,40 +110,7 @@ struct FontNamesTest {
     },
 };
 
-static void TestFontNames(skiatest::Reporter* reporter) {
-    static const char* interestingFont[] = {
-        "Arial",
-        "Times New Roman",
-        "MS PGothic", // Has Japanese name.
-        "Wingdings", // Uses 'Symbol' name encoding.
-    };
-    static const SkFontTableTag nameTag = SkSetFourByteTag('n','a','m','e');
-
-    for (size_t i = 0; i < SK_ARRAY_COUNT(interestingFont); ++i) {
-        SkAutoTUnref<SkTypeface> typeface(SkTypeface::CreateFromName(interestingFont[i],
-                                          SkTypeface::kNormal));
-        if (NULL == typeface.get()) {
-            continue;
-        }
-        size_t nameTableSize = typeface->getTableSize(nameTag);
-        if (0 == nameTableSize) {
-            continue;
-        }
-        uint8_t* nameTableData = new uint8_t[nameTableSize];
-        SkAutoTDeleteArray<uint8_t> ada(nameTableData);
-        size_t copied = typeface->getTableData(nameTag, 0, nameTableSize, nameTableData);
-        if (copied != nameTableSize) {
-            continue;
-        }
-
-        SkOTTableName::Iterator iter(*((SkOTTableName*)nameTableData),
-                                     SkOTTableName::Record::NameID::Predefined::FontFamilyName);
-        SkOTTableName::Iterator::Record record;
-        while (iter.next(record)) {
-            //printf("%s <%s>\n", record.name.c_str(), record.language.c_str());
-        }
-    }
-
+static void test_synthetic(skiatest::Reporter* reporter, bool verbose) {
     for (size_t i = 0; i < SK_ARRAY_COUNT(test); ++i) {
         SkOTTableName::Iterator iter(*test[i].data, test[i].nameID.predefined.value);
         SkOTTableName::Iterator::Record record;
@@ -170,6 +139,84 @@ static void TestFontNames(skiatest::Reporter* reporter) {
     }
 }
 
+#define MAX_FAMILIES 1000
+static void test_systemfonts(skiatest::Reporter* reporter, bool verbose) {
+    static const SkFontTableTag nameTag = SkSetFourByteTag('n','a','m','e');
+
+    SkAutoTUnref<SkFontMgr> fm(SkFontMgr::RefDefault());
+    int count = SkMin32(fm->countFamilies(), MAX_FAMILIES);
+    for (int i = 0; i < count; ++i) {
+        SkAutoTUnref<SkFontStyleSet> set(fm->createStyleSet(i));
+        for (int j = 0; j < set->count(); ++j) {
+            SkString sname;
+            SkFontStyle fs;
+            set->getStyle(j, &fs, &sname);
+
+            SkAutoTUnref<SkTypeface> typeface(set->createTypeface(j));
+
+            SkString familyName;
+            typeface->getFamilyName(&familyName);
+            if (verbose) {
+                printf("[%s]\n", familyName.c_str());
+            }
+
+            SkAutoTDelete<SkTypeface::LocalizedStrings> familyNamesIter(typeface->getFamilyNames());
+            SkTypeface::LocalizedString familyNameLocalized;
+            while (familyNamesIter->next(&familyNameLocalized)) {
+                if (verbose) {
+                    printf("(%s) <%s>\n", familyNameLocalized.fString.c_str(),
+                                          familyNameLocalized.fLanguage.c_str());
+                }
+            }
+
+            size_t nameTableSize = typeface->getTableSize(nameTag);
+            if (0 == nameTableSize) {
+                continue;
+            }
+            SkAutoTMalloc<uint8_t> nameTableData(nameTableSize);
+            size_t copied = typeface->getTableData(nameTag, 0, nameTableSize, nameTableData.get());
+            if (copied != nameTableSize) {
+                continue;
+            }
+
+            SkOTTableName::Iterator::Record record;
+            SkOTTableName::Iterator familyNameIter(*((SkOTTableName*)nameTableData.get()),
+                SkOTTableName::Record::NameID::Predefined::FontFamilyName);
+            while (familyNameIter.next(record)) {
+                REPORTER_ASSERT_MESSAGE(reporter,
+                    SkOTTableName::Record::NameID::Predefined::FontFamilyName == record.type,
+                    "Requested family name, got something else."
+                );
+                if (verbose) {
+                    printf("{%s} <%s>\n", record.name.c_str(), record.language.c_str());
+                }
+            }
+
+            SkOTTableName::Iterator styleNameIter(*((SkOTTableName*)nameTableData.get()),
+                SkOTTableName::Record::NameID::Predefined::FontSubfamilyName);
+            while (styleNameIter.next(record)) {
+                REPORTER_ASSERT_MESSAGE(reporter,
+                    SkOTTableName::Record::NameID::Predefined::FontSubfamilyName == record.type,
+                    "Requested subfamily name, got something else."
+                );
+                if (verbose) {
+                    printf("{{%s}} <%s>\n", record.name.c_str(), record.language.c_str());
+                }
+            }
+
+            if (verbose) {
+                printf("\n");
+            }
+        }
+    }
+}
+
+DEFINE_bool(verboseFontNames, false, "verbose FontNames test.");
+
+static void TestFontNames(skiatest::Reporter* reporter) {
+    test_synthetic(reporter, FLAGS_verboseFontNames);
+    test_systemfonts(reporter, FLAGS_verboseFontNames);
+}
 
 #include "TestClassDef.h"
 DEFINE_TESTCLASS("FontNames", FontNamesTestClass, TestFontNames)