Skip to content

Commit bcff857

Browse files
committed
8361381: GlyphLayout behavior differs on JDK 11+ compared to JDK 8
Reviewed-by: prr, serb
1 parent 03c54d4 commit bcff857

File tree

3 files changed

+136
-8
lines changed

3 files changed

+136
-8
lines changed

src/java.desktop/share/classes/sun/font/ExtendedTextSourceLabel.java

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,7 @@ private float[] getCharinfo() {
577577
* not all do, and it cannot be relied upon.
578578
* - each glyph maps to a single character, when multiple glyphs exist for a character they all map to it, but
579579
* no two characters map to the same glyph
580+
* This was only true for the old, ICU layout engine which inserted 0xffff glyphs for ligaturized characters!
580581
* - multiple glyphs mapping to the same character need not be in sequence (thai, tamil have split characters)
581582
* - glyphs may be arbitrarily reordered (Indic reorders glyphs)
582583
* - all glyphs share the same bidi level
@@ -712,8 +713,6 @@ protected float[] createCharinfo() {
712713

713714
while (gx != gxlimit) {
714715
// start of new cluster
715-
int clusterExtraGlyphs = 0;
716-
717716
minIndex = indices[gx];
718717
maxIndex = minIndex;
719718

@@ -730,14 +729,11 @@ protected float[] createCharinfo() {
730729

731730
while (gx != gxlimit &&
732731
((glyphinfo[gp + advx] == 0) ||
733-
(indices[gx] <= maxIndex) ||
734-
(maxIndex - minIndex > clusterExtraGlyphs))) {
732+
(indices[gx] <= maxIndex))) {
735733

736-
++clusterExtraGlyphs; // have an extra glyph in this cluster
737734
if (DEBUG) {
738735
System.err.println("gp=" +gp +" adv=" + glyphinfo[gp + advx] +
739-
" gx="+ gx+ " i[gx]="+indices[gx] +
740-
" clusterExtraGlyphs="+clusterExtraGlyphs);
736+
" gx="+ gx+ " i[gx]="+indices[gx]);
741737
}
742738

743739
// adjust advance only if new glyph has non-zero advance

test/jdk/java/awt/font/GlyphVector/GetGlyphCharIndexTest.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323

2424
/* @test
2525
* @summary Test getGlyphCharIndex() results from layout
26-
* @bug 8152680
26+
* @bug 8152680 8361381
2727
*/
2828

2929
import java.awt.Font;
@@ -40,5 +40,22 @@ public static void main(String[] args) {
4040
if (idx0 != 0) {
4141
throw new RuntimeException("Expected 0, got " + idx0);
4242
}
43+
44+
// This is the encoding-independent Khmer string "បានស្នើសុំនៅតែត្រូវបានបដិសេធ"
45+
// We can't check for more details like e.g. correct line breaking because it is font and platform dependent,
46+
// but we can at least chack that the created GlyphVector has monotonically increasing character indices.
47+
// This is guaranteed by HarfBuzz's HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS cluster level which is used
48+
// in the OpenJDK layout implementation.
49+
String khmer = "\u1794\u17b6\u1793\u179f\u17d2\u1793\u17be\u179f\u17bb\u17c6\u1793\u17c5" +
50+
"\u178f\u17c2\u178f\u17d2\u179a\u17bc\u179c\u1794\u17b6\u1793\u1794\u178a\u17b7\u179f\u17c1\u1792";
51+
font = new Font(Font.DIALOG, Font.PLAIN, 12);
52+
gv = font.layoutGlyphVector(frc, khmer.toCharArray(), 0, khmer.length(), 0);
53+
int[] indices = gv.getGlyphCharIndices(0, gv.getNumGlyphs(), null);
54+
for (int i = 0; i < (indices.length - 1); i++) {
55+
if (indices[i] > indices[i + 1]) {
56+
throw new RuntimeException("Glyph character indices are supposed to be monotonically growing, but character index at position " +
57+
i + " is bigger then the one at position " + (i + 1) + ", i.e. " + indices[i] + " > " + indices[i + 1] + ".");
58+
}
59+
}
4360
}
4461
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/*
2+
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/**
25+
* @test
26+
* @bug 8361381
27+
* @summary GlyphLayout behavior differs on JDK 11+ compared to JDK 8
28+
*/
29+
30+
import java.awt.*;
31+
import java.awt.font.FontRenderContext;
32+
import java.awt.font.LineBreakMeasurer;
33+
import java.awt.font.TextAttribute;
34+
import java.text.AttributedCharacterIterator;
35+
import java.text.AttributedString;
36+
import java.text.BreakIterator;
37+
import java.util.Locale;
38+
39+
public class KhmerLineBreakTest {
40+
static String khmer = "បានស្នើសុំនៅតែត្រូវបានបដិសេធ";
41+
/*
42+
43+
This is part of the output we get from `ExtendedTextSourceLabel::createCharinfo()`
44+
when running with `-Dsun.java2d.debugfonts=true`. It's a listing of the 28 code points
45+
of the `khmer` string defined above and displays their x-position during rendering as
46+
well as their advance. Code points with zero advance belong to the glyph cluster which
47+
is started by the first preceding code point with a non-zero advance. There should be no
48+
breaks at characters with zero advance, because this would break a glyph cluster.
49+
50+
0 ch: 1794 x: 0.0 xa: 68.115234
51+
1 ch: 17b6 x: 68.115234 xa: 0.0
52+
2 ch: 1793 x: 68.115234 xa: 45.410156
53+
3 ch: 179f x: 113.52539 xa: 90.82031
54+
4 ch: 17d2 x: 204.3457 xa: 0.0
55+
5 ch: 1793 x: 204.3457 xa: 0.0
56+
6 ch: 17be x: 204.3457 xa: 0.0
57+
7 ch: 179f x: 204.3457 xa: 68.115234
58+
8 ch: 17bb x: 272.46094 xa: 0.0
59+
9 ch: 17c6 x: 272.46094 xa: 0.0
60+
10 ch: 1793 x: 272.46094 xa: 90.82031
61+
11 ch: 17c5 x: 363.28125 xa: 0.0
62+
12 ch: 178f x: 363.28125 xa: 68.115234
63+
13 ch: 17c2 x: 431.39648 xa: 0.0
64+
14 ch: 178f x: 431.39648 xa: 68.115234
65+
15 ch: 17d2 x: 499.51172 xa: 0.0
66+
16 ch: 179a x: 499.51172 xa: 0.0
67+
17 ch: 17bc x: 499.51172 xa: 0.0
68+
18 ch: 179c x: 499.51172 xa: 22.705078
69+
19 ch: 1794 x: 522.2168 xa: 68.115234
70+
20 ch: 17b6 x: 590.33203 xa: 0.0
71+
21 ch: 1793 x: 590.33203 xa: 45.410156
72+
22 ch: 1794 x: 635.7422 xa: 45.410156
73+
23 ch: 178a x: 681.15234 xa: 45.410156
74+
24 ch: 17b7 x: 726.5625 xa: 0.0
75+
25 ch: 179f x: 726.5625 xa: 90.82031
76+
26 ch: 17c1 x: 817.3828 xa: 0.0
77+
27 ch: 1792 x: 817.3828 xa: 45.410156
78+
79+
*/
80+
static boolean[] possibleBreak = new boolean[]
81+
{ true, false, true, true, false, false, false, true, false, false,
82+
true, false, true, false, true, false, false, false, true, true,
83+
false, true, true, true, false, true, false, true, true /* */ };
84+
static Locale locale = new Locale.Builder().setLanguage("km").setRegion("KH").build();
85+
static BreakIterator breakIterator = BreakIterator.getLineInstance(locale);
86+
static FontRenderContext frc = new FontRenderContext(null, true, true);
87+
88+
public static void main(String[] args) {
89+
Font[] allFonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
90+
for (int i=0; i < allFonts.length; i++) {
91+
if (allFonts[i].canDisplayUpTo(khmer) == -1) {
92+
Font font = allFonts[i].deriveFont(Font.PLAIN, 60f);
93+
System.out.println("Trying font: " + font.getFontName());
94+
AttributedString attrStr = new AttributedString(khmer);
95+
attrStr.addAttribute(TextAttribute.FONT, font);
96+
AttributedCharacterIterator it = attrStr.getIterator();
97+
for (int width = 200; width < 400; width += 10) {
98+
LineBreakMeasurer measurer = new LineBreakMeasurer(it, breakIterator, frc);
99+
System.out.print(width + " : ");
100+
while (measurer.getPosition() < it.getEndIndex()) {
101+
int nextOffset = measurer.nextOffset(width);
102+
System.out.print(nextOffset + " ");
103+
if (!possibleBreak[nextOffset]) {
104+
System.out.println();
105+
throw new RuntimeException("Invalid break at offset " + nextOffset + " (width = " + width + " font = " + font.getFontName() + ")");
106+
}
107+
measurer.setPosition(nextOffset);
108+
}
109+
System.out.println();
110+
}
111+
System.out.println("OK");
112+
}
113+
}
114+
}
115+
}

0 commit comments

Comments
 (0)