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
--- 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 <APPLET> tag but isn't running the applet, for some reason."
+Your browser is completely ignoring the <APPLET> 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;
+}