mirror of
https://github.com/openjdk/jdk.git
synced 2026-04-13 00:18:59 +00:00
4337267: Arabic Numeral Shaping
Reviewed-by: peterz
This commit is contained in:
parent
840c0d8150
commit
c975650b82
@ -30,6 +30,7 @@ import java.text.AttributedCharacterIterator;
|
||||
import java.text.BreakIterator;
|
||||
import java.awt.font.*;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import sun.font.BidiUtils;
|
||||
|
||||
@ -301,6 +302,13 @@ class TextLayoutStrategy extends FlowView.FlowStrategy {
|
||||
iter = BreakIterator.getLineInstance();
|
||||
}
|
||||
|
||||
Object shaper = null;
|
||||
if (c instanceof JComponent) {
|
||||
shaper = ((JComponent) c).getClientProperty(
|
||||
TextAttribute.NUMERIC_SHAPING);
|
||||
}
|
||||
text.setShaper(shaper);
|
||||
|
||||
measurer = new LineBreakMeasurer(text, iter, frc);
|
||||
|
||||
// If the children of the FlowView's logical view are GlyphViews, they
|
||||
@ -399,6 +407,10 @@ class TextLayoutStrategy extends FlowView.FlowStrategy {
|
||||
return pos - v.getStartOffset() + getBeginIndex();
|
||||
}
|
||||
|
||||
private void setShaper(Object shaper) {
|
||||
this.shaper = shaper;
|
||||
}
|
||||
|
||||
// --- AttributedCharacterIterator methods -------------------------
|
||||
|
||||
/**
|
||||
@ -511,6 +523,8 @@ class TextLayoutStrategy extends FlowView.FlowStrategy {
|
||||
} else if( attribute == TextAttribute.RUN_DIRECTION ) {
|
||||
return
|
||||
v.getDocument().getProperty(TextAttribute.RUN_DIRECTION);
|
||||
} else if (attribute == TextAttribute.NUMERIC_SHAPING) {
|
||||
return shaper;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -532,8 +546,10 @@ class TextLayoutStrategy extends FlowView.FlowStrategy {
|
||||
keys = new HashSet<Attribute>();
|
||||
keys.add(TextAttribute.FONT);
|
||||
keys.add(TextAttribute.RUN_DIRECTION);
|
||||
keys.add(TextAttribute.NUMERIC_SHAPING);
|
||||
}
|
||||
|
||||
private Object shaper = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -192,6 +192,19 @@ public class SwingUtilities2 {
|
||||
fontCache = new LSBCacheEntry[CACHE_SIZE];
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the character buffer cache. Return the buffer length.
|
||||
*/
|
||||
private static int syncCharsBuffer(String s) {
|
||||
int length = s.length();
|
||||
if ((charsBuffer == null) || (charsBuffer.length < length)) {
|
||||
charsBuffer = s.toCharArray();
|
||||
} else {
|
||||
s.getChars(0, length, charsBuffer, 0);
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
/**
|
||||
* checks whether TextLayout is required to handle characters.
|
||||
*
|
||||
@ -353,7 +366,21 @@ public class SwingUtilities2 {
|
||||
if (string == null || string.equals("")) {
|
||||
return 0;
|
||||
}
|
||||
return fm.stringWidth(string);
|
||||
boolean needsTextLayout = ((c != null) &&
|
||||
(c.getClientProperty(TextAttribute.NUMERIC_SHAPING) != null));
|
||||
if (needsTextLayout) {
|
||||
synchronized(charsBufferLock) {
|
||||
int length = syncCharsBuffer(string);
|
||||
needsTextLayout = isComplexLayout(charsBuffer, 0, length);
|
||||
}
|
||||
}
|
||||
if (needsTextLayout) {
|
||||
TextLayout layout = createTextLayout(c, string,
|
||||
fm.getFont(), fm.getFontRenderContext());
|
||||
return (int) layout.getAdvance();
|
||||
} else {
|
||||
return fm.stringWidth(string);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -394,21 +421,11 @@ public class SwingUtilities2 {
|
||||
String string, int availTextWidth) {
|
||||
// c may be null here.
|
||||
String clipString = "...";
|
||||
int stringLength = string.length();
|
||||
availTextWidth -= SwingUtilities2.stringWidth(c, fm, clipString);
|
||||
if (availTextWidth <= 0) {
|
||||
//can not fit any characters
|
||||
return clipString;
|
||||
}
|
||||
|
||||
boolean needsTextLayout;
|
||||
|
||||
synchronized (charsBufferLock) {
|
||||
if (charsBuffer == null || charsBuffer.length < stringLength) {
|
||||
charsBuffer = string.toCharArray();
|
||||
} else {
|
||||
string.getChars(0, stringLength, charsBuffer, 0);
|
||||
}
|
||||
int stringLength = syncCharsBuffer(string);
|
||||
needsTextLayout =
|
||||
isComplexLayout(charsBuffer, 0, stringLength);
|
||||
if (!needsTextLayout) {
|
||||
@ -425,6 +442,10 @@ public class SwingUtilities2 {
|
||||
if (needsTextLayout) {
|
||||
FontRenderContext frc = getFontRenderContext(c, fm);
|
||||
AttributedString aString = new AttributedString(string);
|
||||
if (c != null) {
|
||||
aString.addAttribute(TextAttribute.NUMERIC_SHAPING,
|
||||
c.getClientProperty(TextAttribute.NUMERIC_SHAPING));
|
||||
}
|
||||
LineBreakMeasurer measurer =
|
||||
new LineBreakMeasurer(aString.getIterator(), frc);
|
||||
int nChars = measurer.nextOffset(availTextWidth);
|
||||
@ -465,7 +486,7 @@ public class SwingUtilities2 {
|
||||
*/
|
||||
float screenWidth = (float)
|
||||
g2d.getFont().getStringBounds(text, DEFAULT_FRC).getWidth();
|
||||
TextLayout layout = new TextLayout(text, g2d.getFont(),
|
||||
TextLayout layout = createTextLayout(c, text, g2d.getFont(),
|
||||
g2d.getFontRenderContext());
|
||||
|
||||
layout = layout.getJustifiedLayout(screenWidth);
|
||||
@ -505,7 +526,21 @@ public class SwingUtilities2 {
|
||||
}
|
||||
}
|
||||
|
||||
g.drawString(text, x, y);
|
||||
boolean needsTextLayout = ((c != null) &&
|
||||
(c.getClientProperty(TextAttribute.NUMERIC_SHAPING) != null));
|
||||
if (needsTextLayout) {
|
||||
synchronized(charsBufferLock) {
|
||||
int length = syncCharsBuffer(text);
|
||||
needsTextLayout = isComplexLayout(charsBuffer, 0, length);
|
||||
}
|
||||
}
|
||||
if (needsTextLayout) {
|
||||
TextLayout layout = createTextLayout(c, text, g2.getFont(),
|
||||
g2.getFontRenderContext());
|
||||
layout.draw(g2, x, y);
|
||||
} else {
|
||||
g.drawString(text, x, y);
|
||||
}
|
||||
|
||||
if (oldAAValue != null) {
|
||||
g2.setRenderingHint(KEY_TEXT_ANTIALIASING, oldAAValue);
|
||||
@ -547,11 +582,7 @@ public class SwingUtilities2 {
|
||||
boolean needsTextLayout = isPrinting;
|
||||
if (!needsTextLayout) {
|
||||
synchronized (charsBufferLock) {
|
||||
if (charsBuffer == null || charsBuffer.length < textLength) {
|
||||
charsBuffer = text.toCharArray();
|
||||
} else {
|
||||
text.getChars(0, textLength, charsBuffer, 0);
|
||||
}
|
||||
syncCharsBuffer(text);
|
||||
needsTextLayout =
|
||||
isComplexLayout(charsBuffer, 0, textLength);
|
||||
}
|
||||
@ -567,7 +598,7 @@ public class SwingUtilities2 {
|
||||
Graphics2D g2d = getGraphics2D(g);
|
||||
if (g2d != null) {
|
||||
TextLayout layout =
|
||||
new TextLayout(text, g2d.getFont(),
|
||||
createTextLayout(c, text, g2d.getFont(),
|
||||
g2d.getFontRenderContext());
|
||||
if (isPrinting) {
|
||||
float screenWidth = (float)g2d.getFont().
|
||||
@ -728,7 +759,7 @@ public class SwingUtilities2 {
|
||||
!isFontRenderContextPrintCompatible
|
||||
(deviceFontRenderContext, frc)) {
|
||||
TextLayout layout =
|
||||
new TextLayout(new String(data,offset,length),
|
||||
createTextLayout(c, new String(data, offset, length),
|
||||
g2d.getFont(),
|
||||
deviceFontRenderContext);
|
||||
float screenWidth = (float)g2d.getFont().
|
||||
@ -846,6 +877,20 @@ public class SwingUtilities2 {
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private static TextLayout createTextLayout(JComponent c, String s,
|
||||
Font f, FontRenderContext frc) {
|
||||
Object shaper = (c == null ?
|
||||
null : c.getClientProperty(TextAttribute.NUMERIC_SHAPING));
|
||||
if (shaper == null) {
|
||||
return new TextLayout(s, f, frc);
|
||||
} else {
|
||||
Map<TextAttribute, Object> a = new HashMap<TextAttribute, Object>();
|
||||
a.put(TextAttribute.FONT, f);
|
||||
a.put(TextAttribute.NUMERIC_SHAPING, shaper);
|
||||
return new TextLayout(s, a, frc);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks if two given FontRenderContexts are compatible for printing.
|
||||
* We can't just use equals as we want to exclude from the comparison :
|
||||
|
||||
254
jdk/test/javax/swing/JComponent/4337267/bug4337267.java
Normal file
254
jdk/test/javax/swing/JComponent/4337267/bug4337267.java
Normal file
@ -0,0 +1,254 @@
|
||||
/*
|
||||
* @test
|
||||
* @bug 4337267
|
||||
* @summary test that numeric shaping works in Swing components
|
||||
* @author Sergey Groznyh
|
||||
* @run main bug4337267
|
||||
*/
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.font.NumericShaper;
|
||||
import java.awt.font.TextAttribute;
|
||||
import java.awt.image.BufferedImage;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
public class bug4337267 {
|
||||
TestJPanel p1, p2;
|
||||
TestBufferedImage i1, i2;
|
||||
JComponent[] printq;
|
||||
JFrame window;
|
||||
static boolean testFailed = false;
|
||||
static boolean done = false;
|
||||
|
||||
String shaped =
|
||||
"000 (E) 111 (A) \u0641\u0642\u0643 \u0662\u0662\u0662 (E) 333";
|
||||
String text = "000 (E) 111 (A) \u0641\u0642\u0643 222 (E) 333";
|
||||
|
||||
void run() {
|
||||
initUI();
|
||||
testTextComponent();
|
||||
testNonTextComponentHTML();
|
||||
testNonTextComponentPlain();
|
||||
|
||||
doneTask();
|
||||
}
|
||||
|
||||
void initUI() {
|
||||
window = new JFrame("bug4337267");
|
||||
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
window.setSize(800, 600);
|
||||
Component content = createContentPane();
|
||||
window.add(content);
|
||||
window.setVisible(true);
|
||||
}
|
||||
|
||||
Runnable printComponents = new Runnable() {
|
||||
public void run() {
|
||||
printComponent(printq[0], i1);
|
||||
printComponent(printq[1], i2);
|
||||
}
|
||||
};
|
||||
|
||||
Runnable compareRasters = new Runnable() {
|
||||
public void run() {
|
||||
assertEquals(p1.image, p2.image);
|
||||
assertEquals(i1, i2);
|
||||
}
|
||||
};
|
||||
|
||||
void doneTask() {
|
||||
final Object monitor = this;
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
done = true;
|
||||
synchronized(monitor) {
|
||||
monitor.notify();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void fail(String message) {
|
||||
testFailed = true;
|
||||
throw new RuntimeException(message);
|
||||
}
|
||||
|
||||
void assertEquals(Object o1, Object o2) {
|
||||
if ((o1 == null) && (o2 != null)) {
|
||||
fail("Expected null, got " + o2);
|
||||
} else if ((o1 != null) && (o2 == null)) {
|
||||
fail("Expected " + o1 + ", got null");
|
||||
} else if (!o1.equals(o2)) {
|
||||
fail("Expected " + o1 + ", got " + o2);
|
||||
}
|
||||
}
|
||||
|
||||
void testTextComponent() {
|
||||
System.out.println("testTextComponent:");
|
||||
JTextArea area1 = new JTextArea();
|
||||
injectComponent(p1, area1, false);
|
||||
area1.setText(shaped);
|
||||
JTextArea area2 = new JTextArea();
|
||||
injectComponent(p2, area2, true);
|
||||
area2.setText(text);
|
||||
window.repaint();
|
||||
printq = new JComponent[] { area1, area2 };
|
||||
SwingUtilities.invokeLater(printComponents);
|
||||
SwingUtilities.invokeLater(compareRasters);
|
||||
}
|
||||
|
||||
void testNonTextComponentHTML() {
|
||||
System.out.println("testNonTextComponentHTML:");
|
||||
JLabel label1 = new JLabel();
|
||||
injectComponent(p1, label1, false);
|
||||
label1.setText("<html>" + shaped);
|
||||
JLabel label2 = new JLabel();
|
||||
injectComponent(p2, label2, true);
|
||||
label2.setText("<html>" + text);
|
||||
window.repaint();
|
||||
printq = new JComponent[] { label1, label2 };
|
||||
SwingUtilities.invokeLater(printComponents);
|
||||
SwingUtilities.invokeLater(compareRasters);
|
||||
}
|
||||
|
||||
void testNonTextComponentPlain() {
|
||||
System.out.println("testNonTextComponentHTML:");
|
||||
JLabel label1 = new JLabel();
|
||||
injectComponent(p1, label1, false);
|
||||
label1.setText(shaped);
|
||||
JLabel label2 = new JLabel();
|
||||
injectComponent(p2, label2, true);
|
||||
label2.setText(text);
|
||||
window.repaint();
|
||||
printq = new JComponent[] { label1, label2 };
|
||||
SwingUtilities.invokeLater(printComponents);
|
||||
SwingUtilities.invokeLater(compareRasters);
|
||||
}
|
||||
|
||||
void setShaping(JComponent c) {
|
||||
c.putClientProperty(TextAttribute.NUMERIC_SHAPING,
|
||||
NumericShaper.getContextualShaper(NumericShaper.ARABIC));
|
||||
}
|
||||
|
||||
void injectComponent(JComponent p, JComponent c, boolean shape) {
|
||||
if (shape) {
|
||||
setShaping(c);
|
||||
}
|
||||
p.removeAll();
|
||||
p.add(c);
|
||||
}
|
||||
|
||||
void printComponent(JComponent c, TestBufferedImage i) {
|
||||
Graphics g = i.getGraphics();
|
||||
g.setColor(c.getBackground());
|
||||
g.fillRect(0, 0, i.getWidth(), i.getHeight());
|
||||
c.print(g);
|
||||
}
|
||||
|
||||
Component createContentPane() {
|
||||
Dimension size = new Dimension(500, 100);
|
||||
i1 = new TestBufferedImage(size.width, size.height,
|
||||
BufferedImage.TYPE_INT_ARGB);
|
||||
i2 = new TestBufferedImage(size.width, size.height,
|
||||
BufferedImage.TYPE_INT_ARGB);
|
||||
p1 = new TestJPanel();
|
||||
p1.setPreferredSize(size);
|
||||
p2 = new TestJPanel();
|
||||
p2.setPreferredSize(size);
|
||||
JPanel panel = new JPanel();
|
||||
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
|
||||
panel.add(p1);
|
||||
panel.add(p2);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
static class TestBufferedImage extends BufferedImage {
|
||||
int MAX_GLITCHES = 0;
|
||||
|
||||
TestBufferedImage(int width, int height, int imageType) {
|
||||
super(width, height, imageType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (! (other instanceof TestBufferedImage)) {
|
||||
return false;
|
||||
}
|
||||
TestBufferedImage image2 = (TestBufferedImage) other;
|
||||
int width = getWidth();
|
||||
int height = getHeight();
|
||||
if ((image2.getWidth() != width) || (image2.getHeight() != height)) {
|
||||
return false;
|
||||
}
|
||||
int glitches = 0;
|
||||
for (int x = 0; x < width; x++) {
|
||||
for (int y = 0; y < height; y++) {
|
||||
int rgb1 = getRGB(x, y);
|
||||
int rgb2 = image2.getRGB(x, y);
|
||||
if (rgb1 != rgb2) {
|
||||
//System.out.println(x+" "+y+" "+rgb1+" "+rgb2);
|
||||
glitches++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return glitches <= MAX_GLITCHES;
|
||||
}
|
||||
}
|
||||
|
||||
static class TestJPanel extends JPanel {
|
||||
TestBufferedImage image = createImage(new Dimension(1, 1));
|
||||
|
||||
TestBufferedImage createImage(Dimension d) {
|
||||
return new TestBufferedImage(d.width, d.height,
|
||||
BufferedImage.TYPE_INT_ARGB);
|
||||
}
|
||||
|
||||
public void setPreferredSize(Dimension size) {
|
||||
super.setPreferredSize(size);
|
||||
image = createImage(size);
|
||||
}
|
||||
|
||||
public void paint(Graphics g) {
|
||||
Graphics g0 = image.getGraphics();
|
||||
super.paint(g0);
|
||||
g.drawImage(image, 0, 0, this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
final bug4337267 test = new bug4337267();
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
test.run();
|
||||
}
|
||||
});
|
||||
|
||||
synchronized(test) {
|
||||
while (!done) {
|
||||
try {
|
||||
test.wait();
|
||||
} catch (InterruptedException ex) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (testFailed) {
|
||||
throw new RuntimeException("FAIL");
|
||||
}
|
||||
|
||||
System.out.println("OK");
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user