Provide windows font host implementation needed to support TrueType text in pdf backend.
- Move AdvanceMetric template functions into new file SkAdvancedTypefaceMetrics.cpp

Review URL: http://codereview.appspot.com/4174041

git-svn-id: http://skia.googlecode.com/svn/trunk@789 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/core/SkAdvancedTypefaceMetrics.cpp b/src/core/SkAdvancedTypefaceMetrics.cpp
new file mode 100755
index 0000000..1b86fb5
--- /dev/null
+++ b/src/core/SkAdvancedTypefaceMetrics.cpp
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkAdvancedTypefaceMetrics.h"
+#include "SkTypes.h"
+
+#ifdef SK_BUILD_FOR_UNIX
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#endif
+
+namespace skia_advanced_typeface_metrics_utils {
+
+template <typename Data>
+void resetRange(SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* range,
+                int startId) {
+    range->fStartId = startId;
+    range->fAdvance.setCount(0);
+}
+
+template <typename Data>
+SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* appendRange(
+        SkTScopedPtr<SkAdvancedTypefaceMetrics::AdvanceMetric<Data> >* nextSlot,
+        int startId) {
+    nextSlot->reset(new SkAdvancedTypefaceMetrics::AdvanceMetric<Data>);
+    resetRange(nextSlot->get(), startId);
+    return nextSlot->get();
+}
+
+template <typename Data>
+void finishRange(
+        SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* range,
+        int endId,
+        typename SkAdvancedTypefaceMetrics::AdvanceMetric<Data>::MetricType
+                type) {
+    range->fEndId = endId;
+    range->fType = type;
+    int newLength;
+    if (type == SkAdvancedTypefaceMetrics::AdvanceMetric<Data>::kRange) {
+        newLength = endId - range->fStartId + 1;
+    } else {
+        newLength = 1;
+    }
+    SkASSERT(range->fAdvance.count() >= newLength);
+    range->fAdvance.setCount(newLength);
+}
+
+template <typename Data, typename FontHandle>
+SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* getAdvanceData(
+        FontHandle fontHandle,
+        int num_glyphs,
+        bool (*getAdvance)(FontHandle fontHandle, int gId, Data* data)) {
+    // Assuming that an ASCII representation of a width or a glyph id is,
+    // on average, 3 characters long gives the following cut offs for
+    // using different range types:
+    // When currently in a range
+    //  - Removing 4 0's is a win
+    //  - Removing 5 repeats is a win
+    // When not currently in a range
+    //  - Removing 1 0 is a win
+    //  - Removing 3 repeats is a win
+
+    SkTScopedPtr<SkAdvancedTypefaceMetrics::AdvanceMetric<Data> > result;
+    SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* curRange;
+    curRange = appendRange(&result, 0);
+    Data lastAdvance = SK_MinS16;
+    int repeats = 0;
+    for (int gId = 0; gId < num_glyphs; gId++) {
+        Data advance;
+        if (!getAdvance(fontHandle, gId, &advance)) {
+            num_glyphs = (gId > 0) ? gId - 1 : 0;
+            break;
+        }
+        if (advance == lastAdvance) {
+            repeats++;
+        } else if (curRange->fAdvance.count() == repeats + 1) {
+            if (lastAdvance == 0 && repeats >= 0) {
+                resetRange(curRange, gId);
+            } else if (repeats >= 2) {
+                finishRange(curRange, gId - 1,
+                            SkAdvancedTypefaceMetrics::WidthRange::kRun);
+                curRange = appendRange(&curRange->fNext, gId);
+            }
+            repeats = 0;
+        } else {
+            if (lastAdvance == 0 && repeats >= 3) {
+                finishRange(curRange, gId - repeats - 2,
+                            SkAdvancedTypefaceMetrics::WidthRange::kRange);
+                curRange = appendRange(&curRange->fNext, gId);
+            } else if (repeats >= 4) {
+                finishRange(curRange, gId - repeats - 2,
+                            SkAdvancedTypefaceMetrics::WidthRange::kRange);
+                curRange = appendRange(&curRange->fNext, gId - repeats - 1);
+                curRange->fAdvance.append(1, &lastAdvance);
+                finishRange(curRange, gId - 1,
+                            SkAdvancedTypefaceMetrics::WidthRange::kRun);
+                curRange = appendRange(&curRange->fNext, gId);
+            }
+            repeats = 0;
+        }
+        curRange->fAdvance.append(1, &advance);
+        lastAdvance = advance;
+    }
+    finishRange(curRange, num_glyphs - 1,
+                SkAdvancedTypefaceMetrics::WidthRange::kRange);
+    return result.release();
+}
+
+// Make AdvanceMetric template functions available for linking with typename
+// WidthRange and VerticalAdvanceRange.
+#ifdef SK_BUILD_FOR_WIN
+template SkAdvancedTypefaceMetrics::WidthRange* getAdvanceData(
+        HDC hdc,
+        int num_glyphs,
+        bool (*getAdvance)(HDC hdc, int gId, int16_t* data));
+#elif SK_BUILD_FOR_UNIX
+template SkAdvancedTypefaceMetrics::WidthRange* getAdvanceData(
+        FT_Face face,
+        int num_glyphs,
+        bool (*getAdvance)(FT_Face face, int gId, int16_t* data));
+#endif
+template void resetRange(
+        SkAdvancedTypefaceMetrics::WidthRange* range,
+        int startId);
+template SkAdvancedTypefaceMetrics::WidthRange* appendRange(
+        SkTScopedPtr<SkAdvancedTypefaceMetrics::WidthRange >* nextSlot,
+        int startId);
+template void finishRange<int16_t>(
+        SkAdvancedTypefaceMetrics::WidthRange* range,
+        int endId,
+        SkAdvancedTypefaceMetrics::WidthRange::MetricType type);
+
+template void resetRange(
+        SkAdvancedTypefaceMetrics::VerticalAdvanceRange* range,
+        int startId);
+template SkAdvancedTypefaceMetrics::VerticalAdvanceRange* appendRange(
+        SkTScopedPtr<SkAdvancedTypefaceMetrics::VerticalAdvanceRange >*
+            nextSlot,
+        int startId);
+template void finishRange<SkAdvancedTypefaceMetrics::VerticalMetric>(
+        SkAdvancedTypefaceMetrics::VerticalAdvanceRange* range,
+        int endId,
+        SkAdvancedTypefaceMetrics::VerticalAdvanceRange::MetricType type);
+
+} // namespace skia_advanced_typeface_metrics_utils
diff --git a/src/core/core_files.mk b/src/core/core_files.mk
index 3e3436e..399b1a5 100644
--- a/src/core/core_files.mk
+++ b/src/core/core_files.mk
@@ -1,5 +1,6 @@
 SOURCE := \
     Sk64.cpp \
+    SkAdvancedTypefaceMetrics.cpp \
     SkAlphaRuns.cpp \
     SkBitmap.cpp \
     SkBitmapProcShader.cpp \
diff --git a/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.cpp
index 1b1d47b..7f1e377 100644
--- a/src/ports/SkFontHost_FreeType.cpp
+++ b/src/ports/SkFontHost_FreeType.cpp
@@ -73,6 +73,8 @@
     #define SkASSERT_CONTINUE(pred)
 #endif
 
+using namespace skia_advanced_typeface_metrics_utils;
+
 //////////////////////////////////////////////////////////////////////////
 
 struct SkFaceRec;
@@ -331,98 +333,14 @@
     return true;
 }
 
-static int16_t getWidthAdvance(FT_Face face, int gId) {
+static bool getWidthAdvance(FT_Face face, int gId, int16_t* data) {
     FT_Fixed advance = 0;
-    SkAssertResult(getAdvances(face, gId, 1, FT_LOAD_NO_SCALE, &advance) == 0);
-    return advance;
-}
-
-template <typename Data>
-static void resetRange(SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* range,
-                       int startId) {
-    range->fStartId = startId;
-    range->fAdvance.setCount(0);
-}
-
-template <typename Data>
-static SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* appendRange(
-        SkTScopedPtr<SkAdvancedTypefaceMetrics::AdvanceMetric<Data> >* nextSlot,
-        int startId) {
-    nextSlot->reset(new SkAdvancedTypefaceMetrics::AdvanceMetric<Data>);
-    resetRange(nextSlot->get(), startId);
-    return nextSlot->get();
-}
-
-template <typename Data>
-static void finishRange(
-        SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* range,
-        int endId,
-        typename SkAdvancedTypefaceMetrics::AdvanceMetric<Data>::MetricType
-                type) {
-    range->fEndId = endId;
-    range->fType = type;
-    int newLength;
-    if (type == SkAdvancedTypefaceMetrics::AdvanceMetric<Data>::kRange)
-        newLength = endId - range->fStartId + 1;
-    else
-        newLength = 1;
-    SkASSERT(range->fAdvance.count() >= newLength);
-    range->fAdvance.setCount(newLength);
-}
-
-template <typename Data>
-static SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* getAdvanceData(
-        FT_Face face, Data (*getAdvance)(FT_Face face, int gId)) {
-    // Assuming that an ASCII representation of a width or a glyph id is,
-    // on average, 3 characters long gives the following cut offs for
-    // using different range types:
-    // When currently in a range
-    //  - Removing 4 0's is a win
-    //  - Removing 5 repeats is a win
-    // When not currently in a range
-    //  - Removing 1 0 is a win
-    //  - Removing 3 repeats is a win
-
-    SkTScopedPtr<SkAdvancedTypefaceMetrics::AdvanceMetric<Data> > result;
-    SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* curRange;
-    curRange = appendRange(&result, 0);
-    Data lastAdvance = SHRT_MIN;
-    int repeats = 0;
-    for (int gId = 0; gId < face->num_glyphs; gId++) {
-        Data advance = getAdvance(face, gId);
-        if (advance == lastAdvance) {
-            repeats++;
-        } else if (curRange->fAdvance.count() == repeats + 1) {
-            if (lastAdvance == 0 && repeats >= 0) {
-                resetRange(curRange, gId);
-            } else if (repeats >= 2) {
-                finishRange(curRange, gId - 1,
-                            SkAdvancedTypefaceMetrics::WidthRange::kRun);
-                curRange = appendRange(&curRange->fNext, gId);
-            }
-            repeats = 0;
-        } else {
-            if (lastAdvance == 0 && repeats >= 3) {
-                finishRange(curRange, gId - repeats - 2,
-                            SkAdvancedTypefaceMetrics::WidthRange::kRange);
-                curRange = appendRange(&curRange->fNext, gId);
-            } else if (repeats >= 4) {
-                finishRange(curRange, gId - repeats - 2,
-                            SkAdvancedTypefaceMetrics::WidthRange::kRange);
-                curRange = appendRange(&curRange->fNext, gId - repeats - 1);
-                curRange->fAdvance.append(1, &lastAdvance);
-                finishRange(curRange, gId - 1,
-                            SkAdvancedTypefaceMetrics::WidthRange::kRun);
-                curRange = appendRange(&curRange->fNext, gId);
-            }
-            repeats = 0;
-        }
-        curRange->fAdvance.append(1, &advance);
-        lastAdvance = advance;
+    if (getAdvances(face, gId, 1, FT_LOAD_NO_SCALE, &advance)) {
+        return false;
     }
-    finishRange(curRange, face->num_glyphs - 1,
-                SkAdvancedTypefaceMetrics::WidthRange::kRange);
-    return result.release();
+    SkASSERT(data);
+    *data = advance;
+    return true;
 }
 
 // static
@@ -499,6 +417,7 @@
     // Figure out a good guess for StemV - Min width of i, I, !, 1.
     // This probably isn't very good with an italic font.
     int16_t min_width = SHRT_MAX;
+    info->fStemV = 0;
     char stem_chars[] = {'i', 'I', '!', '1'};
     for (size_t i = 0; i < SK_ARRAY_COUNT(stem_chars); i++) {
         FT_BBox bbox;
@@ -550,7 +469,7 @@
             info->fGlyphWidths->fAdvance.append(1, &advance);
             finishRange(info->fGlyphWidths.get(), 0,
                         SkAdvancedTypefaceMetrics::WidthRange::kDefault);
-        } else if(!cid) {
+        } else if (!cid) {
             appendRange(&info->fGlyphWidths, 0);
             // So as to not blow out the stack, get advances in batches.
             for (int gID = 0; gID < face->num_glyphs; gID += 128) {
@@ -568,7 +487,8 @@
             finishRange(info->fGlyphWidths.get(), face->num_glyphs - 1,
                         SkAdvancedTypefaceMetrics::WidthRange::kRange);
         } else {
-            info->fGlyphWidths.reset(getAdvanceData(face, &getWidthAdvance));
+            info->fGlyphWidths.reset(
+                getAdvanceData(face, face->num_glyphs, &getWidthAdvance));
         }
 
         if (info->fType == SkAdvancedTypefaceMetrics::kType1_Font) {
diff --git a/src/ports/SkFontHost_win.cpp b/src/ports/SkFontHost_win.cpp
old mode 100644
new mode 100755
index dacabac..c2e43fa
--- a/src/ports/SkFontHost_win.cpp
+++ b/src/ports/SkFontHost_win.cpp
@@ -19,6 +19,8 @@
 

 #include "SkFontHost.h"

 #include "SkDescriptor.h"

+#include "SkAdvancedTypefaceMetrics.h"

+#include "SkStream.h"

 #include "SkThread.h"

 

 #ifdef WIN32

@@ -28,6 +30,8 @@
 // client3d has to undefine this for now

 #define CAN_USE_LOGFONT_NAME

 

+using namespace skia_advanced_typeface_metrics_utils;

+

 static SkMutex gFTMutex;

 

 static const uint16_t BUFFERSIZE = (16384 - 32);

@@ -456,11 +460,133 @@
     return NULL;

 }

 

+static bool getWidthAdvance(HDC hdc, int gId, int16_t* advance) {

+    // Initialize the MAT2 structure to the identify transformation matrix.

+    static const MAT2 mat2 = {SkScalarToFIXED(1), SkScalarToFIXED(0),

+                        SkScalarToFIXED(0), SkScalarToFIXED(1)};

+    int flags = GGO_METRICS | GGO_GLYPH_INDEX;

+    GLYPHMETRICS gm;

+    if (GDI_ERROR == GetGlyphOutline(hdc, gId, flags, &gm, 0, NULL, &mat2)) {

+        return false;

+    }

+    SkASSERT(advance);

+    *advance = gm.gmCellIncX;

+    return true;

+}

+

 // static

 SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics(

         uint32_t fontID, bool perGlyphInfo) {

-    SkASSERT(!"SkFontHost::GetAdvancedTypefaceMetrics unimplemented");

-    return NULL;

+    SkAutoMutexAcquire ac(gFTMutex);

+    LogFontTypeface* rec = LogFontTypeface::FindById(fontID);

+    LOGFONT lf = rec->logFont();

+    SkAdvancedTypefaceMetrics* info = NULL;

+

+    HDC hdc = CreateCompatibleDC(NULL);

+    HFONT font = CreateFontIndirect(&lf);

+    HFONT savefont = (HFONT)SelectObject(hdc, font);

+    HFONT designFont = NULL;

+

+    // To request design units, create a logical font whose height is specified

+    // as unitsPerEm.

+    OUTLINETEXTMETRIC otm;

+    if (!GetOutlineTextMetrics(hdc, sizeof(otm), &otm) ||

+        !GetTextFace(hdc, LF_FACESIZE, lf.lfFaceName)) {

+        goto Error;

+    }

+    lf.lfHeight = -SkToS32(otm.otmEMSquare);

+    designFont = CreateFontIndirect(&lf);

+    SelectObject(hdc, designFont);

+    if (!GetOutlineTextMetrics(hdc, sizeof(otm), &otm)) {

+        goto Error;

+    }

+

+    info = new SkAdvancedTypefaceMetrics;

+#ifdef UNICODE

+    // Get the buffer size needed first.

+    size_t str_len = WideCharToMultiByte(CP_UTF8, 0, lf.lfFaceName, -1, NULL,

+                                         0, NULL, NULL);

+    // Allocate a buffer (str_len already has terminating null accounted for).

+    char *familyName = new char[str_len];

+    // Now actually convert the string.

+    WideCharToMultiByte(CP_UTF8, 0, lf.lfFaceName, -1, familyName, str_len,

+                          NULL, NULL);

+    info->fFontName.set(familyName);

+    delete [] familyName;

+#else

+    info->fFontName.set(lf.lfFaceName);

+#endif

+

+    if (otm.otmTextMetrics.tmPitchAndFamily & TMPF_TRUETYPE) {

+        info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;

+    } else {

+        info->fType = SkAdvancedTypefaceMetrics::kOther_Font;

+    }

+    info->fEmSize = otm.otmEMSquare;

+    info->fMultiMaster = false;

+

+    info->fStyle = 0;

+    // If this bit is clear the font is a fixed pitch font.

+    if (!(otm.otmTextMetrics.tmPitchAndFamily & TMPF_FIXED_PITCH)) {

+        info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;

+    }

+    if (otm.otmTextMetrics.tmItalic) {

+        info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;

+    }

+    // Setting symbolic style by default for now.

+    info->fStyle |= SkAdvancedTypefaceMetrics::kSymbolic_Style;

+    if (otm.otmTextMetrics.tmPitchAndFamily & FF_ROMAN) {

+        info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;

+    } else if (otm.otmTextMetrics.tmPitchAndFamily & FF_SCRIPT) {

+            info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;

+    }

+

+    // The main italic angle of the font, in tenths of a degree counterclockwise

+    // from vertical.

+    info->fItalicAngle = otm.otmItalicAngle / 10;

+    info->fAscent = SkToS16(otm.otmTextMetrics.tmAscent);

+    info->fDescent = SkToS16(-otm.otmTextMetrics.tmDescent);

+    // TODO(ctguil): Use alternate cap height calculation.

+    // MSDN says otmsCapEmHeight is not support but it is returning a value on

+    // my Win7 box.

+    info->fCapHeight = otm.otmsCapEmHeight;

+    info->fBBox =

+        SkIRect::MakeLTRB(otm.otmrcFontBox.left, otm.otmrcFontBox.top,

+                          otm.otmrcFontBox.right, otm.otmrcFontBox.bottom);

+

+    // Figure out a good guess for StemV - Min width of i, I, !, 1.

+    // This probably isn't very good with an italic font.

+    int16_t min_width = SHRT_MAX;

+    info->fStemV = 0;

+    char stem_chars[] = {'i', 'I', '!', '1'};

+    for (size_t i = 0; i < SK_ARRAY_COUNT(stem_chars); i++) {

+        ABC abcWidths;

+        if (GetCharABCWidths(hdc, stem_chars[i], stem_chars[i], &abcWidths)) {

+            int16_t width = abcWidths.abcB;

+            if (width > 0 && width < min_width) {

+                min_width = width;

+                info->fStemV = min_width;

+            }

+        }

+    }

+

+    // If bit 1 is set, the font may not be embedded in a document.

+    // If bit 1 is clear, the font can be embedded.

+    // If bit 2 is set, the embedding is read-only.

+    if (otm.otmfsType & 0x1) {

+        info->fType = SkAdvancedTypefaceMetrics::kNotEmbeddable_Font;

+    } else if (perGlyphInfo) {

+        info->fGlyphWidths.reset(

+            getAdvanceData(hdc, SHRT_MAX, &getWidthAdvance));

+    }

+

+Error:

+    SelectObject(hdc, savefont);

+    DeleteObject(designFont);

+    DeleteObject(font);

+    DeleteDC(hdc);

+

+    return info;

 }

 

 SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {

@@ -471,8 +597,25 @@
 }

 

 SkStream* SkFontHost::OpenStream(SkFontID uniqueID) {

-    SkASSERT(!"SkFontHost::OpenStream unimplemented");

-    return NULL;

+    SkAutoMutexAcquire ac(gFTMutex);

+    LogFontTypeface* rec = LogFontTypeface::FindById(uniqueID);

+

+    HDC hdc = ::CreateCompatibleDC(NULL);

+    HFONT font = CreateFontIndirect(&rec->logFont());

+    HFONT savefont = (HFONT)SelectObject(hdc, font);

+

+    size_t bufferSize = GetFontData(hdc, 0, 0, NULL, 0);

+    SkMemoryStream* stream = new SkMemoryStream(bufferSize);

+    if (!GetFontData(hdc, 0, 0, (void*)stream->getMemoryBase(), bufferSize)) {

+        delete stream;

+        stream = NULL;

+    }

+

+    SelectObject(hdc, savefont);

+    DeleteObject(font);

+    DeleteDC(hdc);

+

+    return stream;

 }

 

 SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {