提交 e5c72fdd 编写于 作者: G gsm

4337267: Arabic Numeral Shaping

Reviewed-by: peterz
上级 6dddd7cd
...@@ -30,6 +30,7 @@ import java.text.AttributedCharacterIterator; ...@@ -30,6 +30,7 @@ import java.text.AttributedCharacterIterator;
import java.text.BreakIterator; import java.text.BreakIterator;
import java.awt.font.*; import java.awt.font.*;
import java.awt.geom.AffineTransform; import java.awt.geom.AffineTransform;
import javax.swing.JComponent;
import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentEvent;
import sun.font.BidiUtils; import sun.font.BidiUtils;
...@@ -301,6 +302,13 @@ class TextLayoutStrategy extends FlowView.FlowStrategy { ...@@ -301,6 +302,13 @@ class TextLayoutStrategy extends FlowView.FlowStrategy {
iter = BreakIterator.getLineInstance(); 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); measurer = new LineBreakMeasurer(text, iter, frc);
// If the children of the FlowView's logical view are GlyphViews, they // If the children of the FlowView's logical view are GlyphViews, they
...@@ -399,6 +407,10 @@ class TextLayoutStrategy extends FlowView.FlowStrategy { ...@@ -399,6 +407,10 @@ class TextLayoutStrategy extends FlowView.FlowStrategy {
return pos - v.getStartOffset() + getBeginIndex(); return pos - v.getStartOffset() + getBeginIndex();
} }
private void setShaper(Object shaper) {
this.shaper = shaper;
}
// --- AttributedCharacterIterator methods ------------------------- // --- AttributedCharacterIterator methods -------------------------
/** /**
...@@ -511,6 +523,8 @@ class TextLayoutStrategy extends FlowView.FlowStrategy { ...@@ -511,6 +523,8 @@ class TextLayoutStrategy extends FlowView.FlowStrategy {
} else if( attribute == TextAttribute.RUN_DIRECTION ) { } else if( attribute == TextAttribute.RUN_DIRECTION ) {
return return
v.getDocument().getProperty(TextAttribute.RUN_DIRECTION); v.getDocument().getProperty(TextAttribute.RUN_DIRECTION);
} else if (attribute == TextAttribute.NUMERIC_SHAPING) {
return shaper;
} }
return null; return null;
} }
...@@ -532,8 +546,10 @@ class TextLayoutStrategy extends FlowView.FlowStrategy { ...@@ -532,8 +546,10 @@ class TextLayoutStrategy extends FlowView.FlowStrategy {
keys = new HashSet<Attribute>(); keys = new HashSet<Attribute>();
keys.add(TextAttribute.FONT); keys.add(TextAttribute.FONT);
keys.add(TextAttribute.RUN_DIRECTION); keys.add(TextAttribute.RUN_DIRECTION);
keys.add(TextAttribute.NUMERIC_SHAPING);
} }
private Object shaper = null;
} }
} }
...@@ -192,6 +192,19 @@ public class SwingUtilities2 { ...@@ -192,6 +192,19 @@ public class SwingUtilities2 {
fontCache = new LSBCacheEntry[CACHE_SIZE]; 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. * checks whether TextLayout is required to handle characters.
* *
...@@ -353,7 +366,21 @@ public class SwingUtilities2 { ...@@ -353,7 +366,21 @@ public class SwingUtilities2 {
if (string == null || string.equals("")) { if (string == null || string.equals("")) {
return 0; 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 { ...@@ -394,21 +421,11 @@ public class SwingUtilities2 {
String string, int availTextWidth) { String string, int availTextWidth) {
// c may be null here. // c may be null here.
String clipString = "..."; String clipString = "...";
int stringLength = string.length();
availTextWidth -= SwingUtilities2.stringWidth(c, fm, clipString); availTextWidth -= SwingUtilities2.stringWidth(c, fm, clipString);
if (availTextWidth <= 0) {
//can not fit any characters
return clipString;
}
boolean needsTextLayout; boolean needsTextLayout;
synchronized (charsBufferLock) { synchronized (charsBufferLock) {
if (charsBuffer == null || charsBuffer.length < stringLength) { int stringLength = syncCharsBuffer(string);
charsBuffer = string.toCharArray();
} else {
string.getChars(0, stringLength, charsBuffer, 0);
}
needsTextLayout = needsTextLayout =
isComplexLayout(charsBuffer, 0, stringLength); isComplexLayout(charsBuffer, 0, stringLength);
if (!needsTextLayout) { if (!needsTextLayout) {
...@@ -425,6 +442,10 @@ public class SwingUtilities2 { ...@@ -425,6 +442,10 @@ public class SwingUtilities2 {
if (needsTextLayout) { if (needsTextLayout) {
FontRenderContext frc = getFontRenderContext(c, fm); FontRenderContext frc = getFontRenderContext(c, fm);
AttributedString aString = new AttributedString(string); AttributedString aString = new AttributedString(string);
if (c != null) {
aString.addAttribute(TextAttribute.NUMERIC_SHAPING,
c.getClientProperty(TextAttribute.NUMERIC_SHAPING));
}
LineBreakMeasurer measurer = LineBreakMeasurer measurer =
new LineBreakMeasurer(aString.getIterator(), frc); new LineBreakMeasurer(aString.getIterator(), frc);
int nChars = measurer.nextOffset(availTextWidth); int nChars = measurer.nextOffset(availTextWidth);
...@@ -465,7 +486,7 @@ public class SwingUtilities2 { ...@@ -465,7 +486,7 @@ public class SwingUtilities2 {
*/ */
float screenWidth = (float) float screenWidth = (float)
g2d.getFont().getStringBounds(text, DEFAULT_FRC).getWidth(); g2d.getFont().getStringBounds(text, DEFAULT_FRC).getWidth();
TextLayout layout = new TextLayout(text, g2d.getFont(), TextLayout layout = createTextLayout(c, text, g2d.getFont(),
g2d.getFontRenderContext()); g2d.getFontRenderContext());
layout = layout.getJustifiedLayout(screenWidth); layout = layout.getJustifiedLayout(screenWidth);
...@@ -505,7 +526,21 @@ public class SwingUtilities2 { ...@@ -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) { if (oldAAValue != null) {
g2.setRenderingHint(KEY_TEXT_ANTIALIASING, oldAAValue); g2.setRenderingHint(KEY_TEXT_ANTIALIASING, oldAAValue);
...@@ -547,11 +582,7 @@ public class SwingUtilities2 { ...@@ -547,11 +582,7 @@ public class SwingUtilities2 {
boolean needsTextLayout = isPrinting; boolean needsTextLayout = isPrinting;
if (!needsTextLayout) { if (!needsTextLayout) {
synchronized (charsBufferLock) { synchronized (charsBufferLock) {
if (charsBuffer == null || charsBuffer.length < textLength) { syncCharsBuffer(text);
charsBuffer = text.toCharArray();
} else {
text.getChars(0, textLength, charsBuffer, 0);
}
needsTextLayout = needsTextLayout =
isComplexLayout(charsBuffer, 0, textLength); isComplexLayout(charsBuffer, 0, textLength);
} }
...@@ -567,7 +598,7 @@ public class SwingUtilities2 { ...@@ -567,7 +598,7 @@ public class SwingUtilities2 {
Graphics2D g2d = getGraphics2D(g); Graphics2D g2d = getGraphics2D(g);
if (g2d != null) { if (g2d != null) {
TextLayout layout = TextLayout layout =
new TextLayout(text, g2d.getFont(), createTextLayout(c, text, g2d.getFont(),
g2d.getFontRenderContext()); g2d.getFontRenderContext());
if (isPrinting) { if (isPrinting) {
float screenWidth = (float)g2d.getFont(). float screenWidth = (float)g2d.getFont().
...@@ -728,7 +759,7 @@ public class SwingUtilities2 { ...@@ -728,7 +759,7 @@ public class SwingUtilities2 {
!isFontRenderContextPrintCompatible !isFontRenderContextPrintCompatible
(deviceFontRenderContext, frc)) { (deviceFontRenderContext, frc)) {
TextLayout layout = TextLayout layout =
new TextLayout(new String(data,offset,length), createTextLayout(c, new String(data, offset, length),
g2d.getFont(), g2d.getFont(),
deviceFontRenderContext); deviceFontRenderContext);
float screenWidth = (float)g2d.getFont(). float screenWidth = (float)g2d.getFont().
...@@ -846,6 +877,20 @@ public class SwingUtilities2 { ...@@ -846,6 +877,20 @@ public class SwingUtilities2 {
return retVal; 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. * Checks if two given FontRenderContexts are compatible for printing.
* We can't just use equals as we want to exclude from the comparison : * We can't just use equals as we want to exclude from the comparison :
......
/*
* @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");
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册