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 */
31 import javax.swing.*;
32 import javax.swing.text.BadLocationException;
33 import javax.swing.text.Style;
34 import javax.swing.text.StyleConstants;
35 import javax.swing.text.StyledDocument;
36 import java.awt.*;
37 import java.awt.image.BufferedImage;
38 import java.lang.reflect.InvocationTargetException;
39 
40 public class bug8016833 {
41 
drawText(final Graphics g, final boolean underline, final boolean strikethrough, final boolean background)42     void drawText(final Graphics g, final boolean underline, final boolean strikethrough, final boolean background) {
43         drawText(g, "mama", underline, strikethrough, background);
44     }
45 
drawText(final Graphics g, final String text, final boolean underline, final boolean strikethrough, final boolean background)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 
setNormalStyle(Style style)83     void setNormalStyle(Style style) {
84         StyleConstants.setSuperscript(style, true);
85     }
86 
getEmptyPixel()87     int getEmptyPixel() {
88         return 0xFFFFFFFF;
89     }
90 
isPixelEmpty(int argb)91     boolean isPixelEmpty(int argb) {
92         return (argb & 0x00FFFFFF) == (getEmptyPixel() & 0x00FFFFFF);
93     }
94 
isLineEmpty(BufferedImage img, int coord, boolean isHorizontal)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 
getPixelsOutline(BufferedImage img)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 
createImage()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 
subPixels(int pix1, int pix2)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      */
subImages(BufferedImage img1, BufferedImage img2)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 
testUnderline()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