1/* 2 * Copyright (c) 2014, Oracle and/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/* @test 25 @bug 8016833 26 @summary underlines and strikethroughs should be painted at the correct 27 positions for different kind of text styles: normal, superscript and subscript 28 @author Anton Nashatyrev 29 @run main bug8016833 30*/ 31import javax.swing.*; 32import javax.swing.text.BadLocationException; 33import javax.swing.text.Style; 34import javax.swing.text.StyleConstants; 35import javax.swing.text.StyledDocument; 36import java.awt.*; 37import java.awt.image.BufferedImage; 38import java.lang.reflect.InvocationTargetException; 39 40public class bug8016833 { 41 42 void drawText(final Graphics g, final boolean underline, final boolean strikethrough, final boolean background) { 43 drawText(g, "mama", underline, strikethrough, background); 44 } 45 46 void drawText(final Graphics g, final String text, final boolean underline, final boolean strikethrough, final boolean background) { 47 try { 48 SwingUtilities.invokeAndWait(new Runnable() { 49 @Override 50 public void run() { 51 final JTextPane comp = new JTextPane(); 52 final StyledDocument doc = comp.getStyledDocument(); 53 54 Style style = comp.addStyle("superscript", null); 55 setNormalStyle(style); 56 57 if (underline) { 58 StyleConstants.setUnderline(style, true); 59 } 60 if (strikethrough) { 61 StyleConstants.setStrikeThrough(style, true); 62 } 63 if (background) { 64 StyleConstants.setBackground(style, Color.BLUE); 65 } 66 try { 67 doc.insertString(doc.getLength(), "mama", style); 68 } catch (BadLocationException e) { 69 throw new RuntimeException(e); 70 } 71 72 comp.setSize(200, 100); 73 comp.paint(g); 74 } 75 }); 76 } catch (InterruptedException e) { 77 throw new RuntimeException(e); 78 } catch (InvocationTargetException e) { 79 throw new RuntimeException(e); 80 } 81 } 82 83 void setNormalStyle(Style style) { 84 StyleConstants.setSuperscript(style, true); 85 } 86 87 int getEmptyPixel() { 88 return 0xFFFFFFFF; 89 } 90 91 boolean isPixelEmpty(int argb) { 92 return (argb & 0x00FFFFFF) == (getEmptyPixel() & 0x00FFFFFF); 93 } 94 95 boolean isLineEmpty(BufferedImage img, int coord, boolean isHorizontal) { 96 int len = isHorizontal ? img.getWidth() : img.getHeight(); 97 for (int i = 0; i < len; i++) { 98 int pixel = isHorizontal ? img.getRGB(i, coord) : img.getRGB(coord, i); 99 if (!isPixelEmpty(pixel)) { 100 return false; 101 } 102 } 103 return true; 104 } 105 106 Rectangle getPixelsOutline(BufferedImage img) { 107 int x1 = 0; 108 while (x1 < img.getWidth() && isLineEmpty(img, x1, false)) { 109 x1++; 110 } 111 int x2 = img.getWidth() - 1; 112 while (x2 >= 0 && isLineEmpty(img, x2, false)) { 113 x2--; 114 } 115 int y1 = 0; 116 while (y1 < img.getHeight() && isLineEmpty(img, y1, true)) { 117 y1++; 118 } 119 int y2 = img.getHeight() - 1; 120 while (y2 >= 0 && isLineEmpty(img, y2, true)) { 121 y2--; 122 } 123 124 return new Rectangle(x1, y1, x2 - x1 + 1, y2 - y1 + 1); 125 } 126 127 BufferedImage createImage() { 128 final BufferedImage img = new BufferedImage(200, 100, BufferedImage.TYPE_INT_ARGB); 129 try { 130 SwingUtilities.invokeAndWait(new Runnable() { 131 @Override 132 public void run() { 133 Graphics g = img.getGraphics(); 134 g.setColor(new Color(getEmptyPixel())); 135 g.fillRect(0, 0, 10000, 10000); 136 } 137 }); 138 } catch (InterruptedException e) { 139 throw new RuntimeException(e); 140 } catch (InvocationTargetException e) { 141 throw new RuntimeException(e); 142 } 143 return img; 144 } 145 146 int subPixels(int pix1, int pix2) { 147 if (pix1 == pix2) { 148 return getEmptyPixel(); 149 } 150 return pix1; 151 } 152 153 /** 154 * Subtracts img2 from img1 155 */ 156 BufferedImage subImages(BufferedImage img1, BufferedImage img2) { 157 if (img1.getHeight() != img2.getHeight() || 158 img1.getWidth() != img2.getWidth()) { 159 throw new RuntimeException("Different sizes"); 160 } 161 BufferedImage ret = new BufferedImage(img1.getWidth(), img1.getHeight(), img1.getType()); 162 163 for (int x = 0; x < ret.getWidth(); x++) { 164 for (int y = 0; y < ret.getHeight(); y++) { 165 ret.setRGB(x, y, subPixels(img1.getRGB(x, y), img2.getRGB(x, y))); 166 } 167 } 168 return ret; 169 } 170 171 void testUnderline() { 172 System.out.println(" testUnderline()"); 173 174 final BufferedImage img1 = createImage(); 175 drawText(img1.getGraphics(), true, false, false); 176 final Rectangle out1 = getPixelsOutline(img1); 177 System.out.println(" Underlined: " + out1); 178 179 final BufferedImage img2 = createImage(); 180 drawText(img2.getGraphics(), false, false, false); 181 final Rectangle out2 = getPixelsOutline(img2); 182 System.out.println(" Normal: " + out2); 183 184 final BufferedImage img3 = subImages(img1, img2); 185 final Rectangle out3 = getPixelsOutline(img3); 186 System.out.println(" Sub: " + out3); 187 188 // underline is not too thick 189 assertTrue(out3.getHeight() <= 2); 190 // not too wide 191 assertTrue(out3.getWidth() * 0.8 < out2.getWidth()); 192 // not too low 193 assertTrue(out3.getY() - (out1.getY() + out2.getHeight() - 1) < 4); 194 // not too high 195 assertTrue(out3.getY() - (out1.getY() + out2.getHeight() - 1) > 0); 196 } 197 198 void testStrikthrough() { 199 System.out.println(" testStrikthrough()"); 200 201 final BufferedImage img1 = createImage(); 202 drawText(img1.getGraphics(), false, true, false); 203 final Rectangle out1 = getPixelsOutline(img1); 204 System.out.println(" Striked: " + out1); 205 206 final BufferedImage img2 = createImage(); 207 drawText(img2.getGraphics(), false, false, false); 208 final Rectangle out2 = getPixelsOutline(img2); 209 System.out.println(" Normal: " + out2); 210 211 final BufferedImage img3 = subImages(img1, img2); 212 final Rectangle out3 = getPixelsOutline(img3); 213 System.out.println(" Sub: " + out3); 214 215 // strikethrough is not too thick 216 assertTrue(out3.getHeight() <= 2); 217 // not too wide 218 assertTrue(out3.getWidth() * 0.8 < out2.getWidth()); 219 // not too low 220 assertTrue(out3.getY() - (out1.getY() + out2.getHeight() - 1) < 0); 221 // not too high 222 assertTrue(out3.getY() - out1.getY() > 1); 223 } 224 void assertTrue(boolean b) { 225 if (!b) { 226 throw new RuntimeException("Assertion failed"); 227 } 228 } 229 230 static void testSuperScript() { 231 System.out.println("testSuperScript()"); 232 bug8016833 b = new bug8016833() { 233 @Override 234 void setNormalStyle(Style style) { 235 StyleConstants.setSuperscript(style, true); 236 } 237 }; 238 b.testUnderline(); 239 b.testStrikthrough(); 240 } 241 242 static void testSubScript() { 243 System.out.println("testSubScript()"); 244 bug8016833 b = new bug8016833() { 245 @Override 246 void setNormalStyle(Style style) { 247 StyleConstants.setSubscript(style, true); 248 } 249 }; 250 b.testUnderline(); 251 b.testStrikthrough(); 252 } 253 254 static void testNormalScript() { 255 System.out.println("testNormalScript()"); 256 bug8016833 b = new bug8016833() { 257 @Override 258 void setNormalStyle(Style style) { 259 } 260 }; 261 b.testUnderline(); 262 b.testStrikthrough(); 263 } 264 265 public static void main(String[] args) { 266 testSubScript(); 267 testSuperScript(); 268 testNormalScript(); 269 } 270} 271