2009-06-30 Adam Langley <agl@google.com>
authoragl@chromium.org <agl@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 30 Jun 2009 16:57:01 +0000 (16:57 +0000)
committeragl@chromium.org <agl@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 30 Jun 2009 16:57:01 +0000 (16:57 +0000)
        Reviewed by Eric Seidel.

        Chromium: Add complex text support on Linux.

        https://bugs.webkit.org/show_bug.cgi?id=25068

        This patch adds complex text support on Linux using Harfbuzz. It's not
        the fastest code possible: some caching of font tables will certainly
        be required. However, it's probably the simplest code that works.

        This will require checking in new baselines in the Chromium tree for
        those layout tests which now pass.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@45379 268f45cc-cd09-0410-ab3c-d52691b4dbfc

WebCore/ChangeLog
WebCore/platform/graphics/chromium/FontLinux.cpp
WebCore/platform/graphics/chromium/HarfbuzzSkia.cpp [new file with mode: 0644]

index e8ba912d35b594481bdd170673b136fbfec29161..4ad433650201178a998cf9d56fdfd665e8be576a 100644 (file)
@@ -1,3 +1,59 @@
+2009-06-30  Adam Langley  <agl@google.com>
+
+        Reviewed by Eric Seidel.
+
+        Chromium: Add complex text support on Linux.
+
+        https://bugs.webkit.org/show_bug.cgi?id=25068
+
+        This patch adds complex text support on Linux using Harfbuzz. It's not
+        the fastest code possible: some caching of font tables will certainly
+        be required. However, it's probably the simplest code that works.
+
+        This will require checking in new baselines in the Chromium tree for
+        those layout tests which now pass.
+
+        * platform/graphics/chromium/FontLinux.cpp:
+        (WebCore::Font::drawGlyphs):
+        (WebCore::truncateFixedPointToInteger):
+        (WebCore::TextRunWalker::TextRunWalker):
+        (WebCore::TextRunWalker::~TextRunWalker):
+        (WebCore::TextRunWalker::reset):
+        (WebCore::TextRunWalker::setXOffsetToZero):
+        (WebCore::TextRunWalker::rtl):
+        (WebCore::TextRunWalker::setBackwardsIteration):
+        (WebCore::TextRunWalker::nextScriptRun):
+        (WebCore::TextRunWalker::glyphs):
+        (WebCore::TextRunWalker::length):
+        (WebCore::TextRunWalker::xPositions):
+        (WebCore::TextRunWalker::advances):
+        (WebCore::TextRunWalker::width):
+        (WebCore::TextRunWalker::logClusters):
+        (WebCore::TextRunWalker::numCodePoints):
+        (WebCore::TextRunWalker::widthOfFullRun):
+        (WebCore::TextRunWalker::allocHarfbuzzFont):
+        (WebCore::TextRunWalker::deleteGlyphArrays):
+        (WebCore::TextRunWalker::createGlyphArrays):
+        (WebCore::TextRunWalker::expandGlyphArrays):
+        (WebCore::TextRunWalker::shapeGlyphs):
+        (WebCore::TextRunWalker::setGlyphXPositions):
+        (WebCore::setupForTextPainting):
+        (WebCore::fontPlatformDataForTextRun):
+        (WebCore::Font::drawComplexText):
+        (WebCore::Font::floatWidthForComplexText):
+        (WebCore::glyphIndexForXPositionInScriptRun):
+        (WebCore::Font::offsetForPositionForComplexText):
+        (WebCore::Font::selectionRectForComplexText):
+        * platform/graphics/chromium/HarfbuzzSkia.cpp: Added.
+        (WebCore::SkiaScalarToHarfbuzzFixed):
+        (WebCore::stringToGlyphs):
+        (WebCore::glyphsToAdvances):
+        (WebCore::canRender):
+        (WebCore::getOutlinePoint):
+        (WebCore::getGlyphMetrics):
+        (WebCore::getFontMetric):
+        (WebCore::harfbuzzSkiaGetTable):
+
 2009-06-30  Eric Carlson  <eric.carlson@apple.com>
 
         Reviewed by Simon Fraser.
index a952685b7c891138f80cb744a072f2cc2dab65a3..e9c15417f17e0c24915151924d18b4282fca1ec4 100644 (file)
 #include "SkTypeface.h"
 #include "SkUtils.h"
 
+extern "C" {
+#include "harfbuzz-shaper.h"
+#include "harfbuzz-unicode.h"
+}
+
 namespace WebCore {
 
 bool Font::canReturnFallbackFontsForComplexText()
@@ -54,7 +59,7 @@ bool Font::canReturnFallbackFontsForComplexText()
 void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font,
                       const GlyphBuffer& glyphBuffer,  int from, int numGlyphs,
                       const FloatPoint& point) const {
-    SkASSERT(sizeof(GlyphBufferGlyph) == sizeof(uint16_t));  // compile-time assert
+    SkASSERT(sizeof(GlyphBufferGlyph) == sizeof(uint16_t)); // compile-time assert
 
     const GlyphBufferGlyph* glyphs = glyphBuffer.glyphs(from);
     SkScalar x = SkFloatToScalar(point.x());
@@ -110,31 +115,502 @@ void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font,
     }
 }
 
-void Font::drawComplexText(GraphicsContext* context, const TextRun& run,
+extern const HB_FontClass harfbuzzSkiaClass;
+extern HB_Error harfbuzzSkiaGetTable(void* voidface, const HB_Tag, HB_Byte* buffer, HB_UInt* len);
+
+// Harfbuzz uses 26.6 fixed point values for pixel offsets. However, we don't
+// handle subpixel positioning so this function is used to truncate Harfbuzz
+// values to a number of pixels.
+static int truncateFixedPointToInteger(HB_Fixed value)
+{
+    return value >> 6;
+}
+
+// TextRunWalker walks a TextRun and presents each script run in sequence. A
+// TextRun is a sequence of code-points with the same embedding level (i.e. they
+// are all left-to-right or right-to-left). A script run is a subsequence where
+// all the characters have the same script (e.g. Arabic, Thai etc). Shaping is
+// only ever done with script runs since the shapers only know how to deal with
+// a single script.
+//
+// After creating it, the script runs are either iterated backwards or forwards.
+// It defaults to backwards for RTL and forwards otherwise (which matches the
+// presentation order), however you can set it with |setBackwardsIteration|.
+//
+// Once you have setup the object, call |nextScriptRun| to get the first script
+// run. This will return false when the iteration is complete. At any time you
+// can call |reset| to start over again.
+class TextRunWalker {
+public:
+    TextRunWalker(const TextRun& run, SkPaint* paint, unsigned startingX, const FontPlatformData* font)
+        : m_run(run)
+        , m_startingX(startingX)
+        , m_offsetX(m_startingX)
+        , m_iterateBackwards(run.rtl())
+    {
+        memset(&m_item, 0, sizeof(m_item));
+        // We cannot know, ahead of time, how many glyphs a given script run
+        // will produce. We take a guess that script runs will not produce more
+        // than twice as many glyphs as there are code points and fallback if
+        // we find that we are wrong.
+        m_maxGlyphs = run.length() * 2;
+        createGlyphArrays();
+
+        m_item.log_clusters = new unsigned short[run.length()];
+
+        m_item.face = HB_NewFace(const_cast<FontPlatformData*>(font), harfbuzzSkiaGetTable);
+        m_item.font = allocHarfbuzzFont(const_cast<FontPlatformData*>(font));
+        // This sets up the SkPaint graphics context such that the text related
+        // input will always by assumed to be font specific glyph ids.
+        paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+
+        m_item.string = run.characters();
+        m_item.stringLength = run.length();
+        m_item.item.bidiLevel = run.rtl();
+
+        reset();
+    }
+
+    ~TextRunWalker()
+    {
+        fastFree(m_item.font);
+        deleteGlyphArrays();
+        delete[] m_item.log_clusters;
+        HB_FreeFace(m_item.face);
+    }
+
+    void reset()
+    {
+        if (m_iterateBackwards)
+            m_indexOfNextScriptRun = m_run.length() - 1;
+        else
+            m_indexOfNextScriptRun = 0;
+        m_offsetX = m_startingX;
+    }
+
+    // Set the x offset for the next script run. This affects the values in
+    // |xPositions|
+    void setXOffsetToZero()
+    {
+        m_offsetX = 0;
+    }
+
+    bool rtl() const
+    {
+        return m_run.rtl();
+    }
+
+    void setBackwardsIteration(bool isBackwards)
+    {
+        m_iterateBackwards = isBackwards;
+        reset();
+    }
+
+    // Advance to the next script run, returning false when the end of the
+    // TextRun has been reached.
+    bool nextScriptRun()
+    {
+        if (m_iterateBackwards) {
+            // In right-to-left mode we need to render the shaped glyph backwards and
+            // also render the script runs themselves backwards. So given a TextRun:
+            //    AAAAAAACTTTTTTT   (A = Arabic, C = Common, T = Thai)
+            // we render:
+            //    TTTTTTCAAAAAAA
+            // (and the glyphs in each A, C and T section are backwards too)
+            if (!hb_utf16_script_run_prev(&m_numCodePoints, &m_item.item, m_run.characters(), m_run.length(), &m_indexOfNextScriptRun))
+                return false;
+        } else {
+            if (!hb_utf16_script_run_next(&m_numCodePoints, &m_item.item, m_run.characters(), m_run.length(), &m_indexOfNextScriptRun))
+                return false;
+        }
+
+        if (!shapeGlyphs())
+            return false;
+        setGlyphXPositions(rtl());
+        return true;
+    }
+
+    const uint16_t* glyphs() const
+    {
+        return m_glyphs16;
+    }
+
+    // Return the length of the array returned by |glyphs|
+    const unsigned length() const
+    {
+        return m_item.num_glyphs;
+    }
+
+    // Return the x offset for each of the glyphs. Note that this is translated
+    // by the current x offset and that the x offset is updated for each script
+    // run.
+    const SkScalar* xPositions() const
+    {
+        return m_xPositions;
+    }
+
+    // Get the advances (widths) for each glyph.
+    const HB_Fixed* advances() const
+    {
+        return m_item.advances;
+    }
+
+    // Return the width (in px) of the current script run.
+    const unsigned width() const
+    {
+        return m_pixelWidth;
+    }
+
+    // Return the cluster log for the current script run. For example:
+    //   script run: f i a n c é  (fi gets ligatured)
+    //   log clutrs: 0 0 1 2 3 4
+    // So, for each input code point, the log tells you which output glyph was
+    // generated for it.
+    const unsigned short* logClusters() const
+    {
+        return m_item.log_clusters;
+    }
+
+    // return the number of code points in the current script run
+    const unsigned numCodePoints() const
+    {
+        return m_numCodePoints;
+    }
+
+
+    float widthOfFullRun()
+    {
+        float widthSum = 0;
+        while (nextScriptRun())
+            widthSum += width();
+
+        return widthSum;
+    }
+
+private:
+    HB_FontRec* allocHarfbuzzFont(void* userData)
+    {
+        HB_FontRec* font = reinterpret_cast<HB_FontRec*>(fastMalloc(sizeof(HB_FontRec)));
+        memset(font, 0, sizeof(HB_FontRec));
+        font->klass = &harfbuzzSkiaClass;
+        font->userData = userData;
+        // The values which harfbuzzSkiaClass returns are already scaled to
+        // pixel units, so we just set all these to one to disable further
+        // scaling.
+        font->x_ppem = 1;
+        font->y_ppem = 1;
+        font->x_scale = 1;
+        font->y_scale = 1;
+
+        return font;
+    }
+
+    void deleteGlyphArrays()
+    {
+        delete[] m_item.glyphs;
+        delete[] m_item.attributes;
+        delete[] m_item.advances;
+        delete[] m_item.offsets;
+        delete[] m_glyphs16;
+        delete[] m_xPositions;
+    }
+
+    bool createGlyphArrays()
+    {
+        m_item.glyphs = new HB_Glyph[m_maxGlyphs];
+        m_item.attributes = new HB_GlyphAttributes[m_maxGlyphs];
+        m_item.advances = new HB_Fixed[m_maxGlyphs];
+        m_item.offsets = new HB_FixedPoint[m_maxGlyphs];
+        m_glyphs16 = new uint16_t[m_maxGlyphs];
+        m_xPositions = new SkScalar[m_maxGlyphs];
+
+        return m_item.glyphs
+            && m_item.attributes
+            && m_item.advances
+            && m_item.offsets
+            && m_glyphs16
+            && m_xPositions;
+    }
+
+    bool expandGlyphArrays()
+    {
+        deleteGlyphArrays();
+        m_maxGlyphs <<= 1;
+        return createGlyphArrays();
+    }
+
+    bool shapeGlyphs()
+    {
+        for (;;) {
+            m_item.num_glyphs = m_maxGlyphs;
+            HB_ShapeItem(&m_item);
+            if (m_item.num_glyphs < m_maxGlyphs)
+                break;
+
+            // We overflowed our arrays. Resize and retry.
+            if (!expandGlyphArrays())
+                return false;
+        }
+
+        return true;
+    }
+
+    void setGlyphXPositions(bool isRTL)
+    {
+        m_pixelWidth = 0;
+        for (unsigned i = 0; i < m_item.num_glyphs; ++i) {
+            int index;
+            if (isRTL)
+                index = m_item.num_glyphs - (i + 1);
+            else
+                index = i;
+
+            m_glyphs16[i] = m_item.glyphs[i];
+            m_xPositions[index] = m_offsetX + m_pixelWidth;
+            m_pixelWidth += truncateFixedPointToInteger(m_item.advances[index]);
+        }
+        m_offsetX += m_pixelWidth;
+    }
+
+    const TextRun& m_run;
+    HB_ShaperItem m_item;
+    uint16_t* m_glyphs16; // A vector of 16-bit glyph ids.
+    SkScalar* m_xPositions; // A vector of x positions for each glyph.
+    ssize_t m_indexOfNextScriptRun; // Indexes the script run in |m_run|.
+    const unsigned m_startingX; // Offset in pixels of the first script run.
+    unsigned m_offsetX; // Offset in pixels to the start of the next script run.
+    unsigned m_pixelWidth; // Width (in px) of the current script run.
+    unsigned m_numCodePoints; // Code points in current script run.
+    unsigned m_maxGlyphs; // Current size of all the Harfbuzz arrays.
+    bool m_iterateBackwards;
+};
+
+static void setupForTextPainting(const FontPlatformData& font, SkPaint* paint, SkColor color)
+{
+    font.setupPaint(paint);
+    paint->setFlags(SkPaint::kAntiAlias_Flag);
+    paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+    paint->setColor(color);
+}
+
+static FontPlatformData fontPlatformDataForTextRun(const Font* font, const TextRun& run)
+{
+    return font->fontDataForCharacters(run.characters(), run.length())->fontDataForCharacter(' ')->platformData();
+}
+
+void Font::drawComplexText(GraphicsContext* gc, const TextRun& run,
                            const FloatPoint& point, int from, int to) const
 {
-    notImplemented();
+    if (!run.length())
+        return;
+
+    const FontPlatformData& font = fontPlatformDataForTextRun(this, run);
+
+    SkCanvas* canvas = gc->platformContext()->canvas();
+    int textMode = gc->platformContext()->getTextDrawingMode();
+    bool fill = textMode & cTextFill;
+    bool stroke = (textMode & cTextStroke)
+               && gc->platformContext()->getStrokeStyle() != NoStroke
+               && gc->platformContext()->getStrokeThickness() > 0;
+
+    if (!fill && !stroke)
+        return;
+
+    SkPaint strokePaint, fillPaint;
+    if (fill) {
+        gc->platformContext()->setupPaintForFilling(&fillPaint);
+        setupForTextPainting(font, &fillPaint, gc->fillColor().rgb());
+    }
+    if (stroke) {
+        gc->platformContext()->setupPaintForStroking(&strokePaint, 0, 0);
+        setupForTextPainting(font, &strokePaint, gc->strokeColor().rgb());
+    }
+
+    TextRunWalker walker(run, stroke ? &strokePaint : &fillPaint, point.x(), &font);
+
+    while (walker.nextScriptRun()) {
+        if (fill)
+            canvas->drawPosTextH(walker.glyphs(), walker.length() << 1, walker.xPositions(), point.y(), fillPaint);
+        if (stroke)
+            canvas->drawPosTextH(walker.glyphs(), walker.length() << 1, walker.xPositions(), point.y(), strokePaint);
+    }
 }
 
 float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>* /* fallbackFonts */) const
 {
-    notImplemented();
-    return 0;
+    SkPaint paint;
+    const FontPlatformData& font = fontPlatformDataForTextRun(this, run);
+    font.setupPaint(&paint);
+
+    TextRunWalker walker(run, &paint, 0, &font);
+    return walker.widthOfFullRun();
+}
+
+static int glyphIndexForXPositionInScriptRun(const TextRunWalker& walker, int x)
+{
+    const HB_Fixed* advances = walker.advances();
+    int glyphIndex;
+    if (walker.rtl()) {
+        for (glyphIndex = walker.length() - 1; glyphIndex >= 0; --glyphIndex) {
+            if (x < truncateFixedPointToInteger(advances[glyphIndex]))
+                break;
+            x -= truncateFixedPointToInteger(advances[glyphIndex]);
+        }
+    } else {
+        for (glyphIndex = 0; glyphIndex < walker.length(); ++glyphIndex) {
+            if (x < truncateFixedPointToInteger(advances[glyphIndex]))
+                break;
+            x -= truncateFixedPointToInteger(advances[glyphIndex]);
+        }
+    }
+
+    return glyphIndex;
 }
 
+// Return the code point index for the given |x| offset into the text run.
 int Font::offsetForPositionForComplexText(const TextRun& run, int x,
                                           bool includePartialGlyphs) const
 {
-    notImplemented();
-    return 0;
+    // (Mac code ignores includePartialGlyphs, and they don't know what it's
+    // supposed to do, so we just ignore it as well.)
+    SkPaint paint;
+    const FontPlatformData& font = fontPlatformDataForTextRun(this, run);
+    font.setupPaint(&paint);
+
+    TextRunWalker walker(run, &paint, 0, &font);
+
+    // If this is RTL text, the first glyph from the left is actually the last
+    // code point. So we need to know how many code points there are total in
+    // order to subtract. This is different from the length of the TextRun
+    // because UTF-16 surrogate pairs are a single code point, but 32-bits long.
+    // In LTR we leave this as 0 so that we get the correct value for
+    // |basePosition|, below.
+    unsigned totalCodePoints = 0;
+    if (walker.rtl()) {
+        ssize_t offset = 0;
+        while (offset < run.length()) {
+            utf16_to_code_point(run.characters(), run.length(), &offset);
+            totalCodePoints++;
+        }
+    }
+
+    unsigned basePosition = totalCodePoints;
+
+    // For RTL:
+    //   code-point order:  abcd efg hijkl
+    //   on screen:         lkjih gfe dcba
+    //                                ^   ^
+    //                                |   |
+    //                  basePosition--|   |
+    //                 totalCodePoints----|
+    // Since basePosition is currently the total number of code-points, the
+    // first thing we do is decrement it so that it's pointing to the start of
+    // the current script-run.
+    //
+    // For LTR, basePosition is zero so it already points to the start of the
+    // first script run.
+    while (walker.nextScriptRun()) {
+        if (walker.rtl())
+            basePosition -= walker.numCodePoints();
+
+        if (x < walker.width()) {
+            // The x value in question is within this script run. We consider
+            // each glyph in presentation order and stop when we find the one
+            // covering this position.
+            const int glyphIndex = glyphIndexForXPositionInScriptRun(walker, x);
+
+            // Now that we have a glyph index, we have to turn that into a
+            // code-point index. Because of ligatures, several code-points may
+            // have gone into a single glyph. We iterate over the clusters log
+            // and find the first code-point which contributed to the glyph.
+            const unsigned short* log = walker.logClusters();
+            for (unsigned j = 0; j < walker.numCodePoints(); ++j) {
+                if (log[j] == glyphIndex)
+                    return basePosition + j;
+            }
+
+            ASSERT_NOT_REACHED();
+        }
+
+        x -= walker.width();
+
+        if (!walker.rtl())
+            basePosition += walker.numCodePoints();
+    }
+
+    return basePosition;
 }
 
+// Return the rectangle for selecting the given range of code-points in the TextRun.
 FloatRect Font::selectionRectForComplexText(const TextRun& run,
-                                            const IntPoint& point, int h,
+                                            const IntPoint& point, int height,
                                             int from, int to) const
 {
-    notImplemented();
-    return FloatRect();
+    SkPaint paint;
+    const FontPlatformData& font = fontPlatformDataForTextRun(this, run);
+    font.setupPaint(&paint);
+
+    int fromX = -1, toX = -1, fromAdvance = -1, toAdvance = -1;
+
+    TextRunWalker walker(run, &paint, 0, &font);
+
+    // Base will point to the x offset for the current script run. Note that, in
+    // the LTR case, width will be 0.
+    int base = walker.rtl() ? walker.widthOfFullRun() : 0;
+    const int leftEdge = base;
+
+    // We want to enumerate the script runs in code point order in the following
+    // code. This call also resets |walker|.
+    walker.setBackwardsIteration(false);
+
+    while (walker.nextScriptRun() && (fromX == -1 || toX == -1)) {
+        // TextRunWalker will helpfully accululate the x offsets for different
+        // script runs for us. For this code, however, we always want the x offsets
+        // to start from zero so we call this before each script run.
+        walker.setXOffsetToZero();
+
+        if (walker.rtl())
+            base -= walker.width();
+
+        if (fromX == -1 && from < walker.numCodePoints()) {
+            // |from| is within this script run. So we index the clusters log to
+            // find which glyph this code-point contributed to and find its x
+            // position.
+            int glyph = walker.logClusters()[from];
+            fromX = base + walker.xPositions()[glyph];
+            fromAdvance = walker.advances()[glyph];
+        } else
+            from -= walker.numCodePoints();
+
+        if (toX == -1 && to < walker.numCodePoints()) {
+            int glyph = walker.logClusters()[to];
+            toX = base + walker.xPositions()[glyph];
+            toAdvance = walker.advances()[glyph];
+        } else
+            to -= walker.numCodePoints();
+
+        if (!walker.rtl())
+            base += walker.width();
+    }
+
+    // The position in question might be just after the text.
+    const int rightEdge = base;
+    if (fromX == -1 && !from)
+        fromX = leftEdge;
+    else if (walker.rtl())
+       fromX += truncateFixedPointToInteger(fromAdvance);
+
+    if (toX == -1 && !to)
+        toX = rightEdge;
+    else if (!walker.rtl())
+        toX += truncateFixedPointToInteger(toAdvance);
+
+    ASSERT(fromX != -1 && toX != -1);
+
+    if (fromX < toX)
+        return FloatRect(point.x() + fromX, point.y(), toX - fromX, height);
+
+    return FloatRect(point.x() + toX, point.y(), fromX - toX, height);
 }
 
-}  // namespace WebCore
+} // namespace WebCore
diff --git a/WebCore/platform/graphics/chromium/HarfbuzzSkia.cpp b/WebCore/platform/graphics/chromium/HarfbuzzSkia.cpp
new file mode 100644 (file)
index 0000000..9fd09e1
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "FontPlatformData.h"
+#include "wtf/OwnArrayPtr.h"
+
+#include "SkFontHost.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkPoint.h"
+#include "SkRect.h"
+
+extern "C" {
+#include "harfbuzz-shaper.h"
+}
+
+// This file implements the callbacks which Harfbuzz requires by using Skia
+// calls. See the Harfbuzz source for references about what these callbacks do.
+
+namespace WebCore {
+
+static HB_Fixed SkiaScalarToHarfbuzzFixed(SkScalar value)
+{
+    // HB_Fixed is a 26.6 fixed point format.
+    return value * 64;
+}
+
+static HB_Bool stringToGlyphs(HB_Font hbFont, const HB_UChar16* characters, hb_uint32 length, HB_Glyph* glyphs, hb_uint32* glyphsSize, HB_Bool isRTL)
+{
+    FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData);
+    SkPaint paint;
+
+    font->setupPaint(&paint);
+    paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
+    int numGlyphs = paint.textToGlyphs(characters, length * sizeof(uint16_t), reinterpret_cast<uint16_t*>(glyphs));
+
+    // HB_Glyph is 32-bit, but Skia outputs only 16-bit numbers. So our
+    // |glyphs| array needs to be converted.
+    for (int i = numGlyphs - 1; i >= 0; --i) {
+        uint16_t value;
+        // We use a memcpy to avoid breaking strict aliasing rules.
+        memcpy(&value, reinterpret_cast<char*>(glyphs) + sizeof(uint16_t) * i, sizeof(uint16_t));
+        glyphs[i] = value;
+    }
+
+    *glyphsSize = numGlyphs;
+    return 1;
+}
+
+static void glyphsToAdvances(HB_Font hbFont, const HB_Glyph* glyphs, hb_uint32 numGlyphs, HB_Fixed* advances, int flags)
+{
+    FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData);
+    SkPaint paint;
+
+    font->setupPaint(&paint);
+    paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+
+    OwnArrayPtr<uint16_t> glyphs16(new uint16_t[numGlyphs]);
+    if (!glyphs16.get())
+        return;
+    for (unsigned i = 0; i < numGlyphs; ++i)
+        glyphs16[i] = glyphs[i];
+    paint.getTextWidths(glyphs16.get(), numGlyphs * sizeof(uint16_t), reinterpret_cast<SkScalar*>(advances));
+
+    // The |advances| values which Skia outputs are SkScalars, which are floats
+    // in Chromium. However, Harfbuzz wants them in 26.6 fixed point format.
+    // These two formats are both 32-bits long.
+    for (unsigned i = 0; i < numGlyphs; ++i) {
+        float value;
+        // We use a memcpy to avoid breaking strict aliasing rules.
+        memcpy(&value, reinterpret_cast<char*>(advances) + sizeof(float) * i, sizeof(float));
+        advances[i] = SkiaScalarToHarfbuzzFixed(value);
+    }
+}
+
+static HB_Bool canRender(HB_Font hbFont, const HB_UChar16* characters, hb_uint32 length)
+{
+    FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData);
+    SkPaint paint;
+
+    font->setupPaint(&paint);
+    paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
+
+    OwnArrayPtr<uint16_t> glyphs16(new uint16_t[length]);
+    if (!glyphs16.get())
+        return 0;
+    int numGlyphs = paint.textToGlyphs(characters, length * sizeof(uint16_t), glyphs16.get());
+
+    bool canRender = true;
+    for (int i = 0; i < numGlyphs; ++i) {
+        if (!glyphs16[i]) {
+            canRender = false;
+            break;
+        }
+    }
+
+    return canRender;
+}
+
+static HB_Error getOutlinePoint(HB_Font hbFont, HB_Glyph glyph, int flags, hb_uint32 point, HB_Fixed* xPos, HB_Fixed* yPos, hb_uint32* resultingNumPoints)
+{
+    FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData);
+    SkPaint paint;
+
+    if (flags & HB_ShaperFlag_UseDesignMetrics)
+        return HB_Err_Invalid_Argument;  // This is requesting pre-hinted positions. We can't support this.
+
+    font->setupPaint(&paint);
+    paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+    uint16_t glyph16 = glyph;
+    SkPath path;
+    paint.getTextPath(&glyph16, sizeof(glyph16), 0, 0, &path);
+    int numPoints = path.getPoints(NULL, 0);
+    if (point >= numPoints)
+        return HB_Err_Invalid_SubTable;
+    SkPoint* points = reinterpret_cast<SkPoint*>(fastMalloc(sizeof(SkPoint) * (point + 1)));
+    if (!points)
+        return HB_Err_Invalid_SubTable;
+    // Skia does let us get a single point from the path.
+    path.getPoints(points, point + 1);
+    *xPos = SkiaScalarToHarfbuzzFixed(points[point].fX);
+    *yPos = SkiaScalarToHarfbuzzFixed(points[point].fY);
+    *resultingNumPoints = numPoints;
+    fastFree(points);
+
+    return HB_Err_Ok;
+}
+
+static void getGlyphMetrics(HB_Font hbFont, HB_Glyph glyph, HB_GlyphMetrics* metrics)
+{
+    FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData);
+    SkPaint paint;
+
+    font->setupPaint(&paint);
+    paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+    uint16_t glyph16 = glyph;
+    SkScalar width;
+    SkRect bounds;
+    paint.getTextWidths(&glyph16, sizeof(glyph16), &width, &bounds);
+
+    metrics->x = SkiaScalarToHarfbuzzFixed(width);
+    // We can't actually get the |y| correct because Skia doesn't export
+    // the vertical advance. However, nor we do ever render vertical text at
+    // the moment so it's unimportant.
+    metrics->y = 0;
+    metrics->width = SkiaScalarToHarfbuzzFixed(bounds.width());
+    metrics->height = SkiaScalarToHarfbuzzFixed(bounds.height());
+    metrics->xOffset = SkiaScalarToHarfbuzzFixed(bounds.fLeft);
+    metrics->yOffset = SkiaScalarToHarfbuzzFixed(bounds.fTop);
+}
+
+static HB_Fixed getFontMetric(HB_Font hbFont, HB_FontMetric metric)
+{
+    FontPlatformData* font = reinterpret_cast<FontPlatformData*>(hbFont->userData);
+    SkPaint paint;
+
+    font->setupPaint(&paint);
+    SkPaint::FontMetrics skiaMetrics;
+    paint.getFontMetrics(&skiaMetrics);
+
+    switch (metric) {
+    case HB_FontAscent:
+        return SkiaScalarToHarfbuzzFixed(-skiaMetrics.fAscent);
+    // We don't support getting the rest of the metrics and Harfbuzz doesn't seem to need them.
+    default:
+        return 0;
+    }
+}
+
+HB_FontClass harfbuzzSkiaClass = {
+    stringToGlyphs,
+    glyphsToAdvances,
+    canRender,
+    getOutlinePoint,
+    getGlyphMetrics,
+    getFontMetric,
+};
+
+HB_Error harfbuzzSkiaGetTable(void* voidface, const HB_Tag tag, HB_Byte* buffer, HB_UInt* len)
+{
+    FontPlatformData* font = reinterpret_cast<FontPlatformData*>(voidface);
+
+    const size_t tableSize = SkFontHost::GetTableSize(font->uniqueID(), tag);
+    if (!tableSize)
+        return HB_Err_Invalid_Argument;
+    // If Harfbuzz specified a NULL buffer then it's asking for the size of the table.
+    if (!buffer) {
+        *len = tableSize;
+        return HB_Err_Ok;
+    }
+
+    if (*len < tableSize)
+        return HB_Err_Invalid_Argument;
+    SkFontHost::GetTableData(font->uniqueID(), tag, 0, tableSize, buffer);
+    return HB_Err_Ok;
+}
+
+}  // namespace WebCore