Merge "Use Harfbuzz instead of ICU4C for computing advances"
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index b4a0e4f..66d8a36 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -93,6 +93,7 @@
 	android/graphics/DrawFilter.cpp \
 	android/graphics/CreateJavaOutputStreamAdaptor.cpp \
 	android/graphics/Graphics.cpp \
+	android/graphics/HarfbuzzSkia.cpp \
 	android/graphics/Interpolator.cpp \
 	android/graphics/LayerRasterizer.cpp \
 	android/graphics/MaskFilter.cpp \
@@ -174,6 +175,7 @@
 	external/icu4c/i18n \
 	external/icu4c/common \
 	external/jpeg \
+	external/harfbuzz/src \
 	frameworks/opt/emoji
 
 LOCAL_SHARED_LIBRARIES := \
@@ -206,6 +208,7 @@
 	libjpeg \
 	libnfc_ndef \
 	libusbhost \
+	libharfbuzz \
 
 ifeq ($(USE_OPENGL_RENDERER),true)
 	LOCAL_SHARED_LIBRARIES += libhwui
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index 0cdb357..b4ad9e9 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -755,6 +755,27 @@
         env->ReleaseStringChars(text, textArray);
     }
 
+    static void drawGlyphs___CIIFFIPaint(JNIEnv* env, jobject, SkCanvas* canvas,
+                                         jcharArray glyphs, int index, int count,
+                                         jfloat x, jfloat y, int flags, SkPaint* paint) {
+        jchar* glyphArray = env->GetCharArrayElements(glyphs, NULL);
+
+        // TODO: need to suppress this code after the GL renderer is modified for not
+        // copying the paint
+
+        // Save old text encoding
+        SkPaint::TextEncoding oldEncoding = paint->getTextEncoding();
+        // Define Glyph encoding
+        paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+
+        TextLayout::drawText(paint, glyphArray + index, count, flags, x, y, canvas);
+
+        // Get back old encoding
+        paint->setTextEncoding(oldEncoding);
+
+        env->ReleaseCharArrayElements(glyphs, glyphArray, JNI_ABORT);
+    }
+
     static void drawTextRun___CIIIIFFIPaint(
         JNIEnv* env, jobject, SkCanvas* canvas, jcharArray text, int index,
         int count, int contextIndex, int contextCount,
@@ -946,6 +967,8 @@
         (void*) SkCanvasGlue::drawText___CIIFFIPaint},
     {"native_drawText","(ILjava/lang/String;IIFFII)V",
         (void*) SkCanvasGlue::drawText__StringIIFFIPaint},
+    {"native_drawGlyphs","(I[CIIFFII)V",
+        (void*) SkCanvasGlue::drawGlyphs___CIIFFIPaint},
     {"native_drawTextRun","(I[CIIIIFFII)V",
         (void*) SkCanvasGlue::drawTextRun___CIIIIFFIPaint},
     {"native_drawTextRun","(ILjava/lang/String;IIIIFFII)V",
diff --git a/core/jni/android/graphics/HarfbuzzSkia.cpp b/core/jni/android/graphics/HarfbuzzSkia.cpp
new file mode 100644
index 0000000..58fb32b
--- /dev/null
+++ b/core/jni/android/graphics/HarfbuzzSkia.cpp
@@ -0,0 +1,239 @@
+/*
+ * Copyright 2011, The Android Open Source Project
+ * Copyright 2011, 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 "HarfbuzzSkia.h"
+
+#include "SkFontHost.h"
+
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkPoint.h"
+#include "SkRect.h"
+#include "SkTypeface.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 android {
+
+static HB_Fixed SkiaScalarToHarfbuzzFixed(SkScalar value)
+{
+    // HB_Fixed is a 26.6 fixed point format.
+    return value * 64;
+}
+
+static void setupPaintWithFontData(SkPaint* paint, FontData* data) {
+    paint->setAntiAlias(true);
+    paint->setSubpixelText(true);
+    paint->setHinting(SkPaint::kSlight_Hinting);
+    paint->setTextSize(SkFloatToScalar(data->textSize));
+    paint->setTypeface(data->typeFace);
+    paint->setFakeBoldText(data->fakeBold);
+    paint->setTextSkewX(data->fakeItalic ? -SK_Scalar1/4 : 0);
+}
+
+static HB_Bool stringToGlyphs(HB_Font hbFont, const HB_UChar16* characters, hb_uint32 length,
+        HB_Glyph* glyphs, hb_uint32* glyphsSize, HB_Bool isRTL)
+{
+    FontData* data = reinterpret_cast<FontData*>(hbFont->userData);
+    SkPaint paint;
+    setupPaintWithFontData(&paint, data);
+
+    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(value));
+        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)
+{
+    FontData* data = reinterpret_cast<FontData*>(hbFont->userData);
+    SkPaint paint;
+    setupPaintWithFontData(&paint, data);
+
+    paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+
+    uint16_t* glyphs16 = new uint16_t[numGlyphs];
+    if (!glyphs16)
+        return;
+    for (unsigned i = 0; i < numGlyphs; ++i)
+        glyphs16[i] = glyphs[i];
+    paint.getTextWidths(glyphs16, 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(value));
+        advances[i] = SkiaScalarToHarfbuzzFixed(value);
+    }
+    delete glyphs16;
+}
+
+static HB_Bool canRender(HB_Font hbFont, const HB_UChar16* characters, hb_uint32 length)
+{
+    FontData* data = reinterpret_cast<FontData*>(hbFont->userData);
+    SkPaint paint;
+    setupPaintWithFontData(&paint, data);
+
+    paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
+
+    uint16_t* glyphs16 = new uint16_t[length];
+    int numGlyphs = paint.textToGlyphs(characters, length * sizeof(uint16_t), glyphs16);
+
+    bool result = true;
+    for (int i = 0; i < numGlyphs; ++i) {
+        if (!glyphs16[i]) {
+            result = false;
+            break;
+        }
+    }
+    delete glyphs16;
+    return result;
+}
+
+static HB_Error getOutlinePoint(HB_Font hbFont, HB_Glyph glyph, int flags, hb_uint32 point,
+        HB_Fixed* xPos, HB_Fixed* yPos, hb_uint32* resultingNumPoints)
+{
+    FontData* data = reinterpret_cast<FontData*>(hbFont->userData);
+    SkPaint paint;
+    setupPaintWithFontData(&paint, data);
+
+    if (flags & HB_ShaperFlag_UseDesignMetrics)
+        // This is requesting pre-hinted positions. We can't support this.
+        return HB_Err_Invalid_Argument;
+
+    paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+    uint16_t glyph16 = glyph;
+    SkPath path;
+    paint.getTextPath(&glyph16, sizeof(glyph16), 0, 0, &path);
+    uint32_t numPoints = path.getPoints(0, 0);
+    if (point >= numPoints)
+        return HB_Err_Invalid_SubTable;
+    SkPoint* points = reinterpret_cast<SkPoint*>(malloc(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;
+    delete points;
+
+    return HB_Err_Ok;
+}
+
+static void getGlyphMetrics(HB_Font hbFont, HB_Glyph glyph, HB_GlyphMetrics* metrics)
+{
+    FontData* data = reinterpret_cast<FontData*>(hbFont->userData);
+    SkPaint paint;
+    setupPaintWithFontData(&paint, data);
+
+    paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+    uint16_t glyph16 = glyph;
+    SkScalar width;
+    SkRect bounds;
+    paint.getTextWidths(&glyph16, sizeof(glyph16), &width, &bounds);
+
+    metrics->x = SkiaScalarToHarfbuzzFixed(bounds.fLeft);
+    metrics->y = SkiaScalarToHarfbuzzFixed(bounds.fTop);
+    metrics->width = SkiaScalarToHarfbuzzFixed(bounds.width());
+    metrics->height = SkiaScalarToHarfbuzzFixed(bounds.height());
+
+    metrics->xOffset = 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->yOffset = 0;
+}
+
+static HB_Fixed getFontMetric(HB_Font hbFont, HB_FontMetric metric)
+{
+    FontData* data = reinterpret_cast<FontData*>(hbFont->userData);
+    SkPaint paint;
+    setupPaintWithFontData(&paint, data);
+
+    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;
+    }
+    return 0;
+}
+
+const HB_FontClass harfbuzzSkiaClass = {
+    stringToGlyphs,
+    glyphsToAdvances,
+    canRender,
+    getOutlinePoint,
+    getGlyphMetrics,
+    getFontMetric,
+};
+
+HB_Error harfbuzzSkiaGetTable(void* voidface, const HB_Tag tag, HB_Byte* buffer, HB_UInt* len)
+{
+    FontData* data = reinterpret_cast<FontData*>(voidface);
+    SkTypeface* typeface = data->typeFace;
+
+    const size_t tableSize = SkFontHost::GetTableSize(typeface->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(typeface->uniqueID(), tag, 0, tableSize, buffer);
+    return HB_Err_Ok;
+}
+
+}  // namespace android
diff --git a/core/jni/android/graphics/HarfbuzzSkia.h b/core/jni/android/graphics/HarfbuzzSkia.h
new file mode 100644
index 0000000..d057d76
--- /dev/null
+++ b/core/jni/android/graphics/HarfbuzzSkia.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2011, The Android Open Source Project
+ * Copyright 2011, 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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.
+ */
+
+#ifndef HarfbuzzSkia_h
+#define HarfbuzzSkia_h
+
+#include "SkTypeface.h"
+
+extern "C" {
+#include "harfbuzz-shaper.h"
+}
+
+namespace android {
+    typedef struct {
+        SkTypeface* typeFace;
+        float textSize;
+        bool fakeBold;
+        bool fakeItalic;
+    } FontData;
+
+    HB_Error harfbuzzSkiaGetTable(void* voidface, const HB_Tag, HB_Byte* buffer, HB_UInt* len);
+    extern const HB_FontClass harfbuzzSkiaClass;
+}  // namespace android
+
+#endif
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index e62b034..5c3497f 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -393,13 +393,40 @@
         return count;
     }
  
-    static int getTextWidths__StringII_F(JNIEnv* env, jobject clazz, SkPaint* paint, jstring text, int start, int end, jfloatArray widths) {
+    static int getTextWidths__StringII_F(JNIEnv* env, jobject clazz, SkPaint* paint, jstring text,
+            int start, int end, jfloatArray widths) {
         const jchar* textArray = env->GetStringChars(text, NULL);
         int count = dotextwidths(env, paint, textArray + start, end - start, widths);
         env->ReleaseStringChars(text, textArray);
         return count;
     }
 
+    static int doTextGlyphs(JNIEnv* env, SkPaint* paint, const jchar* text, jint start, jint count,
+            jint contextCount, jint flags, jcharArray glyphs) {
+        jchar* glyphsArray = env->GetCharArrayElements(glyphs, NULL);
+        HB_ShaperItem shaperItem;
+        HB_FontRec font;
+        FontData fontData;
+        RunAdvanceDescription::shapeWithHarfbuzz(&shaperItem, &font, &fontData, paint, text,
+                start, count, contextCount, flags);
+
+        int glyphCount = shaperItem.num_glyphs;
+        for (int i = 0; i < glyphCount; i++) {
+            glyphsArray[i] = (jchar) shaperItem.glyphs[i];
+        }
+        return glyphCount;
+    }
+
+    static int getTextGlyphs__StringIIIII_C(JNIEnv* env, jobject clazz, SkPaint* paint,
+            jstring text, jint start, jint end, jint contextStart, jint contextEnd, jint flags,
+            jcharArray glyphs) {
+        const jchar* textArray = env->GetStringChars(text, NULL);
+        int count = doTextGlyphs(env, paint, textArray + contextStart, start - contextStart,
+                end - start, contextEnd - contextStart, flags, glyphs);
+        env->ReleaseStringChars(text, textArray);
+        return count;
+    }
+
     static jfloat doTextRunAdvances(JNIEnv *env, SkPaint *paint, const jchar *text,
                                     jint start, jint count, jint contextCount, jint flags,
                                     jfloatArray advances, jint advancesIndex) {
@@ -725,6 +752,8 @@
         SkPaintGlue::getTextRunAdvances___CIIIII_FI},
     {"native_getTextRunAdvances","(ILjava/lang/String;IIIII[FI)F",
         (void*) SkPaintGlue::getTextRunAdvances__StringIIIII_FI},
+    {"native_getTextGlyphs","(ILjava/lang/String;IIIII[C)I",
+        (void*) SkPaintGlue::getTextGlyphs__StringIIIII_C},
     {"native_getTextRunCursor", "(I[CIIIII)I", (void*) SkPaintGlue::getTextRunCursor___C},
     {"native_getTextRunCursor", "(ILjava/lang/String;IIIII)I",
         (void*) SkPaintGlue::getTextRunCursor__String},
diff --git a/core/jni/android/graphics/RtlProperties.h b/core/jni/android/graphics/RtlProperties.h
index 6d8ba91..2c68fa3 100644
--- a/core/jni/android/graphics/RtlProperties.h
+++ b/core/jni/android/graphics/RtlProperties.h
@@ -45,5 +45,7 @@
     return kRtlDebugDisabled;
 }
 
+#define RTL_USE_HARFBUZZ 1
+
 } // namespace android
 #endif // ANDROID_RTL_PROPERTIES_H
diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp
index 7888769..a7265be 100644
--- a/core/jni/android/graphics/TextLayoutCache.cpp
+++ b/core/jni/android/graphics/TextLayoutCache.cpp
@@ -47,8 +47,16 @@
     if (mDebugEnabled) {
         LOGD("TextLayoutCache start time: %lld", mCacheStartTime);
     }
-
     mInitialized = true;
+
+    if (mDebugEnabled) {
+#if RTL_USE_HARFBUZZ
+        LOGD("TextLayoutCache is using HARFBUZZ");
+#else
+        LOGD("TextLayoutCache is using ICU");
+#endif
+    }
+
     if (mDebugEnabled) {
         LOGD("TextLayoutCache initialization is done");
     }
diff --git a/core/jni/android/graphics/TextLayoutCache.h b/core/jni/android/graphics/TextLayoutCache.h
index 9d55918..e962a86 100644
--- a/core/jni/android/graphics/TextLayoutCache.h
+++ b/core/jni/android/graphics/TextLayoutCache.h
@@ -30,6 +30,8 @@
 
 #include "unicode/ubidi.h"
 #include "unicode/ushape.h"
+#include "HarfbuzzSkia.h"
+#include "harfbuzz-shaper.h"
 
 #include <android_runtime/AndroidRuntime.h>
 
@@ -52,8 +54,14 @@
 // Define the interval in number of cache hits between two statistics dump
 #define DEFAULT_DUMP_STATS_CACHE_HIT_INTERVAL 100
 
+// Define if we want to have Advances debug values
+#define DEBUG_ADVANCES 0
+
 namespace android {
 
+// Harfbuzz uses 26.6 fixed point values for pixel offsets
+#define HB_FIXED_TO_FLOAT(v) (((float) v) * (1.0 / 64))
+
 /**
  * TextLayoutCacheKey is the Cache key
  */
@@ -149,8 +157,18 @@
         advances = new float[count];
         this->count = count;
 
-        computeAdvances(paint, chars, start, count, contextCount, dirFlags,
+#if RTL_USE_HARFBUZZ
+        computeAdvancesWithHarfbuzz(paint, chars, start, count, contextCount, dirFlags,
                 advances, &totalAdvance);
+#else
+        computeAdvancesWithICU(paint, chars, start, count, contextCount, dirFlags,
+                advances, &totalAdvance);
+#endif
+#if DEBUG_ADVANCES
+        LOGD("Advances - count=%d - countextCount=%d - totalAdvance=%f - "
+                "adv[0]=%f adv[1]=%f adv[2]=%f adv[3]=%f", count, contextCount, totalAdvance,
+                advances[0], advances[1], advances[2], advances[3]);
+#endif
     }
 
     void copyResult(jfloat* outAdvances, jfloat* outTotalAdvance) {
@@ -165,8 +183,108 @@
         return sizeof(RunAdvanceDescription) + sizeof(jfloat) * count;
     }
 
-    static void computeAdvances(SkPaint* paint, const UChar* chars, size_t start, size_t count,
-            size_t contextCount, int dirFlags, jfloat* outAdvances, jfloat* outTotalAdvance) {
+    static void setupShaperItem(HB_ShaperItem* shaperItem, HB_FontRec* font, FontData* fontData,
+            SkPaint* paint, const UChar* chars, size_t start, size_t count, size_t contextCount,
+            int dirFlags) {
+        bool isRTL = dirFlags & 0x1;
+
+        font->klass = &harfbuzzSkiaClass;
+        font->userData = 0;
+        // 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;
+
+        memset(shaperItem, 0, sizeof(*shaperItem));
+        shaperItem->font = font;
+        shaperItem->face = HB_NewFace(shaperItem->font, harfbuzzSkiaGetTable);
+
+        // 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 plus a bit of
+        // padding and fallback if we find that we are wrong.
+        createGlyphArrays(shaperItem, (contextCount + 2) * 2);
+
+        // Free memory for clusters if needed and recreate the clusters array
+        if (shaperItem->log_clusters) {
+            delete shaperItem->log_clusters;
+        }
+        shaperItem->log_clusters = new unsigned short[contextCount];
+
+        shaperItem->item.pos = start;
+        shaperItem->item.length = count;
+        shaperItem->item.bidiLevel = isRTL;
+        shaperItem->item.script = isRTL ? HB_Script_Arabic : HB_Script_Common;
+
+        shaperItem->string = chars;
+        shaperItem->stringLength = contextCount;
+
+        fontData->textSize = paint->getTextSize();
+        fontData->fakeBold = paint->isFakeBoldText();
+        fontData->fakeItalic = (paint->getTextSkewX() > 0);
+        fontData->typeFace = paint->getTypeface();
+
+        shaperItem->font->userData = fontData;
+    }
+
+    static void shapeWithHarfbuzz(HB_ShaperItem* shaperItem, HB_FontRec* font, FontData* fontData,
+            SkPaint* paint, const UChar* chars, size_t start, size_t count, size_t contextCount,
+            int dirFlags) {
+        // Setup Harfbuzz Shaper
+        setupShaperItem(shaperItem, font, fontData, paint, chars, start, count,
+                contextCount, dirFlags);
+
+        // Shape
+        resetGlyphArrays(shaperItem);
+        while (!HB_ShapeItem(shaperItem)) {
+            // We overflowed our arrays. Resize and retry.
+            // HB_ShapeItem fills in shaperItem.num_glyphs with the needed size.
+            deleteGlyphArrays(shaperItem);
+            createGlyphArrays(shaperItem, shaperItem->num_glyphs << 1);
+            resetGlyphArrays(shaperItem);
+        }
+    }
+
+    static void computeAdvancesWithHarfbuzz(SkPaint* paint, const UChar* chars, size_t start,
+            size_t count, size_t contextCount, int dirFlags,
+            jfloat* outAdvances, jfloat* outTotalAdvance) {
+
+        bool isRTL = dirFlags & 0x1;
+
+        HB_ShaperItem shaperItem;
+        HB_FontRec font;
+        FontData fontData;
+        shapeWithHarfbuzz(&shaperItem, &font, &fontData, paint, chars, start, count,
+                contextCount, dirFlags);
+
+#if DEBUG_ADVANCES
+        LOGD("HARFBUZZ -- num_glypth=%d", shaperItem.num_glyphs);
+#endif
+
+        jfloat totalAdvance = 0;
+        for (size_t i = 0; i < count; i++) {
+            // Be careful: we need to use roundf() for doing the same way as Skia is doing
+            totalAdvance += outAdvances[i] = roundf(HB_FIXED_TO_FLOAT(shaperItem.advances[i]));
+
+#if DEBUG_ADVANCES
+            LOGD("hb-adv = %d - rebased = %f - total = %f", shaperItem.advances[i], outAdvances[i],
+                    totalAdvance);
+#endif
+        }
+
+        deleteGlyphArrays(&shaperItem);
+        HB_FreeFace(shaperItem.face);
+
+        *outTotalAdvance = totalAdvance;
+    }
+
+    static void computeAdvancesWithICU(SkPaint* paint, const UChar* chars, size_t start,
+            size_t count, size_t contextCount, int dirFlags,
+            jfloat* outAdvances, jfloat* outTotalAdvance) {
+
         SkAutoSTMalloc<CHAR_BUFFER_SIZE, jchar> tempBuffer(contextCount);
         jchar* buffer = tempBuffer.get();
 
@@ -199,6 +317,9 @@
 
         jfloat totalAdvance = 0;
         if (widths < count) {
+#if DEBUG_ADVANCES
+        LOGD("ICU -- count=%d", widths);
+#endif
             // Skia operates on code points, not code units, so surrogate pairs return only
             // one value. Expand the result so we have one value per UTF-16 code unit.
 
@@ -213,10 +334,19 @@
                         text[p-1] < UNICODE_FIRST_LOW_SURROGATE) {
                     outAdvances[p++] = 0;
                 }
+#if DEBUG_ADVANCES
+                LOGD("icu-adv = %f - total = %f", outAdvances[i], totalAdvance);
+#endif
             }
         } else {
+#if DEBUG_ADVANCES
+        LOGD("ICU -- count=%d", count);
+#endif
             for (size_t i = 0; i < count; i++) {
                 totalAdvance += outAdvances[i] = SkScalarToFloat(scalarArray[i]);
+#if DEBUG_ADVANCES
+                LOGD("icu-adv = %f - total = %f", outAdvances[i], totalAdvance);
+#endif
             }
         }
         *outTotalAdvance = totalAdvance;
@@ -228,6 +358,32 @@
     size_t count;
 
     uint32_t elapsedTime;
+
+    static void deleteGlyphArrays(HB_ShaperItem* shaperItem) {
+        delete[] shaperItem->glyphs;
+        delete[] shaperItem->attributes;
+        delete[] shaperItem->advances;
+        delete[] shaperItem->offsets;
+    }
+
+    static void createGlyphArrays(HB_ShaperItem* shaperItem, int size) {
+        shaperItem->glyphs = new HB_Glyph[size];
+        shaperItem->attributes = new HB_GlyphAttributes[size];
+        shaperItem->advances = new HB_Fixed[size];
+        shaperItem->offsets = new HB_FixedPoint[size];
+        shaperItem->num_glyphs = size;
+    }
+
+    static void resetGlyphArrays(HB_ShaperItem* shaperItem) {
+        int size = shaperItem->num_glyphs;
+        // All the types here don't have pointers. It is safe to reset to
+        // zero unless Harfbuzz breaks the compatibility in the future.
+        memset(shaperItem->glyphs, 0, size * sizeof(shaperItem->glyphs[0]));
+        memset(shaperItem->attributes, 0, size * sizeof(shaperItem->attributes[0]));
+        memset(shaperItem->advances, 0, size * sizeof(shaperItem->advances[0]));
+        memset(shaperItem->offsets, 0, size * sizeof(shaperItem->offsets[0]));
+    }
+
 }; // RunAdvanceDescription
 
 
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 965abe9..e493b18 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -1330,6 +1330,29 @@
     }
 
     /**
+     * Draw the glyphs, with origin at (x,y), using the specified paint. The
+     * origin is interpreted based on the Align setting in the paint.
+     *
+     * @param glyphs The glyphs to be drawn
+     * @param x      The x-coordinate of the origin of the text being drawn
+     * @param y      The y-coordinate of the origin of the text being drawn
+     * @param paint  The paint used for the text (e.g. color, size, style)
+     *
+     * @hide
+     *
+     * Used only for BiDi / RTL Tests
+     */
+    public void drawGlyphs(char[] glyphs, int index, int count, float x, float y,
+                         Paint paint) {
+        if ((index | count | (index + count) |
+            (glyphs.length - index - count)) < 0) {
+            throw new IndexOutOfBoundsException();
+        }
+        native_drawGlyphs(mNativeCanvas, glyphs, index, count, x, y, paint.mBidiFlags,
+                paint.mNativePaint);
+    }
+
+    /**
      * Draw the text, with origin at (x,y), using the specified paint. The
      * origin is interpreted based on the Align setting in the paint.
      *
@@ -1722,7 +1745,9 @@
     private static native void native_drawText(int nativeCanvas, String text,
                                                int start, int end, float x,
                                                float y, int flags, int paint);
-
+    private static native void native_drawGlyphs(int nativeCanvas, char[] glyphs,
+                                               int index, int count, float x,
+                                               float y, int flags, int paint);
     private static native void native_drawTextRun(int nativeCanvas, String text,
             int start, int end, int contextStart, int contextEnd,
             float x, float y, int flags, int paint);
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 0a23bae..96eb936 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -1455,6 +1455,43 @@
     }
 
     /**
+     * Return the glypth Ids for the characters in the string.
+     *
+     * @param text   The text to measure
+     * @param start  The index of the first char to to measure
+     * @param end    The end of the text slice to measure
+     * @param contextStart the index of the first character to use for shaping context,
+     * must be <= start
+     * @param contextEnd the index past the last character to use for shaping context,
+     * must be >= end
+     * @param flags the flags to control the advances, either {@link #DIRECTION_LTR}
+     * or {@link #DIRECTION_RTL}
+     * @param glyphs array to receive the glyph Ids of the characters.
+     *               Must be at least a large as the text.
+     * @return       the number of glyphs in the returned array
+     *
+     * @hide
+     *
+     * Used only for BiDi / RTL Tests
+     */
+    public int getTextGlypths(String text, int start, int end, int contextStart, int contextEnd,
+            int flags, char[] glyphs) {
+        if ((start | end | contextStart | contextEnd | (end - start)
+                | (start - contextStart) | (contextEnd - end) | (text.length() - end)
+                | (text.length() - contextEnd)) < 0) {
+            throw new IndexOutOfBoundsException();
+        }
+        if (end - start > glyphs.length) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+        if (flags != DIRECTION_LTR && flags != DIRECTION_RTL) {
+            throw new IllegalArgumentException("unknown flags value: " + flags);
+        }
+        return native_getTextGlyphs(mNativePaint, text, start, end, contextStart, contextEnd,
+                flags, glyphs);
+    }
+
+    /**
      * Convenience overload that takes a char array instead of a
      * String.
      *
@@ -1859,6 +1896,10 @@
     private static native int native_getTextWidths(int native_object,
                             String text, int start, int end, float[] widths);
 
+    private static native int native_getTextGlyphs(int native_object,
+            String text, int start, int end, int contextStart, int contextEnd,
+            int flags, char[] glyphs);
+
     private static native float native_getTextRunAdvances(int native_object,
             char[] text, int index, int count, int contextIndex, int contextCount,
             int flags, float[] advances, int advancesIndex);
diff --git a/tests/BiDiTests/Android.mk b/tests/BiDiTests/Android.mk
new file mode 100644
index 0000000..ae29fc2
--- /dev/null
+++ b/tests/BiDiTests/Android.mk
@@ -0,0 +1,27 @@
+# Copyright (C) 2011 The Android Open Source Project
+#
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := BiDiTests
+
+LOCAL_PROGUARD_FLAG_FILES := proguard.flags
+
+include $(BUILD_PACKAGE)
\ No newline at end of file
diff --git a/tests/BiDiTests/AndroidManifest.xml b/tests/BiDiTests/AndroidManifest.xml
new file mode 100644
index 0000000..346ace8
--- /dev/null
+++ b/tests/BiDiTests/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     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.
+-->
+
+<!-- Declare the contents of this Android application.  The namespace
+     attribute brings in the Android platform namespace, and the package
+     supplies a unique name for the application.  When writing your
+     own application, the package name must be changed from "com.example.*"
+     to come from a domain that you own or have control over. -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.bidi"
+    android:versionCode="1"
+    android:versionName="1.0">
+
+    <application android:label="BiDiTests">
+        <activity android:name="BiDiTestActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+    </application>
+</manifest>
\ No newline at end of file
diff --git a/tests/BiDiTests/proguard.flags b/tests/BiDiTests/proguard.flags
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/BiDiTests/proguard.flags
diff --git a/tests/BiDiTests/res/layout/biditest_main.xml b/tests/BiDiTests/res/layout/biditest_main.xml
new file mode 100644
index 0000000..9f77ad2
--- /dev/null
+++ b/tests/BiDiTests/res/layout/biditest_main.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+
+       <Button android:id="@+id/button"
+               android:layout_height="wrap_content"
+               android:layout_width="wrap_content"
+               android:onClick="onButtonClick"
+               android:text="@string/button_text"
+               android:textSize="32dip"
+        />
+
+        <TextView android:id="@+id/textview"
+                  android:layout_height="wrap_content"
+                  android:layout_width="wrap_content"
+                  android:textSize="32dip"
+                  android:text="@string/textview_text"
+        />
+
+        <EditText android:id="@+id/textview"
+                  android:layout_height="wrap_content"
+                  android:layout_width="match_parent"
+                  android:textSize="32dip"
+                  android:text="@string/edittext_text"
+        />
+
+    </LinearLayout>
+
+    <view class="com.android.bidi.BiDiTestView"
+        android:id="@+id/main"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="#FF0000"
+    />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/BiDiTests/res/values/strings.xml b/tests/BiDiTests/res/values/strings.xml
new file mode 100644
index 0000000..ecff76e
--- /dev/null
+++ b/tests/BiDiTests/res/values/strings.xml
@@ -0,0 +1,26 @@
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     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.
+-->
+<resources>
+    <string name="button_text">Button</string>
+    <string name="textview_text">This is a text for a TextView</string>
+    <string name="edittext_text">mmmmmmmmmmmmmmmmmmmmmmmm</string>
+    <string name="normal_text">Normal String</string>
+    <string name="normal_long_text">mmmmmmmmmmmmmmmmmmmmmmmm</string>
+    <string name="arabic_text">&#x0644;&#x0627;</string>
+    <string name="chinese_text">利比亚局势或影响美俄关系发展</string>
+    <string name="italic_text">Italic String</string>
+    <string name="bold_text">Bold String</string>
+    <string name="bold_italic_text">Bold Italic String</string>
+</resources>
\ No newline at end of file
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
new file mode 100644
index 0000000..3d7dd81
--- /dev/null
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * 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.
+ */
+
+package com.android.bidi;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+
+public class BiDiTestActivity extends Activity {
+
+    static final String TAG = "BiDiTestActivity";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.biditest_main);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+
+    public void onButtonClick(View v) {
+        Log.v(TAG, "onButtonClick");
+    }
+}
\ No newline at end of file
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestView.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestView.java
new file mode 100644
index 0000000..e9b6fa6
--- /dev/null
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestView.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * 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.
+ */
+
+package com.android.bidi;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+
+public class BiDiTestView extends View {
+
+    private static final String TAG = "BiDiTestView";
+
+    private static final int BORDER_PADDING = 4;
+    private static final int TEXT_PADDING = 16;
+    private static final int TEXT_SIZE = 32;
+    private static final int ORIGIN = 48;
+    private static final int DELTA_Y = TEXT_SIZE;
+
+    private static final float DEFAULT_ITALIC_SKEW_X = -0.25f;
+
+    private Paint paint = new Paint();
+    private Rect rect = new Rect();
+
+    private String NORMAL_TEXT;
+    private String NORMAL_LONG_TEXT;
+    private String ITALIC_TEXT;
+    private String BOLD_TEXT;
+    private String BOLD_ITALIC_TEXT;
+    private String ARABIC_TEXT;
+    private String CHINESE_TEXT;
+
+    private Typeface typeface;
+
+    public BiDiTestView(Context context) {
+        super(context);
+        init(context);
+    }
+
+    public BiDiTestView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init(context);
+    }
+
+    public BiDiTestView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        init(context);
+    }
+
+    private void init(Context context) {
+        NORMAL_TEXT = context.getString(R.string.normal_text);
+        NORMAL_LONG_TEXT = context.getString(R.string.normal_long_text);
+        ITALIC_TEXT = context.getString(R.string.italic_text);
+        BOLD_TEXT = context.getString(R.string.bold_text);
+        BOLD_ITALIC_TEXT = context.getString(R.string.bold_italic_text);
+        ARABIC_TEXT = context.getString(R.string.arabic_text);
+        CHINESE_TEXT = context.getString(R.string.chinese_text);
+
+        typeface = paint.getTypeface();
+        paint.setAntiAlias(true);
+    }
+
+    @Override
+    public void onDraw(Canvas canvas) {
+        drawInsideRect(canvas, Color.BLACK);
+
+        int deltaX = testString(canvas, NORMAL_TEXT, ORIGIN, ORIGIN, paint, typeface,
+                false, false,  Paint.DIRECTION_LTR);
+        deltaX += testString(canvas, ITALIC_TEXT, ORIGIN + deltaX, ORIGIN, paint, typeface,
+                true, false,  Paint.DIRECTION_LTR);
+        deltaX += testString(canvas, BOLD_TEXT, ORIGIN + deltaX, ORIGIN, paint, typeface,
+                false, true,  Paint.DIRECTION_LTR);
+        deltaX += testString(canvas, BOLD_ITALIC_TEXT, ORIGIN + deltaX, ORIGIN, paint, typeface,
+                true, true,  Paint.DIRECTION_LTR);
+
+        // Test with a long string
+        deltaX = testString(canvas, NORMAL_LONG_TEXT, ORIGIN, ORIGIN + 2 * DELTA_Y, paint, typeface,
+                false, false,  Paint.DIRECTION_LTR);
+
+        // Test Arabic ligature
+        deltaX = testString(canvas, ARABIC_TEXT, ORIGIN, ORIGIN + 4 * DELTA_Y, paint, typeface,
+                false, false,  Paint.DIRECTION_RTL);
+
+        // Test Chinese
+        deltaX = testString(canvas, CHINESE_TEXT, ORIGIN, ORIGIN + 6 * DELTA_Y, paint, typeface,
+                false, false,  Paint.DIRECTION_LTR);
+    }
+
+    private int testString(Canvas canvas, String text, int x, int y, Paint paint, Typeface typeface,
+            boolean isItalic, boolean isBold, int dir) {
+        paint.setTypeface(typeface);
+
+        // Set paint properties
+        boolean oldFakeBold = paint.isFakeBoldText();
+        paint.setFakeBoldText(isBold);
+
+        float oldTextSkewX = paint.getTextSkewX();
+        if (isItalic) {
+            paint.setTextSkewX(DEFAULT_ITALIC_SKEW_X);
+        }
+
+        drawTextWithCanvasDrawText(text, canvas, x, y, TEXT_SIZE, Color.WHITE);
+
+        int length = text.length();
+        float[] advances = new float[length];
+        float textWidth = paint.getTextRunAdvances(text, 0, length, 0, length, 0, advances, 0);
+
+        logAdvances(text, textWidth, advances);
+        drawBoxAroundText(canvas, x, y, textWidth, TEXT_SIZE, Color.RED);
+
+        paint.setColor(Color.WHITE);
+        char[] glyphs = new char[2*length];
+        int count = getGlyphs(text, glyphs, dir);
+
+        logGlypths(glyphs, count);
+        drawTextWithDrawGlyph(canvas, glyphs, count, x, y + DELTA_Y);
+
+        // Restore old paint properties
+        paint.setFakeBoldText(oldFakeBold);
+        paint.setTextSkewX(oldTextSkewX);
+
+        return (int) Math.ceil(textWidth) + TEXT_PADDING;
+    }
+
+    private void drawTextWithDrawGlyph(Canvas canvas, char[] glyphs, int count, int x, int y) {
+        canvas.drawGlyphs(glyphs, 0, count, x, y, paint);
+    }
+
+    private void logGlypths(char[] glyphs, int count) {
+        Log.v(TAG, "GlyphIds - count=" + count);
+        for (int n = 0; n < count; n++) {
+            Log.v(TAG, "GlyphIds - Id[" + n + "]="+ (int)glyphs[n]);
+        }
+    }
+
+    private int getGlyphs(String text, char[] glyphs, int dir) {
+//        int dir = 1; // Paint.DIRECTION_LTR;
+        return paint.getTextGlypths(text, 0, text.length(), 0, text.length(), dir, glyphs);
+    }
+
+    private void drawInsideRect(Canvas canvas, int color) {
+        paint.setColor(color);
+        int width = getWidth();
+        int height = getHeight();
+        rect.set(BORDER_PADDING, BORDER_PADDING, width - BORDER_PADDING, height - BORDER_PADDING);
+        canvas.drawRect(rect, paint);
+    }
+
+    private void drawTextWithCanvasDrawText(String text, Canvas canvas,
+            float x, float y, float textSize, int color) {
+        paint.setColor(color);
+        paint.setTextSize(textSize);
+        canvas.drawText(text, x, y, paint);
+    }
+
+    private void drawBoxAroundText(Canvas canvas, int x, int y, float textWidth, int textSize,
+            int color) {
+        paint.setColor(color);
+        canvas.drawLine(x, y - textSize, x, y + 8, paint);
+        canvas.drawLine(x, y + 8, x + textWidth, y + 8, paint);
+        canvas.drawLine(x + textWidth, y - textSize, x + textWidth, y + 8, paint);
+    }
+
+    private void logAdvances(String text, float textWidth, float[] advances) {
+        Log.v(TAG, "Advances for text: " + text + " total=" + textWidth);
+        int length = advances.length;
+        for(int n=0; n<length; n++){
+            Log.v(TAG, "adv[" + n + "]=" + advances[n]);
+        }
+    }
+}