--- a/jdk/src/share/classes/javax/swing/text/TextLayoutStrategy.java Tue Sep 15 16:26:40 2009 +0400
+++ b/jdk/src/share/classes/javax/swing/text/TextLayoutStrategy.java Wed Sep 16 16:15:41 2009 +0400
@@ -30,6 +30,7 @@
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 @@
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 @@
return pos - v.getStartOffset() + getBeginIndex();
}
+ private void setShaper(Object shaper) {
+ this.shaper = shaper;
+ }
+
// --- AttributedCharacterIterator methods -------------------------
/**
@@ -511,6 +523,8 @@
} 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 @@
keys = new HashSet<Attribute>();
keys.add(TextAttribute.FONT);
keys.add(TextAttribute.RUN_DIRECTION);
+ keys.add(TextAttribute.NUMERIC_SHAPING);
}
+ private Object shaper = null;
}
}
--- a/jdk/src/share/classes/sun/swing/SwingUtilities2.java Tue Sep 15 16:26:40 2009 +0400
+++ b/jdk/src/share/classes/sun/swing/SwingUtilities2.java Wed Sep 16 16:15:41 2009 +0400
@@ -193,6 +193,19 @@
}
/**
+ * 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.
*
* @param text characters to be tested
@@ -353,7 +366,21 @@
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 @@
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 @@
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 @@
*/
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 @@
}
}
- 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 @@
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 @@
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 @@
!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 @@
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 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/swing/JComponent/4337267/bug4337267.java Wed Sep 16 16:15:41 2009 +0400
@@ -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");
+ }
+}