8144015: [PIT] failures of text layout font tests
authorprr
Thu, 01 Sep 2016 11:29:20 -0700
changeset 40992 788c9cca556f
parent 40991 24c96f117cf5
child 40993 a24dc7d0dd28
8144015: [PIT] failures of text layout font tests 8144023: [PIT] failure of text measurements in javax/swing/text/html/parser/Parser/6836089/bug6836089.java 8145542: The case failed automatically and thrown java.lang.ArrayIndexOutOfBoundsException exception 8151725: [macosx] ArrayIndexOOB exception when displaying Devanagari text in JEditorPane 8144240: [macosx][PIT] AIOOB in closed/javax/swing/text/GlyphPainter2/6427244/bug6427244.java 8152680: Regression in GlyphVector.getGlyphCharIndex behaviour 8158924: Incorrect i18n text document layout 8041480: ArrayIndexOutOfBoundsException when JTable contains certain string Reviewed-by: serb, srl
jdk/src/java.desktop/share/classes/sun/font/ExtendedTextSourceLabel.java
jdk/src/java.desktop/share/native/libfontmanager/HBShaper.c
jdk/test/java/awt/font/GlyphVector/GetGlyphCharIndexTest.java
jdk/test/java/awt/font/LineBreakMeasurer/TestLineBreakWithFontSub.java
jdk/test/java/awt/font/TextLayout/LigatureCaretTest.java
jdk/test/java/awt/font/TextLayout/TestJustification.html
jdk/test/java/awt/font/TextLayout/TestJustification.java
jdk/test/javax/swing/text/DevanagariEditor.java
jdk/test/javax/swing/text/GlyphPainter2/6427244/bug6427244.java
--- a/jdk/src/java.desktop/share/classes/sun/font/ExtendedTextSourceLabel.java	Thu Sep 01 16:18:14 2016 +0530
+++ b/jdk/src/java.desktop/share/classes/sun/font/ExtendedTextSourceLabel.java	Thu Sep 01 11:29:20 2016 -0700
@@ -550,13 +550,16 @@
     return charinfo;
   }
 
+  private static final boolean DEBUG = FontUtilities.debugFonts();
 /*
 * This takes the glyph info record obtained from the glyph vector and converts it into a similar record
 * adjusted to represent character data instead.  For economy we don't use glyph info records in this processing.
 *
 * Here are some constraints:
 * - there can be more glyphs than characters (glyph insertion, perhaps based on normalization, has taken place)
-* - there can not be fewer glyphs than characters (0xffff glyphs are inserted for characters ligaturized away)
+* - there can be fewer glyphs than characters
+*   Some layout engines may insert 0xffff glyphs for characters ligaturized away, but
+*   not all do, and it cannot be relied upon.
 * - each glyph maps to a single character, when multiple glyphs exist for a character they all map to it, but
 *   no two characters map to the same glyph
 * - multiple glyphs mapping to the same character need not be in sequence (thai, tamil have split characters)
@@ -578,7 +581,8 @@
 *
 * The algorithm works in the following way:
 * 1) we scan the glyphs ltr or rtl based on the bidi run direction
-* 2) we can work in place, since we always consume a glyph for each char we write
+* 2) Since the may be fewer glyphs than chars we cannot work in place.
+*    A new array is allocated for output.
 *    a) if the line is ltr, we start writing at position 0 until we finish, there may be leftver space
 *    b) if the line is rtl and 1-1, we start writing at position numChars/glyphs - 1 until we finish at 0
 *    c) otherwise if we don't finish at 0, we have to copy the data down
@@ -594,7 +598,7 @@
 *       iii) the x advance is the distance to the maximum x + adv of all glyphs whose advance is not zero
 *       iv) the y advance is the baseline
 *       v) vis x,y,w,h tightly encloses the vis x,y,w,h of all the glyphs with nonzero w and h
-* 4) we can make some simple optimizations if we know some things:
+* 4) In the future, we can make some simple optimizations to avoid copying if we know some things:
 *    a) if the mapping is 1-1, unidirectional, and there are no zero-adv glyphs, we just return the glyphinfo
 *    b) if the mapping is 1-1, unidirectional, we just adjust the remaining glyphs to originate at right/left of the base
 *    c) if the mapping is 1-1, we compute the base position and advance as we go, then go back to adjust the remaining glyphs
@@ -625,23 +629,20 @@
         System.out.println(source);
     }
 
-    /*
-    if ((gv.getDescriptionFlags() & 0x7) == 0) {
-        return glyphinfo;
-    }
-    */
-
     int numGlyphs = gv.getNumGlyphs();
     if (numGlyphs == 0) {
         return glyphinfo;
     }
     int[] indices = gv.getGlyphCharIndices(0, numGlyphs, null);
+    float[] charInfo = new float[source.getLength() * numvals];
 
-    boolean DEBUG = false;
     if (DEBUG) {
       System.err.println("number of glyphs: " + numGlyphs);
+      System.err.println("glyphinfo.len: " + glyphinfo.length);
+      System.err.println("indices.len: " + indices.length);
       for (int i = 0; i < numGlyphs; ++i) {
         System.err.println("g: " + i +
+            "  v: " + gv.getGlyphCode(i) +
             ", x: " + glyphinfo[i*numvals+posx] +
             ", a: " + glyphinfo[i*numvals+advx] +
             ", n: " + indices[i]);
@@ -650,22 +651,19 @@
 
     int minIndex = indices[0];  // smallest index seen this cluster
     int maxIndex = minIndex;    // largest index seen this cluster
-    int nextMin = 0;            // expected smallest index for this cluster
     int cp = 0;                 // character position
-    int cx = 0;                 // character index (logical)
+    int cc = 0;
     int gp = 0;                 // glyph position
     int gx = 0;                 // glyph index (visual)
     int gxlimit = numGlyphs;    // limit of gx, when we reach this we're done
     int pdelta = numvals;       // delta for incrementing positions
     int xdelta = 1;             // delta for incrementing indices
 
-    boolean ltr = (source.getLayoutFlags() & 0x1) == 0;
-    if (!ltr) {
+    boolean rtl = (source.getLayoutFlags() & 0x1) == 1;
+    if (rtl) {
         minIndex = indices[numGlyphs - 1];
         maxIndex = minIndex;
-        nextMin  = 0; // still logical
-        cp = glyphinfo.length - numvals;
-        cx = 0; // still logical
+        cp = charInfo.length - numvals;
         gp = glyphinfo.length - numvals;
         gx = numGlyphs - 1;
         gxlimit = -1;
@@ -693,47 +691,36 @@
     float cposl = 0, cposr = 0, cvisl = 0, cvist = 0, cvisr = 0, cvisb = 0;
     float baseline = 0;
 
-    // record if we have to copy data even when no cluster
-    boolean mustCopy = false;
-
     while (gx != gxlimit) {
         // start of new cluster
-        boolean haveCopy = false;
         int clusterExtraGlyphs = 0;
 
         minIndex = indices[gx];
         maxIndex = minIndex;
 
+        cposl = glyphinfo[gp + posx];
+        cposr = cposl + glyphinfo[gp + advx];
+        cvisl = glyphinfo[gp + visx];
+        cvist = glyphinfo[gp + visy];
+        cvisr = cvisl + glyphinfo[gp + visw];
+        cvisb = cvist + glyphinfo[gp + vish];
+
         // advance to next glyph
         gx += xdelta;
         gp += pdelta;
 
- /*
-        while (gx != gxlimit && (glyphinfo[gp + advx] == 0 ||
-                           minIndex != nextMin || indices[gx] <= maxIndex)) {
-  */
         while (gx != gxlimit &&
                ((glyphinfo[gp + advx] == 0) ||
-               (minIndex != nextMin) ||
                (indices[gx] <= maxIndex) ||
                (maxIndex - minIndex > clusterExtraGlyphs))) {
-            // initialize base data first time through, using base glyph
-            if (!haveCopy) {
-                int gps = gp - pdelta;
 
-                cposl = glyphinfo[gps + posx];
-                cposr = cposl + glyphinfo[gps + advx];
-                cvisl = glyphinfo[gps + visx];
-                cvist = glyphinfo[gps + visy];
-                cvisr = cvisl + glyphinfo[gps + visw];
-                cvisb = cvist + glyphinfo[gps + vish];
-
-                haveCopy = true;
+            ++clusterExtraGlyphs; // have an extra glyph in this cluster
+            if (DEBUG) {
+                System.err.println("gp=" +gp +" adv=" + glyphinfo[gp + advx] +
+                                   " gx="+ gx+ " i[gx]="+indices[gx] +
+                                   " clusterExtraGlyphs="+clusterExtraGlyphs);
             }
 
-            // have an extra glyph in this cluster
-            ++clusterExtraGlyphs;
-
             // adjust advance only if new glyph has non-zero advance
             float radvx = glyphinfo[gp + advx];
             if (radvx != 0) {
@@ -764,110 +751,90 @@
         // done with cluster, gx and gp are set for next glyph
 
         if (DEBUG) {
-            System.out.println("minIndex = " + minIndex + ", maxIndex = " + maxIndex);
+            System.err.println("minIndex = " + minIndex + ", maxIndex = " + maxIndex);
         }
 
-        nextMin = maxIndex + 1;
-
-        // do common character adjustments
-        glyphinfo[cp + posy] = baseline;
-        glyphinfo[cp + advy] = 0;
+        // save adjustments to the base character and do common adjustments.
+        charInfo[cp + posx] = cposl;
+        charInfo[cp + posy] = baseline;
+        charInfo[cp + advx] = cposr - cposl;
+        charInfo[cp + advy] = 0;
+        charInfo[cp + visx] = cvisl;
+        charInfo[cp + visy] = cvist;
+        charInfo[cp + visw] = cvisr - cvisl;
+        charInfo[cp + vish] = cvisb - cvist;
+        cc++;
 
-        if (haveCopy) {
-            // save adjustments to the base character
-            glyphinfo[cp + posx] = cposl;
-            glyphinfo[cp + advx] = cposr - cposl;
-            glyphinfo[cp + visx] = cvisl;
-            glyphinfo[cp + visy] = cvist;
-            glyphinfo[cp + visw] = cvisr - cvisl;
-            glyphinfo[cp + vish] = cvisb - cvist;
+        /* We may have consumed multiple glyphs for this char position.
+         * Map those extra consumed glyphs to char positions that would follow
+         * up to the index prior to that which begins the next cluster.
+         * If we have reached the last glyph (reached gxlimit) then we need to
+         * map remaining unmapped chars to the same location as the last one.
+         */
+        int tgt;
+        if (gx == gxlimit) {
+           tgt = charInfo.length / numvals;
+        } else {
+           tgt = indices[gx]-1;
+        }
+        if (DEBUG) {
+           System.err.println("gx=" + gx + " gxlimit=" + gxlimit +
+                              " charInfo.len=" + charInfo.length +
+                              " tgt=" + tgt + " cc=" + cc + " cp=" + cp);
+        }
+        while (cc < tgt) {
+            if (rtl) {
+                // if rtl, characters to left of base, else to right.  reuse cposr.
+                cposr = cposl;
+            }
+            cvisr -= cvisl; // reuse, convert to deltas.
+            cvisb -= cvist;
 
-            // compare number of chars read with number of glyphs read.
-            // if more glyphs than chars, set mustCopy to true, as we'll always have
-            // to copy the data from here on out.
-            if (maxIndex - minIndex < clusterExtraGlyphs) {
-                mustCopy = true;
+            cp += pdelta;
+
+            if (cp < 0 || cp >= charInfo.length) {
+                if (DEBUG)  {
+                    System.err.println("Error : cp=" + cp +
+                                       " charInfo.length=" + charInfo.length);
+                }
+                break;
             }
 
-            // Fix the characters that follow the base character.
-            // New values are all the same.  Note we fix the number of characters
-            // we saw, not the number of glyphs we saw.
-            if (minIndex < maxIndex) {
-                if (!ltr) {
-                    // if rtl, characters to left of base, else to right.  reuse cposr.
-                    cposr = cposl;
-                }
-                cvisr -= cvisl; // reuse, convert to deltas.
-                cvisb -= cvist;
-
-                int iMinIndex = minIndex, icp = cp / 8;
-
-                while (minIndex < maxIndex) {
-                    ++minIndex;
-                    cx += xdelta;
-                    cp += pdelta;
-
-                    if (cp < 0 || cp >= glyphinfo.length) {
-                        if (DEBUG) System.out.println("minIndex = " + iMinIndex + ", maxIndex = " + maxIndex + ", cp = " + icp);
-                    }
-
-                    glyphinfo[cp + posx] = cposr;
-                    glyphinfo[cp + posy] = baseline;
-                    glyphinfo[cp + advx] = 0;
-                    glyphinfo[cp + advy] = 0;
-                    glyphinfo[cp + visx] = cvisl;
-                    glyphinfo[cp + visy] = cvist;
-                    glyphinfo[cp + visw] = cvisr;
-                    glyphinfo[cp + vish] = cvisb;
-                }
+            if (DEBUG) {
+                System.err.println("Insert charIndex " + cc + " at pos="+cp);
             }
-
-            // no longer using this copy
-            haveCopy = false;
-        } else if (mustCopy) {
-            // out of synch, so we have to copy all the time now
-            int gpr = gp - pdelta;
-
-            glyphinfo[cp + posx] = glyphinfo[gpr + posx];
-            glyphinfo[cp + advx] = glyphinfo[gpr + advx];
-            glyphinfo[cp + visx] = glyphinfo[gpr + visx];
-            glyphinfo[cp + visy] = glyphinfo[gpr + visy];
-            glyphinfo[cp + visw] = glyphinfo[gpr + visw];
-            glyphinfo[cp + vish] = glyphinfo[gpr + vish];
+            charInfo[cp + posx] = cposr;
+            charInfo[cp + posy] = baseline;
+            charInfo[cp + advx] = 0;
+            charInfo[cp + advy] = 0;
+            charInfo[cp + visx] = cvisl;
+            charInfo[cp + visy] = cvist;
+            charInfo[cp + visw] = cvisr;
+            charInfo[cp + vish] = cvisb;
+            cc++;
         }
-        // else glyphinfo is already at the correct character position, and is unchanged, so just leave it
-
-        // reset for new cluster
-        cp += pdelta;
-        cx += xdelta;
-    }
-
-    if (mustCopy && !ltr) {
-        // data written to wrong end of array, need to shift down
-
-        cp -= pdelta; // undo last increment, get start of valid character data in array
-        System.arraycopy(glyphinfo, cp, glyphinfo, 0, glyphinfo.length - cp);
+        cp += pdelta; // reset for new cluster
     }
 
     if (DEBUG) {
-      char[] chars = source.getChars();
-      int start = source.getStart();
-      int length = source.getLength();
-      System.out.println("char info for " + length + " characters");
-      for(int i = 0; i < length * numvals;) {
-        System.out.println(" ch: " + Integer.toHexString(chars[start + v2l(i / numvals)]) +
-                           " x: " + glyphinfo[i++] +
-                           " y: " + glyphinfo[i++] +
-                           " xa: " + glyphinfo[i++] +
-                           " ya: " + glyphinfo[i++] +
-                           " l: " + glyphinfo[i++] +
-                           " t: " + glyphinfo[i++] +
-                           " w: " + glyphinfo[i++] +
-                           " h: " + glyphinfo[i++]);
+        char[] chars = source.getChars();
+        int start = source.getStart();
+        int length = source.getLength();
+        System.err.println("char info for " + length + " characters");
+
+        for (int i = 0; i < length * numvals;) {
+            System.err.println(" ch: " + Integer.toHexString(chars[start + v2l(i / numvals)]) +
+                               " x: " + charInfo[i++] +
+                               " y: " + charInfo[i++] +
+                               " xa: " + charInfo[i++] +
+                               " ya: " + charInfo[i++] +
+                               " l: " + charInfo[i++] +
+                               " t: " + charInfo[i++] +
+                               " w: " + charInfo[i++] +
+                               " h: " + charInfo[i++]);
       }
     }
-
-    return glyphinfo;
+    return charInfo;
   }
 
   /**
--- a/jdk/src/java.desktop/share/native/libfontmanager/HBShaper.c	Thu Sep 01 16:18:14 2016 +0530
+++ b/jdk/src/java.desktop/share/native/libfontmanager/HBShaper.c	Thu Sep 01 11:29:20 2016 -0700
@@ -40,6 +40,7 @@
 static jfieldID gvdGlyphsFID = 0;
 static jfieldID gvdPositionsFID = 0;
 static jfieldID gvdIndicesFID = 0;
+static jmethodID gvdGrowMID = 0;
 static int jniInited = 0;
 
 static void getFloat(JNIEnv* env, jobject pt, jfloat *x, jfloat *y) {
@@ -63,73 +64,88 @@
     CHECK_NULL_RETURN(gvdGlyphsFID = (*env)->GetFieldID(env, gvdClass, "_glyphs", "[I"), 0);
     CHECK_NULL_RETURN(gvdPositionsFID = (*env)->GetFieldID(env, gvdClass, "_positions", "[F"), 0);
     CHECK_NULL_RETURN(gvdIndicesFID = (*env)->GetFieldID(env, gvdClass, "_indices", "[I"), 0);
+    CHECK_NULL_RETURN(gvdGrowMID = (*env)->GetMethodID(env, gvdClass, "grow", "()V"), 0);
     jniInited = 1;
     return jniInited;
 }
 
 // gmask is the composite font slot mask
 // baseindex is to be added to the character (code point) index.
-int storeGVData(JNIEnv* env,
-               jobject gvdata, jint slot, jint baseIndex, jobject startPt,
-               int glyphCount, hb_glyph_info_t *glyphInfo,
-               hb_glyph_position_t *glyphPos, hb_direction_t direction,
-               float devScale) {
+jboolean storeGVData(JNIEnv* env,
+                     jobject gvdata, jint slot,
+                     jint baseIndex, int offset, jobject startPt,
+                     int charCount, int glyphCount, hb_glyph_info_t *glyphInfo,
+                     hb_glyph_position_t *glyphPos, float devScale) {
 
-    int i;
+    int i, needToGrow;
     float x=0, y=0;
-    float startX, startY;
+    float startX, startY, advX, advY;
     float scale = 1.0f / FloatToFixedScale / devScale;
     unsigned int* glyphs;
     float* positions;
-    int initialCount, glyphArrayLen, posArrayLen, maxGlyphs, storeadv;
+    int initialCount, glyphArrayLen, posArrayLen, maxGlyphs, storeadv, maxStore;
     unsigned int* indices;
     jarray glyphArray, posArray, inxArray;
 
     if (!init_JNI_IDs(env)) {
-        return 0;
+        return JNI_FALSE;
     }
 
     initialCount = (*env)->GetIntField(env, gvdata, gvdCountFID);
-    glyphArray =
-       (jarray)(*env)->GetObjectField(env, gvdata, gvdGlyphsFID);
-    posArray =
-        (jarray)(*env)->GetObjectField(env, gvdata, gvdPositionsFID);
-
-    if (glyphArray == NULL || posArray == NULL)
-    {
-        JNU_ThrowArrayIndexOutOfBoundsException(env, "");
-        return 0;
-    }
-
-    // The Java code catches the IIOBE and expands the storage
-    // and re-invokes layout. I suppose this is expected to be rare
-    // because at least in a single threaded case there should be
-    // re-use of the same container, but it is a little wasteful/distateful.
-    glyphArrayLen = (*env)->GetArrayLength(env, glyphArray);
-    posArrayLen = (*env)->GetArrayLength(env, posArray);
-    maxGlyphs = glyphCount + initialCount;
-    if ((maxGlyphs >  glyphArrayLen) ||
-        (maxGlyphs * 2 + 2 >  posArrayLen))
-    {
-        JNU_ThrowArrayIndexOutOfBoundsException(env, "");
-        return 0;
-    }
+    do {
+        glyphArray = (jarray)(*env)->GetObjectField(env, gvdata, gvdGlyphsFID);
+        posArray = (jarray)(*env)->GetObjectField(env, gvdata, gvdPositionsFID);
+        inxArray = (jarray)(*env)->GetObjectField(env, gvdata, gvdIndicesFID);
+        if (glyphArray == NULL || posArray == NULL || inxArray == NULL) {
+            JNU_ThrowArrayIndexOutOfBoundsException(env, "");
+            return JNI_FALSE;
+        }
+        glyphArrayLen = (*env)->GetArrayLength(env, glyphArray);
+        posArrayLen = (*env)->GetArrayLength(env, posArray);
+        maxGlyphs = (charCount > glyphCount) ? charCount : glyphCount;
+        maxStore = maxGlyphs + initialCount;
+        needToGrow = (maxStore > glyphArrayLen) ||
+                     (maxStore * 2 + 2 >  posArrayLen);
+        if (needToGrow) {
+            (*env)->CallVoidMethod(env, gvdata, gvdGrowMID);
+            if ((*env)->ExceptionCheck(env)) {
+                return JNI_FALSE;
+            }
+        }
+    } while (needToGrow);
 
     getFloat(env, startPt, &startX, &startY);
 
     glyphs =
         (unsigned int*)(*env)->GetPrimitiveArrayCritical(env, glyphArray, NULL);
+    if (glyphs == NULL) {
+        return JNI_FALSE;
+    }
     positions = (jfloat*)(*env)->GetPrimitiveArrayCritical(env, posArray, NULL);
+    if (positions == NULL) {
+        (*env)->ReleasePrimitiveArrayCritical(env, glyphArray, glyphs, 0);
+        return JNI_FALSE;
+    }
+    indices =
+        (unsigned int*)(*env)->GetPrimitiveArrayCritical(env, inxArray, NULL);
+    if (indices == NULL) {
+        (*env)->ReleasePrimitiveArrayCritical(env, glyphArray, glyphs, 0);
+        (*env)->ReleasePrimitiveArrayCritical(env, posArray, positions, 0);
+        return JNI_FALSE;
+    }
+
     for (i = 0; i < glyphCount; i++) {
         int storei = i + initialCount;
-        int index = glyphInfo[i].codepoint | slot;
-        if (i<glyphCount)glyphs[storei] = (unsigned int)index;
-        positions[(storei*2)] = startX + x + glyphPos[i].x_offset * scale;
-        positions[(storei*2)+1] = startY + y - glyphPos[i].y_offset * scale;
+        int cluster = glyphInfo[i].cluster - offset;
+        indices[storei] = baseIndex + cluster;
+        glyphs[storei] = (unsigned int)(glyphInfo[i].codepoint | slot);
+        positions[storei*2] = startX + x + glyphPos[i].x_offset * scale;
+        positions[(storei*2)+1] = startY + y + glyphPos[i].y_offset * scale;
         x += glyphPos[i].x_advance * scale;
         y += glyphPos[i].y_advance * scale;
+        storei++;
     }
-    storeadv = initialCount+glyphCount;
+    storeadv = initialCount + glyphCount;
     // The final slot in the positions array is important
     // because when the GlyphVector is created from this
     // data it determines the overall advance of the glyphvector
@@ -137,30 +153,17 @@
     // during rendering where text is broken into runs.
     // We also need to report it back into "pt", so layout can
     // pass it back down for that next run in this code.
-    positions[(storeadv*2)] = startX + x;
-    positions[(storeadv*2)+1] = startY + y;
+    advX = startX + x;
+    advY = startY + y;
+    positions[(storeadv*2)] = advX;
+    positions[(storeadv*2)+1] = advY;
     (*env)->ReleasePrimitiveArrayCritical(env, glyphArray, glyphs, 0);
     (*env)->ReleasePrimitiveArrayCritical(env, posArray, positions, 0);
-    putFloat(env, startPt,positions[(storeadv*2)],positions[(storeadv*2)+1] );
-    inxArray =
-        (jarray)(*env)->GetObjectField(env, gvdata, gvdIndicesFID);
-    indices =
-        (unsigned int*)(*env)->GetPrimitiveArrayCritical(env, inxArray, NULL);
-    for (i = 0; i < glyphCount; i++) {
-        int cluster = glyphInfo[i].cluster;
-        if (direction == HB_DIRECTION_LTR) {
-            // I need to understand what hb does when processing a substring
-            // I expected the cluster index to be from the start of the text
-            // to process.
-            // Instead it appears to be from the start of the whole thing.
-            indices[i+initialCount] = cluster;
-        } else {
-            indices[i+initialCount] = baseIndex + glyphCount -1 -i;
-        }
-    }
     (*env)->ReleasePrimitiveArrayCritical(env, inxArray, indices, 0);
-    (*env)->SetIntField(env, gvdata, gvdCountFID, initialCount+glyphCount);
-    return initialCount+glyphCount;
+    putFloat(env, startPt, advX, advY);
+    (*env)->SetIntField(env, gvdata, gvdCountFID, storeadv);
+
+    return JNI_TRUE;
 }
 
 static float euclidianDistance(float a, float b)
@@ -226,7 +229,9 @@
 }
 
 
-#define TYPO_RTL 0x80000000
+#define TYPO_KERN 0x00000001
+#define TYPO_LIGA 0x00000002
+#define TYPO_RTL  0x80000000
 
 JNIEXPORT jboolean JNICALL Java_sun_font_SunLayoutEngine_shape
     (JNIEnv *env, jclass cls,
@@ -255,10 +260,11 @@
      hb_glyph_info_t *glyphInfo;
      hb_glyph_position_t *glyphPos;
      hb_direction_t direction = HB_DIRECTION_LTR;
-     hb_feature_t *features =  NULL;
+     hb_feature_t *features = NULL;
      int featureCount = 0;
-
-     int i;
+     char* kern = (flags & TYPO_KERN) ? "kern" : "-kern";
+     char* liga = (flags & TYPO_LIGA) ? "liga" : "-liga";
+     jboolean ret;
      unsigned int buflen;
 
      JDKFontInfo *jdkFontInfo =
@@ -281,6 +287,8 @@
          direction = HB_DIRECTION_RTL;
      }
      hb_buffer_set_direction(buffer, direction);
+     hb_buffer_set_cluster_level(buffer,
+                                 HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS);
 
      chars = (*env)->GetCharArrayElements(env, text, NULL);
      if ((*env)->ExceptionCheck(env)) {
@@ -293,36 +301,26 @@
 
      hb_buffer_add_utf16(buffer, chars, len, offset, limit-offset);
 
+     features = calloc(2, sizeof(hb_feature_t));
+     if (features) {
+         hb_feature_from_string(kern, -1, &features[featureCount++]);
+         hb_feature_from_string(liga, -1, &features[featureCount++]);
+     }
+
      hb_shape_full(hbfont, buffer, features, featureCount, 0);
      glyphCount = hb_buffer_get_length(buffer);
      glyphInfo = hb_buffer_get_glyph_infos(buffer, 0);
      glyphPos = hb_buffer_get_glyph_positions(buffer, &buflen);
-     for (i = 0; i < glyphCount; i++) {
-         int index = glyphInfo[i].codepoint;
-         int xadv = (glyphPos[i].x_advance);
-         int yadv = (glyphPos[i].y_advance);
-     }
-     // On "input" HB assigns a cluster index to each character in UTF-16.
-     // On output where a sequence of characters have been mapped to
-     // a glyph they are all mapped to the cluster index of the first character.
-     // The next cluster index will be that of the first character in the
-     // next cluster. So cluster indexes may 'skip' on output.
-     // This can also happen if there are supplementary code-points
-     // such that two UTF-16 characters are needed to make one codepoint.
-     // In RTL text you need to count down.
-     // So the following code tries to build the reverse map as expected
-     // by calling code.
 
-     storeGVData(env, gvdata, slot, baseIndex, startPt,
-                 glyphCount, glyphInfo, glyphPos, direction,
-                 jdkFontInfo->devScale);
+     ret = storeGVData(env, gvdata, slot, baseIndex, offset, startPt,
+                       limit - offset, glyphCount, glyphInfo, glyphPos,
+                       jdkFontInfo->devScale);
 
      hb_buffer_destroy (buffer);
      hb_font_destroy(hbfont);
      free((void*)jdkFontInfo);
      if (features != NULL) free(features);
      (*env)->ReleaseCharArrayElements(env, text, chars, JNI_ABORT);
-
-     return JNI_TRUE;
+     return ret;
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/font/GlyphVector/GetGlyphCharIndexTest.java	Thu Sep 01 11:29:20 2016 -0700
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @summary Test getGlyphCharIndex() results from layout
+ * @bug 8152680
+ *
+
+import java.awt.Font;
+import java.awt.font.FontRenderContext;
+import java.awt.font.GlyphVector;
+
+public class GetGlyphCharIndexTest {
+    public static void main(String[] args) {
+        Font font = new Font(Font.MONOSPACED, Font.PLAIN, 12);
+        FontRenderContext frc = new FontRenderContext(null, false, false);
+        GlyphVector gv = font.layoutGlyphVector(frc, "abc".toCharArray(), 1, 3,
+                                                Font.LAYOUT_LEFT_TO_RIGHT);
+        int idx0 = gv.getGlyphCharIndex(0);
+        if (idx0 != 0) {
+           throw new RuntimeException("Expected 0, got " + idx0);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/font/LineBreakMeasurer/TestLineBreakWithFontSub.java	Thu Sep 01 11:29:20 2016 -0700
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 4175418 8158924
+ * @author John Raley
+ * @summary This insures that bug 4175418: Font substitution in TextLayout /
+ * LineBreakMeasurer is inconsistent has been fixed.  The problem was
+ * that text was measured in one Font, but lines were produced
+ * in a different font.
+ */
+
+/*
+ * (C) Copyright IBM Corp. 1999, All Rights Reserved
+ */
+
+import java.text.AttributedString;
+import java.awt.font.LineBreakMeasurer;
+import java.awt.font.TextLayout;
+import java.awt.font.FontRenderContext;
+import java.awt.font.TextAttribute;
+
+/**
+ * This insures that bug 4175418: Font substitution in TextLayout /
+ * LineBreakMeasurer is inconsistent has been fixed.  The problem was
+ * that text was measured in one Font, but lines were produced
+ * in a different font.  One symptom of this problem is that lines are
+ * either too short or too long.  This test line-breaks a paragraph
+ * and checks the line lengths to make sure breaks were chosen well.
+ * This can be checked because the paragraph is so simple.
+ */
+public class TestLineBreakWithFontSub {
+
+    public static void main(String[] args) {
+
+        new TestLineBreakWithFontSub().test();
+        System.out.println("Line break / font substitution test PASSED");
+    }
+
+    private static final String WORD = "word";
+    private static final String SPACING = " ";
+    // The Hebrew character in this string can trigger font substitution
+    private static final String MIXED = "A\u05D0";
+
+    private static final int NUM_WORDS = 12;
+
+    private static final FontRenderContext DEFAULT_FRC =
+                            new FontRenderContext(null, false, false);
+
+    public void test() {
+
+        // construct a paragraph as follows: MIXED + [SPACING + WORD] + ...
+        StringBuffer text = new StringBuffer(MIXED);
+        for (int i=0; i < NUM_WORDS; i++) {
+            text.append(SPACING);
+            text.append(WORD);
+        }
+
+        AttributedString attrString = new AttributedString(text.toString());
+        attrString.addAttribute(TextAttribute.SIZE, new Float(24.0));
+
+        LineBreakMeasurer measurer = new LineBreakMeasurer(attrString.getIterator(),
+                                                           DEFAULT_FRC);
+
+        // get width of a space-word sequence, in context
+        int sequenceLength = WORD.length()+SPACING.length();
+        measurer.setPosition(text.length() - sequenceLength);
+
+        TextLayout layout = measurer.nextLayout(10000.0f);
+
+        if (layout.getCharacterCount() != sequenceLength) {
+            throw new Error("layout length is incorrect!");
+        }
+
+        final float sequenceAdvance = layout.getVisibleAdvance();
+
+        float wrappingWidth = sequenceAdvance * 2;
+
+        // now run test with a variety of widths
+        while (wrappingWidth < (sequenceAdvance*NUM_WORDS)) {
+            measurer.setPosition(0);
+            checkMeasurer(measurer,
+                          wrappingWidth,
+                          sequenceAdvance,
+                          text.length());
+            wrappingWidth += sequenceAdvance / 5;
+        }
+    }
+
+    /**
+     * Iterate through measurer and check that every line is
+     * not too long and not too short, but just right.
+     */
+    private void checkMeasurer(LineBreakMeasurer measurer,
+                               float wrappingWidth,
+                               float sequenceAdvance,
+                               int endPosition) {
+
+        do {
+            TextLayout layout = measurer.nextLayout(wrappingWidth);
+            float visAdvance = layout.getVisibleAdvance();
+
+            // Check that wrappingWidth-sequenceAdvance < visAdvance
+            // Also, if we're not at the end of the paragraph,
+            // check that visAdvance <= wrappingWidth
+
+            if (visAdvance > wrappingWidth) {
+                // line is too long for given wrapping width
+                throw new Error("layout is too long");
+            }
+
+            if (measurer.getPosition() < endPosition) {
+                if (visAdvance <= wrappingWidth - sequenceAdvance) {
+                    // line is too short for given wrapping width;
+                    // another word would have fit
+                    throw new Error("room for more words on line.  diff=" +
+                                    (wrappingWidth - sequenceAdvance - visAdvance));
+                }
+            }
+        } while (measurer.getPosition() != endPosition);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/font/TextLayout/LigatureCaretTest.java	Thu Sep 01 11:29:20 2016 -0700
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ *  @test
+ *  @bug 4178145 8144015
+*/
+
+/*
+ * Copyright 1998 IBM Corp.  All Rights Reserved.
+ */
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Frame;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.font.TextAttribute;
+import java.awt.font.TextLayout;
+import java.awt.font.TextHitInfo;
+import java.awt.font.FontRenderContext;
+import java.util.Hashtable;
+
+/**
+ * This test ensures that TextLayout will not place a caret within
+ * an Arabic lam-alef ligature, and will correctly caret through
+ * bidirectional text with numbers.
+ */
+
+public class LigatureCaretTest {
+
+    public static void main(String[] args) {
+
+        //testBidiWithNumbers();
+        testLamAlef();
+        System.out.println("LigatureCaretTest PASSED");
+    }
+
+    // These values are for TextLayout constructors
+    private static final Hashtable map = new Hashtable();
+    static {
+      map.put(TextAttribute.FONT, new Font("Lucida Sans", Font.PLAIN, 24));
+    }
+    private static final FontRenderContext frc =
+                                new FontRenderContext(null, false, false);
+
+    /**
+     * Caret through text mixed-direction text and check the results.
+     * If the test fails an Error is thrown.
+     * @exception an Error is thrown if the test fails
+     */
+    public static void testBidiWithNumbers() {
+
+        String bidiWithNumbers = "abc\u05D0\u05D1\u05D2123abc";
+        // visual order for the text:
+        // abc123<gimel><bet><aleph>abc
+
+        int[] carets = { 0, 1, 2, 3, 7, 8, 6, 5, 4, 9, 10, 11, 12 };
+        TextLayout layout = new TextLayout(bidiWithNumbers, map, frc);
+
+        // Caret through TextLayout in both directions and check results.
+        for (int i=0; i < carets.length-1; i++) {
+
+            TextHitInfo hit = layout.getNextRightHit(carets[i]);
+            if (hit.getInsertionIndex() != carets[i+1]) {
+                throw new Error("right hit failed within layout");
+            }
+        }
+
+        if (layout.getNextRightHit(carets[carets.length-1]) != null) {
+            throw new Error("right hit failed at end of layout");
+        }
+
+        for (int i=carets.length-1; i > 0; i--) {
+
+            TextHitInfo hit = layout.getNextLeftHit(carets[i]);
+            if (hit.getInsertionIndex() != carets[i-1]) {
+                throw new Error("left hit failed within layout");
+            }
+        }
+
+        if (layout.getNextLeftHit(carets[0]) != null) {
+            throw new Error("left hit failed at end of layout");
+        }
+    }
+
+    /**
+     * Ensure proper careting and hit-testing behavior with
+     * a lam-alef ligature.
+     * If the test fails, an Error is thrown.
+     * @exception an Error is thrown if the test fails
+     */
+    public static void testLamAlef() {
+
+        // lam-alef form a mandantory ligature.
+        final String lamAlef = "\u0644\u0627";
+        final String ltrText = "abcd";
+
+        // Create a TextLayout with just a lam-alef sequence.  There
+        // should only be two valid caret positions:  one at
+        // insertion offset 0 and the other at insertion offset 2.
+        TextLayout layout = new TextLayout(lamAlef, map, frc);
+
+        TextHitInfo hit;
+
+        hit = layout.getNextLeftHit(0);
+        if (hit.getInsertionIndex() != 2) {
+            throw new Error("Left hit failed.  Hit:" + hit);
+        }
+
+        hit = layout.getNextRightHit(2);
+        if (hit.getInsertionIndex() != 0) {
+            throw new Error("Right hit failed.  Hit:" + hit);
+        }
+
+        hit = layout.hitTestChar(layout.getAdvance()/2, 0);
+        if (hit.getInsertionIndex() != 0 && hit.getInsertionIndex() != 2) {
+            throw new Error("Hit-test allowed incorrect caret.  Hit:" + hit);
+        }
+
+
+        // Create a TextLayout with some left-to-right text
+        // before the lam-alef sequence.  There should not be
+        // a caret position between the lam and alef.
+        layout = new TextLayout(ltrText+lamAlef, map, frc);
+
+        final int ltrLen = ltrText.length();
+        final int layoutLen = layout.getCharacterCount();
+
+        for (int i=0; i < ltrLen; i++) {
+            hit = layout.getNextRightHit(i);
+            if (hit.getInsertionIndex() != i+1) {
+                throw new Error("Right hit failed in ltr text.");
+            }
+        }
+
+        hit = layout.getNextRightHit(ltrLen);
+        if (layoutLen != hit.getInsertionIndex()) {
+            throw new Error("Right hit failed at direction boundary.");
+        }
+
+        hit = layout.getNextLeftHit(layoutLen);
+        if (hit.getInsertionIndex() != ltrLen) {
+            throw new Error("Left hit failed at end of text.");
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/font/TextLayout/TestJustification.html	Thu Sep 01 11:29:20 2016 -0700
@@ -0,0 +1,52 @@
+<!--
+ Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved.
+ DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+
+ This code is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License version 2 only, as
+ published by the Free Software Foundation.
+ 
+ This code is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ version 2 for more details (a copy is included in the LICENSE file that
+ accompanied this code).
+ 
+ You should have received a copy of the GNU General Public License version
+ 2 along with this work; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ 
+ Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ or visit www.oracle.com if you need additional information or have any
+ questions.
+--> 
+
+<html>
+<head>
+<title>Test Justification</title>
+</head>
+<body>
+<!--
+ @test
+ @bug 4211728 4178140 8145542
+ @summary Justify several lines of text and verify that the lines are the same
+ length and cursor positions are correct.
+Bug 4211728:  TextLayout.draw() draws characters at wrong position.
+Bug 4178140:  TextLayout does not justify
+ @run applet/manual=yesno TestJustification.html
+-->
+<h3>Test Justification</h1>
+<hr>
+<p>Five lines of text should appear, all justified to the same width,
+followed by a sixth line containing only roman characters and no spaces
+which is not justified, and instead is centered.
+Carets should appear between all characters. Pass the test if this is
+true.
+<p>
+<applet code=TestJustification.class width=500 height=500>
+alt="Your browser understands the &lt;APPLET&gt; tag but isn't running the applet, for some reason."
+Your browser is completely ignoring the &lt;APPLET&gt; tag!
+</applet>
+</body>
+</html>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/font/TextLayout/TestJustification.java	Thu Sep 01 11:29:20 2016 -0700
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ *
+ * See TestJustification.html for main test.
+ */
+
+import java.applet.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.awt.font.*;
+import java.awt.geom.*;
+import java.text.*;
+
+public class TestJustification extends Applet {
+  JustificationPanel panel;
+
+  public void init() {
+    setLayout(new BorderLayout());
+    panel = new JustificationPanel("Bitstream Cyberbit");
+    add("Center", panel);
+  }
+
+  public void destroy() {
+    remove(panel);
+  }
+
+  // calls system.exit, not for use in tests.
+  public static void main(String args[]) {
+    TestJustification justificationTest = new TestJustification();
+    justificationTest.init();
+    justificationTest.start();
+
+    Frame f = new Frame("Test Justification");
+    f.addWindowListener(new WindowAdapter() {
+      public void windowClosing(WindowEvent e) {
+        System.exit(0);
+      }
+    });
+
+    f.add("Center", justificationTest);
+    f.setSize(500, 500);
+    f.show();
+  }
+
+  public String getAppletInfo() {
+    return "Test TextLayout.getJustifiedLayout()";
+  }
+
+  static class JustificationPanel extends Panel {
+    TextLayout[] layouts;
+    String fontname;
+    float height;
+    float oldfsize;
+
+    AttributedCharacterIterator lineText;
+    TextLayout[] lines;
+    int linecount;
+    float oldwidth;
+
+    JustificationPanel(String fontname) {
+      this.fontname = fontname;
+    }
+
+    private static final String[] texts = {
+      "This is an english Highlighting demo.", "Highlighting",
+      "This is an arabic \u0627\u0628\u062a\u062c \u062e\u0644\u0627\u062e demo.", "arabic \u0627\u0628\u062a\u062c",
+      "This is a hebrew \u05d0\u05d1\u05d2 \u05d3\u05d4\u05d5 demo.", "hebrew \u05d0\u05d1\u05d2",
+      "This is a cjk \u4e00\u4e01\u4e02\uac00\uac01\uc4fa\uf900\uf901\uf902 demo.", "cjk",
+      "NoSpaceCJK:\u4e00\u4e01\u4e02and\uac00\uac01\uc4faand\uf900\uf901\uf902", "No",
+      "NoSpaceRoman", "Space"
+    };
+
+    public void paint(Graphics g) {
+      Graphics2D g2d = (Graphics2D)g;
+
+      Dimension d = getSize();
+      Insets insets = getInsets();
+
+      float w = d.width - insets.left - insets.right;
+      float h = d.height - insets.top - insets.bottom;
+      int fsize = (int)w/25;
+
+      FontRenderContext frc = g2d.getFontRenderContext();
+
+      if (layouts == null || fsize != oldfsize) {
+        oldfsize = fsize;
+
+        Font f0 = new Font(fontname, Font.PLAIN, fsize);
+        Font f1 = new Font(fontname, Font.ITALIC, (int)(fsize * 1.5));
+
+        if (layouts == null) {
+          layouts = new TextLayout[texts.length / 2];
+        }
+
+        height = 0;
+        for (int i = 0; i < layouts.length; ++i) {
+          String text = texts[i*2];
+          String target = texts[i*2+1];
+
+          AttributedString astr = new AttributedString(text);
+          astr.addAttribute(TextAttribute.FONT, f0, 0, text.length());
+
+          int start = text.indexOf(target);
+          int limit = start + target.length();
+          astr.addAttribute(TextAttribute.FONT, f1, start, limit);
+
+          TextLayout layout = new TextLayout(astr.getIterator(), frc);
+
+          layout = layout.getJustifiedLayout(w - 20);
+
+          layouts[i] = layout;
+
+          height += layout.getAscent() + layout.getDescent() + layout.getLeading();
+        }
+      }
+
+      g2d.setColor(Color.white);
+      g2d.fill(new Rectangle.Float(insets.left, insets.top, w, h));
+
+      float basey = 20;
+
+      for (int i = 0; i < layouts.length; ++i) {
+        TextLayout layout = layouts[i];
+
+        float la = layout.getAscent();
+        float ld = layout.getDescent();
+        float ll = layout.getLeading();
+        float lw = layout.getAdvance();
+        float lh = la + ld + ll;
+        float lx = (w - lw) / 2f;
+        float ly = basey + layout.getAscent();
+
+        g2d.setColor(Color.black);
+        g2d.translate(insets.left + lx, insets.top + ly);
+
+        Rectangle2D bounds = new Rectangle2D.Float(0, -la, lw, lh);
+        g2d.draw(bounds);
+
+        layout.draw(g2d, 0, 0);
+
+        g2d.setColor(Color.red);
+        for (int j = 0, e = layout.getCharacterCount(); j <= e; ++j) {
+          Shape[] carets = layout.getCaretShapes(j, bounds);
+          g2d.draw(carets[0]);
+        }
+
+        g2d.translate(-insets.left - lx, -insets.top - ly);
+        basey += layout.getAscent() + layout.getDescent() + layout.getLeading();
+      }
+
+      // add LineBreakMeasurer-generated layouts
+
+      if (lineText == null) {
+        String text = "This is a long line of text that should be broken across multiple "
+          + "lines and then justified to fit the break width.  This test should pass if "
+          + "these lines are justified to the same width, and fail otherwise.  It should "
+          + "also format the hebrew (\u05d0\u05d1\u05d2 \u05d3\u05d4\u05d5) and arabic "
+          + "(\u0627\u0628\u062a\u062c \u062e\u0644\u0627\u062e) and CJK "
+          + "(\u4e00\u4e01\u4e02\uac00\uac01\uc4fa\u67b1\u67b2\u67b3\u67b4\u67b5\u67b6\u67b7"
+          + "\u67b8\u67b9) text correctly.";
+
+        Float regular = new Float(16.0);
+        Float big = new Float(24.0);
+        AttributedString astr = new AttributedString(text);
+        astr.addAttribute(TextAttribute.SIZE, regular, 0, text.length());
+        astr.addAttribute(TextAttribute.FAMILY, fontname, 0, text.length());
+
+        int ix = text.indexOf("broken");
+        astr.addAttribute(TextAttribute.SIZE, big, ix, ix + 6);
+        ix = text.indexOf("hebrew");
+        astr.addAttribute(TextAttribute.SIZE, big, ix, ix + 6);
+        ix = text.indexOf("arabic");
+        astr.addAttribute(TextAttribute.SIZE, big, ix, ix + 6);
+        ix = text.indexOf("CJK");
+        astr.addAttribute(TextAttribute.SIZE, big, ix, ix + 3);
+
+        lineText = astr.getIterator();
+      }
+
+      float width = w - 20;
+      if (lines == null || width != oldwidth) {
+        oldwidth = width;
+
+        lines = new TextLayout[10];
+        linecount = 0;
+
+        LineBreakMeasurer measurer = new LineBreakMeasurer(lineText, frc);
+
+        for (;;) {
+          TextLayout layout = measurer.nextLayout(width);
+          if (layout == null) {
+            break;
+          }
+
+          // justify all but last line
+          if (linecount > 0) {
+            lines[linecount - 1] = lines[linecount - 1].getJustifiedLayout(width);
+          }
+
+          if (linecount == lines.length) {
+            TextLayout[] nlines = new TextLayout[lines.length * 2];
+            System.arraycopy(lines, 0, nlines, 0, lines.length);
+            lines = nlines;
+          }
+
+          lines[linecount++] = layout;
+        }
+      }
+
+      float basex = insets.left + 10;
+      basey += 10;
+      g2d.setColor(Color.black);
+
+      for (int i = 0; i < linecount; ++i) {
+        TextLayout layout = lines[i];
+
+        basey += layout.getAscent();
+        float adv = layout.getAdvance();
+        float dx = layout.isLeftToRight() ? 0 : width - adv;
+
+        layout.draw(g2d, basex + dx, basey);
+
+        basey += layout.getDescent() + layout.getLeading();
+      }
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/swing/text/DevanagariEditor.java	Thu Sep 01 11:29:20 2016 -0700
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8151725
+ * @summary Tests no exception creating a JEditorPane with Devanagari
+ */
+
+import javax.swing.JEditorPane;
+
+public class DevanagariEditor {
+    public static void main(String[] args) {
+        new JEditorPane().setText("\u0930\u093E\u0915\u094D\u0937\u0938");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/swing/text/GlyphPainter2/6427244/bug6427244.java	Thu Sep 01 11:29:20 2016 -0700
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/* @test
+   @bug 6427244 8144240
+   @summary Test that pressing HOME correctly moves caret in I18N document.
+   @author Sergey Groznyh
+   @library ../../../regtesthelpers
+   @build JRobot Util TestCase
+   @run main bug6427244
+*/
+
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Point;
+import java.awt.Shape;
+import java.awt.event.KeyEvent;
+import javax.swing.JFrame;
+import javax.swing.JTextPane;
+import javax.swing.SwingUtilities;
+import javax.swing.text.Position;
+
+public class bug6427244 extends TestCase {
+    private static final JRobot ROBOT = JRobot.getRobot();
+
+    final static int TP_SIZE = 200;
+    final static String[] SPACES = new String[] {
+        "\u0020", // ASCII space
+        "\u2002", // EN space
+        "\u2003", // EM space
+        "\u2004", // THREE-PER-EM space
+        "\u2005", // ... etc.
+        "\u2006",
+        //"\u2007",
+        "\u2008",
+        "\u2009",
+        "\u200a",
+        "\u200b",
+        "\u205f",
+        "\u3000",
+    };
+    final static String[] WORDS = new String[] {
+        "It", "is", "a", "long", "paragraph", "for", "testing", "GlyphPainter2\n\n",
+    };
+
+    public static void main(String[] args) {
+        bug6427244 t = new bug6427244();
+        for (String space: SPACES) {
+            t.init(space);
+            t.runAllTests();
+        }
+
+        System.out.println("OK");
+    }
+
+    void init(final String space) {
+        try {
+            SwingUtilities.invokeAndWait(new Runnable() {
+                public void run() {
+                    String text = null;
+                    for (String word: WORDS) {
+                        if (text == null) {
+                            text = "";
+                        } else {
+                            text += space;
+                        }
+                        text += word;
+                    }
+                    tp = new JTextPane();
+                    tp.setText(text +
+                            "Some arabic: \u062a\u0641\u0627\u062d and some not.");
+                    if (jf == null) {
+                        jf = new JFrame();
+                        jf.setTitle("bug6427244");
+                        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+                        jf.setSize(TP_SIZE, TP_SIZE);
+                        jf.setVisible(true);
+                    }
+                    Container c = jf.getContentPane();
+                    c.removeAll();
+                    c.add(tp);
+                    c.invalidate();
+                    c.validate();
+                    dim = c.getSize();
+                }
+            });
+            Util.blockTillDisplayed(tp);
+            ROBOT.waitForIdle();
+        } catch (Exception ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
+    public void testCaretPosition() {
+        Point p = tp.getLocationOnScreen();
+        // the right-top corner position
+        p.x += (dim.width - 5);
+        p.y += 5;
+        ROBOT.mouseMove(p.x, p.y);
+        ROBOT.clickMouse();
+        ROBOT.hitKey(KeyEvent.VK_HOME);
+        ROBOT.waitForIdle();
+        // this will fail if caret moves out of the 1st line.
+        assertEquals(0, getCaretOrdinate());
+    }
+
+    int getCaretOrdinate() {
+        final int[] y = new int[1];
+        try {
+            SwingUtilities.invokeAndWait(new Runnable() {
+                public void run() {
+                    Shape s;
+                    try {
+                        s = tp.getUI().getRootView(tp).modelToView(
+                                        tp.getCaretPosition(), tp.getBounds(),
+                                        Position.Bias.Forward);
+                    } catch (Exception e) {
+                        throw new RuntimeException(e);
+                    }
+                    y[0] = s.getBounds().y;
+                }
+            });
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+        return y[0];
+    }
+
+    JFrame jf;
+    JTextPane tp;
+    Dimension dim;
+}