diff --git a/src/share/classes/sun/swing/SwingUtilities2.java b/src/share/classes/sun/swing/SwingUtilities2.java index a1903f2bdd2eed678f8acd6fe85cd8041fff315c..07bb880d0ffc37c5dc3c8691737677b163fcda97 100644 --- a/src/share/classes/sun/swing/SwingUtilities2.java +++ b/src/share/classes/sun/swing/SwingUtilities2.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2013, 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 @@ -33,6 +33,7 @@ import java.awt.event.*; import java.awt.font.*; import java.awt.geom.*; import java.awt.print.PrinterGraphics; +import java.text.CharacterIterator; import java.text.AttributedCharacterIterator; import java.text.AttributedString; @@ -504,21 +505,24 @@ public class SwingUtilities2 { * it to fit in the screen width. This distributes the spacing * more evenly than directly laying out to the screen advances. */ - float screenWidth = (float) - g2d.getFont().getStringBounds(text, DEFAULT_FRC).getWidth(); - TextLayout layout = createTextLayout(c, text, g2d.getFont(), - g2d.getFontRenderContext()); - - layout = layout.getJustifiedLayout(screenWidth); - /* Use alternate print color if specified */ - Color col = g2d.getColor(); - if (col instanceof PrintColorUIResource) { - g2d.setColor(((PrintColorUIResource)col).getPrintColor()); - } + String trimmedText = trimTrailingSpaces(text); + if (!trimmedText.isEmpty()) { + float screenWidth = (float) g2d.getFont().getStringBounds + (trimmedText, DEFAULT_FRC).getWidth(); + TextLayout layout = createTextLayout(c, text, g2d.getFont(), + g2d.getFontRenderContext()); - layout.draw(g2d, x, y); + layout = layout.getJustifiedLayout(screenWidth); + /* Use alternate print color if specified */ + Color col = g2d.getColor(); + if (col instanceof PrintColorUIResource) { + g2d.setColor(((PrintColorUIResource)col).getPrintColor()); + } - g2d.setColor(col); + layout.draw(g2d, x, y); + + g2d.setColor(col); + } return; } @@ -789,24 +793,26 @@ public class SwingUtilities2 { if (frc != null && !isFontRenderContextPrintCompatible (deviceFontRenderContext, frc)) { - TextLayout layout = - createTextLayout(c, new String(data, offset, length), - g2d.getFont(), - deviceFontRenderContext); - float screenWidth = (float)g2d.getFont(). - getStringBounds(data, offset, offset + length, frc). - getWidth(); - layout = layout.getJustifiedLayout(screenWidth); - /* Use alternate print color if specified */ - Color col = g2d.getColor(); - if (col instanceof PrintColorUIResource) { - g2d.setColor(((PrintColorUIResource)col).getPrintColor()); - } + String text = new String(data, offset, length); + TextLayout layout = new TextLayout(text, g2d.getFont(), + deviceFontRenderContext); + String trimmedText = trimTrailingSpaces(text); + if (!trimmedText.isEmpty()) { + float screenWidth = (float)g2d.getFont(). + getStringBounds(trimmedText, frc).getWidth(); + layout = layout.getJustifiedLayout(screenWidth); - layout.draw(g2d,x,y); + /* Use alternate print color if specified */ + Color col = g2d.getColor(); + if (col instanceof PrintColorUIResource) { + g2d.setColor(((PrintColorUIResource)col).getPrintColor()); + } - g2d.setColor(col); + layout.draw(g2d,x,y); + + g2d.setColor(col); + } return nextX; } @@ -888,14 +894,23 @@ public class SwingUtilities2 { } else { frc = g2d.getFontRenderContext(); } - TextLayout layout = new TextLayout(iterator, frc); + TextLayout layout; if (isPrinting) { FontRenderContext deviceFRC = g2d.getFontRenderContext(); if (!isFontRenderContextPrintCompatible(frc, deviceFRC)) { - float screenWidth = layout.getAdvance(); layout = new TextLayout(iterator, deviceFRC); - layout = layout.getJustifiedLayout(screenWidth); + AttributedCharacterIterator trimmedIt = + getTrimmedTrailingSpacesIterator(iterator); + if (trimmedIt != null) { + float screenWidth = new TextLayout(trimmedIt, frc). + getAdvance(); + layout = layout.getJustifiedLayout(screenWidth); + } + } else { + layout = new TextLayout(iterator, frc); } + } else { + layout = new TextLayout(iterator, frc); } layout.draw(g2d, x, y); retVal = layout.getAdvance(); @@ -1047,6 +1062,39 @@ public class SwingUtilities2 { return (g instanceof PrinterGraphics || g instanceof PrintGraphics); } + private static String trimTrailingSpaces(String s) { + int i = s.length() - 1; + while(i >= 0 && Character.isWhitespace(s.charAt(i))) { + i--; + } + return s.substring(0, i + 1); + } + + private static AttributedCharacterIterator getTrimmedTrailingSpacesIterator + (AttributedCharacterIterator iterator) { + int curIdx = iterator.getIndex(); + + char c = iterator.last(); + while(c != CharacterIterator.DONE && Character.isWhitespace(c)) { + c = iterator.previous(); + } + + if (c != CharacterIterator.DONE) { + int endIdx = iterator.getIndex(); + + if (endIdx == iterator.getEndIndex() - 1) { + iterator.setIndex(curIdx); + return iterator; + } else { + AttributedString trimmedText = new AttributedString(iterator, + iterator.getBeginIndex(), endIdx + 1); + return trimmedText.getIterator(); + } + } else { + return null; + } + } + /** * Determines whether the SelectedTextColor should be used for painting text * foreground for the specified highlight. diff --git a/test/java/awt/print/bug8023392/bug8023392.html b/test/java/awt/print/bug8023392/bug8023392.html new file mode 100644 index 0000000000000000000000000000000000000000..d796a21c660161671eeb925b5af7d51270ab6b65 --- /dev/null +++ b/test/java/awt/print/bug8023392/bug8023392.html @@ -0,0 +1,20 @@ + + + + Bug 8023392 + + + +

Bug ID: 8023392

+ +

See the dialog box (usually in upper left corner) for instructions

+ + + + diff --git a/test/java/awt/print/bug8023392/bug8023392.java b/test/java/awt/print/bug8023392/bug8023392.java new file mode 100644 index 0000000000000000000000000000000000000000..6c6fbb26ee97b6a744f283a4355fd5c9c51df1d0 --- /dev/null +++ b/test/java/awt/print/bug8023392/bug8023392.java @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2013, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 8023392 + @summary Swing text components printed with spaces between chars + @author Anton Nashatyrev + @run applet/manual=yesno bug8023392.html +*/ + +import javax.swing.*; +import javax.swing.border.LineBorder; +import java.applet.Applet; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.font.TextAttribute; +import java.awt.print.PageFormat; +import java.awt.print.Printable; +import java.awt.print.PrinterException; +import java.awt.print.PrinterJob; +import java.text.AttributedCharacterIterator; +import java.text.AttributedString; + + +public class bug8023392 extends Applet { + static final String[] instructions = { + "A Frame containing several pairs of labels ((a) and (b)) is displayed.", + "Labels of each pair look the same and are left-aligned (with spaces ", + "between chars).", + "1. Hit the print button.", + "2. Select any available printer (printing to file is also fine).", + "3. Look at the printing result (paper, PDF, PS, etc.):", + " The (a) and (b) labels should look almost the same and the (a) labels", + " shouldn't appear as if they are stretched along X axis."}; + + public void init() { + this.setLayout(new BorderLayout()); + add(new SimplePrint2(), BorderLayout.CENTER); + + Sysout.createDialogWithInstructions(instructions); + + } + + public static class SimplePrint2 extends JPanel + implements ActionListener, Printable { + JLabel label1; + JLabel label2; + JButton printButton; + + + public SimplePrint2() { + setLayout(new BorderLayout()); + label1 = new JLabel("2a) a b c d e" + + " "); + label2 = new JLabel("2b) a b c d e"); + + Box p1 = new Box(BoxLayout.Y_AXIS); + p1.add(label1); + p1.add(label2); + p1.add(new JLabel("wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww") { + String s = "3a) a b c d e "; + @Override + protected void paintComponent(Graphics g) { + sun.swing.SwingUtilities2.drawChars(this, g, s.toCharArray(), + 0, s.length(), 0, 15); + } + }); + p1.add(new JLabel("wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww") { + String s = "3b) a b c d e"; + @Override + protected void paintComponent(Graphics g) { + sun.swing.SwingUtilities2.drawChars(this, g, s.toCharArray(), + 0, s.length(), 0, 15); + } + }); + p1.add(new JLabel("wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww") { + String s = "4a) a b c d e "; + AttributedCharacterIterator it; + { + AttributedString as = new AttributedString(s); + as.addAttribute(TextAttribute.FONT, getFont()); + as.addAttribute(TextAttribute.FOREGROUND, Color.RED, 3, 8); + it = as.getIterator(); + } + @Override + protected void paintComponent(Graphics g) { + sun.swing.SwingUtilities2.drawString(this, g, it, 0, 15); + } + }); + + p1.add(new JLabel("wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww") { + String s = "4b) a b c d e"; + AttributedCharacterIterator it; + { + AttributedString as = new AttributedString(s); + as.addAttribute(TextAttribute.FONT, getFont()); + as.addAttribute(TextAttribute.FOREGROUND, Color.RED, 3, 8); + it = as.getIterator(); + } + @Override + protected void paintComponent(Graphics g) { + sun.swing.SwingUtilities2.drawString(this, g, it, 0, 15); + } + }); + + JPanel p2 = new JPanel(); + printButton = new JButton("Print"); + printButton.addActionListener(this); + p2.add(printButton); + + Container c = this; + c.add(p1, BorderLayout.CENTER); + c.add(p2, BorderLayout.SOUTH); + + String[] data = { + "1a) \u30aa\u30f3\u30e9\u30a4\u30f3\u6d88\u8fbc" + + " ", + "1b) \u30aa\u30f3\u30e9\u30a4\u30f3\u6d88\u8fbc" + }; + JList l0 = new JList(data); + l0.setVisibleRowCount(l0.getModel().getSize()); + JScrollPane jsp = new JScrollPane(l0); + l0.setBorder(new LineBorder(Color.GRAY)); + c.add(jsp, BorderLayout.NORTH); + + for (Component comp : new Component[]{label1, label2, printButton}) { + comp.setFont(new Font("Monospaced", 0, 16)); + } + } + + public void actionPerformed(ActionEvent e) { + PrinterJob job = PrinterJob.getPrinterJob(); + job.setPrintable(this); + if (job.printDialog()) { + try { + job.print(); + } catch (PrinterException ex) { + ex.printStackTrace(); + } + } + } + + public int print(Graphics graphics, + PageFormat pageFormat, + int pageIndex) + throws PrinterException { + if (pageIndex >= 1) { + return Printable.NO_SUCH_PAGE; + } + + this.paint(graphics); + return Printable.PAGE_EXISTS; + } + } +} + + +/** + * ************************************************* + * Standard Test Machinery + * DO NOT modify anything below -- it's a standard + * chunk of code whose purpose is to make user + * interaction uniform, and thereby make it simpler + * to read and understand someone else's test. + * ************************************************** + */ +class Sysout { + private static TestDialog dialog; + + public static void createDialogWithInstructions(String[] instructions) { + dialog = new TestDialog(new Frame(), "Instructions"); + dialog.printInstructions(instructions); + dialog.show(); + println("Any messages for the tester will display here."); + } + + public static void createDialog() { + dialog = new TestDialog(new Frame(), "Instructions"); + String[] defInstr = {"Instructions will appear here. ", ""}; + dialog.printInstructions(defInstr); + dialog.show(); + println("Any messages for the tester will display here."); + } + + + public static void printInstructions(String[] instructions) { + dialog.printInstructions(instructions); + } + + + public static void println(String messageIn) { + dialog.displayMessage(messageIn); + } + +}// Sysout class + + +class TestDialog extends Dialog { + + TextArea instructionsText; + TextArea messageText; + int maxStringLength = 80; + + //DO NOT call this directly, go through Sysout + public TestDialog(Frame frame, String name) { + super(frame, name); + int scrollBoth = TextArea.SCROLLBARS_BOTH; + instructionsText = new TextArea("", 15, maxStringLength, scrollBoth); + add("North", instructionsText); + + messageText = new TextArea("", 5, maxStringLength, scrollBoth); + add("South", messageText); + + pack(); + + show(); + }// TestDialog() + + //DO NOT call this directly, go through Sysout + public void printInstructions(String[] instructions) { + //Clear out any current instructions + instructionsText.setText(""); + + //Go down array of instruction strings + + String printStr, remainingStr; + for (int i = 0; i < instructions.length; i++) { + //chop up each into pieces maxSringLength long + remainingStr = instructions[i]; + while (remainingStr.length() > 0) { + //if longer than max then chop off first max chars to print + if (remainingStr.length() >= maxStringLength) { + //Try to chop on a word boundary + int posOfSpace = remainingStr. + lastIndexOf(' ', maxStringLength - 1); + + if (posOfSpace <= 0) posOfSpace = maxStringLength - 1; + + printStr = remainingStr.substring(0, posOfSpace + 1); + remainingStr = remainingStr.substring(posOfSpace + 1); + } + //else just print + else { + printStr = remainingStr; + remainingStr = ""; + } + + instructionsText.append(printStr + "\n"); + + }// while + + }// for + + }//printInstructions() + + //DO NOT call this directly, go through Sysout + public void displayMessage(String messageIn) { + messageText.append(messageIn + "\n"); + } + +}// TestDialog class +