1 /* 2 * Copyright (c) 2016, 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 import java.awt.Color; 25 import java.awt.Font; 26 import java.awt.FontMetrics; 27 import java.awt.Graphics2D; 28 import java.awt.GraphicsEnvironment; 29 import java.awt.RenderingHints; 30 import java.awt.font.FontRenderContext; 31 import java.awt.font.NumericShaper; 32 import java.awt.font.TextAttribute; 33 import java.awt.font.TextLayout; 34 import java.awt.image.BufferedImage; 35 import java.util.HashMap; 36 import javax.swing.JComponent; 37 import javax.swing.JLabel; 38 import javax.swing.SwingUtilities; 39 import javax.swing.UIManager; 40 import javax.swing.plaf.basic.BasicGraphicsUtils; 41 import javax.swing.plaf.metal.MetalLookAndFeel; 42 43 /** 44 * @test 45 * @bug 8132119 8168992 8169897 8207941 46 * @author Alexandr Scherbatiy 47 * @summary Provide public API for text related methods in SwingBasicGraphicsUtils2 48 */ 49 public class bug8132119 { 50 51 private static final int WIDTH = 50; 52 private static final int HEIGHT = 50; 53 private static final Color DRAW_COLOR = Color.RED; 54 private static final Color BACKGROUND_COLOR = Color.GREEN; 55 private static final NumericShaper NUMERIC_SHAPER = NumericShaper.getShaper( 56 NumericShaper.ARABIC); 57 main(String[] args)58 public static void main(String[] args) throws Exception { 59 SwingUtilities.invokeAndWait(bug8132119::testStringMethods); 60 } 61 testStringMethods()62 private static void testStringMethods() { 63 setMetalLAF(); 64 testStringWidth(); 65 testStringClip(); 66 testDrawEmptyString(); 67 testDrawString(false); 68 testDrawString(true); 69 checkNullArguments(); 70 } 71 testStringWidth()72 private static void testStringWidth() { 73 74 String str = "12345678910\u036F"; 75 JComponent comp = createComponent(str); 76 Font font = comp.getFont(); 77 FontMetrics fontMetrics = comp.getFontMetrics(font); 78 float stringWidth = BasicGraphicsUtils.getStringWidth(comp, fontMetrics, str); 79 80 if (stringWidth == fontMetrics.stringWidth(str)) { 81 throw new RuntimeException("Numeric shaper is not used!"); 82 } 83 84 if (stringWidth != getLayoutWidth(str, font, NUMERIC_SHAPER)) { 85 throw new RuntimeException("Wrong text width!"); 86 } 87 } 88 testStringClip()89 private static void testStringClip() { 90 91 String str = "1234567890"; 92 JComponent comp = createComponent(str); 93 FontMetrics fontMetrics = comp.getFontMetrics(comp.getFont()); 94 95 int width = (int) BasicGraphicsUtils.getStringWidth(comp, fontMetrics, str); 96 97 String clip = BasicGraphicsUtils.getClippedString(comp, fontMetrics, str, width); 98 checkClippedString(str, clip, str); 99 100 clip = BasicGraphicsUtils.getClippedString(comp, fontMetrics, str, width + 1); 101 checkClippedString(str, clip, str); 102 103 clip = BasicGraphicsUtils.getClippedString(comp, fontMetrics, str, -1); 104 checkClippedString(str, clip, "..."); 105 106 clip = BasicGraphicsUtils.getClippedString(comp, fontMetrics, str, 0); 107 checkClippedString(str, clip, "..."); 108 109 clip = BasicGraphicsUtils.getClippedString(comp, fontMetrics, 110 str, width - width / str.length()); 111 int endIndex = str.length() - 3; 112 checkClippedString(str, clip, str.substring(0, endIndex) + "..."); 113 } 114 checkClippedString(String str, String res, String golden)115 private static void checkClippedString(String str, String res, String golden) { 116 if (!golden.equals(res)) { 117 throw new RuntimeException(String.format("The string '%s' is not " 118 + "properly clipped. The result is '%s' instead of '%s'", 119 str, res, golden)); 120 } 121 } 122 testDrawEmptyString()123 private static void testDrawEmptyString() { 124 JLabel label = new JLabel(); 125 BufferedImage buffImage = createBufferedImage(50, 50); 126 Graphics2D g2 = buffImage.createGraphics(); 127 g2.setColor(DRAW_COLOR); 128 BasicGraphicsUtils.drawString(null, g2, null, 0, 0); 129 BasicGraphicsUtils.drawString(label, g2, null, 0, 0); 130 BasicGraphicsUtils.drawString(null, g2, "", 0, 0); 131 BasicGraphicsUtils.drawString(label, g2, "", 0, 0); 132 BasicGraphicsUtils.drawStringUnderlineCharAt(null, g2, null, 3, 0, 0); 133 BasicGraphicsUtils.drawStringUnderlineCharAt(label, g2, null, 3, 0, 0); 134 BasicGraphicsUtils.drawStringUnderlineCharAt(null, g2, "", 3, 0, 0); 135 BasicGraphicsUtils.drawStringUnderlineCharAt(label, g2, "", 3, 0, 0); 136 g2.dispose(); 137 checkImageIsEmpty(buffImage); 138 } 139 testDrawString(boolean underlined)140 private static void testDrawString(boolean underlined) { 141 String str = "AOB"; 142 JComponent comp = createComponent(str); 143 144 BufferedImage buffImage = createBufferedImage(WIDTH, HEIGHT); 145 Graphics2D g2 = buffImage.createGraphics(); 146 147 g2.setColor(DRAW_COLOR); 148 g2.setFont(comp.getFont()); 149 g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, 150 RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); 151 152 FontMetrics fontMetrices = comp.getFontMetrics(comp.getFont()); 153 float width = BasicGraphicsUtils.getStringWidth(comp, fontMetrices, str); 154 float x = (WIDTH - width) / 2; 155 int y = 3 * HEIGHT / 4; 156 157 if (underlined) { 158 BasicGraphicsUtils.drawStringUnderlineCharAt(comp, g2, str, 1, x, y); 159 } else { 160 BasicGraphicsUtils.drawString(comp, g2, str, x, y); 161 } 162 g2.dispose(); 163 164 float xx = BasicGraphicsUtils.getStringWidth(comp, fontMetrices, "A") + 165 BasicGraphicsUtils.getStringWidth(comp, fontMetrices, "O")/2 - 10; 166 167 checkImageContainsSymbol(buffImage, (int) xx, underlined ? 3 : 2); 168 } 169 checkNullArguments()170 private static void checkNullArguments() { 171 172 Graphics2D g = null; 173 try { 174 String text = "Test"; 175 JComponent component = new JLabel(text); 176 BufferedImage img = createBufferedImage(100, 100); 177 g = img.createGraphics(); 178 checkNullArguments(component, g, text); 179 } finally { 180 g.dispose(); 181 } 182 } 183 checkNullArguments(JComponent comp, Graphics2D g, String text)184 private static void checkNullArguments(JComponent comp, Graphics2D g, 185 String text) { 186 187 checkNullArgumentsDrawString(comp, g, text); 188 checkNullArgumentsDrawStringUnderlineCharAt(comp, g, text); 189 checkNullArgumentsGetClippedString(comp, text); 190 checkNullArgumentsGetStringWidth(comp, text); 191 } 192 checkNullArgumentsDrawString(JComponent comp, Graphics2D g, String text)193 private static void checkNullArgumentsDrawString(JComponent comp, Graphics2D g, 194 String text) { 195 196 float x = 50; 197 float y = 50; 198 BasicGraphicsUtils.drawString(null, g, text, x, y); 199 BasicGraphicsUtils.drawString(comp, g, null, x, y); 200 201 try { 202 BasicGraphicsUtils.drawString(comp, null, text, x, y); 203 } catch (NullPointerException e) { 204 return; 205 } 206 207 throw new RuntimeException("NPE is not thrown"); 208 } 209 checkNullArgumentsDrawStringUnderlineCharAt( JComponent comp, Graphics2D g, String text)210 private static void checkNullArgumentsDrawStringUnderlineCharAt( 211 JComponent comp, Graphics2D g, String text) { 212 213 int x = 50; 214 int y = 50; 215 BasicGraphicsUtils.drawStringUnderlineCharAt(null, g, text, 1, x, y); 216 BasicGraphicsUtils.drawStringUnderlineCharAt(comp, g, null, 1, x, y); 217 218 try { 219 BasicGraphicsUtils.drawStringUnderlineCharAt(comp, null, text, 1, x, y); 220 } catch (NullPointerException e) { 221 return; 222 } 223 224 throw new RuntimeException("NPE is not thrown"); 225 } 226 checkNullArgumentsGetClippedString( JComponent comp, String text)227 private static void checkNullArgumentsGetClippedString( 228 JComponent comp, String text) { 229 230 FontMetrics fontMetrics = comp.getFontMetrics(comp.getFont()); 231 232 BasicGraphicsUtils.getClippedString(null, fontMetrics, text, 1); 233 String result = BasicGraphicsUtils.getClippedString(comp, fontMetrics, null, 1); 234 if (!"".equals(result)) { 235 throw new RuntimeException("Empty string is not returned!"); 236 } 237 238 try { 239 BasicGraphicsUtils.getClippedString(comp, null, text, 1); 240 } catch (NullPointerException e) { 241 return; 242 } 243 244 throw new RuntimeException("NPE is not thrown"); 245 } 246 checkNullArgumentsGetStringWidth(JComponent comp, String text)247 private static void checkNullArgumentsGetStringWidth(JComponent comp, 248 String text) { 249 250 FontMetrics fontMetrics = comp.getFontMetrics(comp.getFont()); 251 BasicGraphicsUtils.getStringWidth(null, fontMetrics, text); 252 float result = BasicGraphicsUtils.getStringWidth(comp, fontMetrics, null); 253 254 if (result != 0) { 255 throw new RuntimeException("The string length is not 0"); 256 } 257 258 try { 259 BasicGraphicsUtils.getStringWidth(comp, null, text); 260 } catch (NullPointerException e) { 261 return; 262 } 263 264 throw new RuntimeException("NPE is not thrown"); 265 } 266 setMetalLAF()267 private static void setMetalLAF() { 268 try { 269 UIManager.setLookAndFeel(new MetalLookAndFeel()); 270 } catch (Exception e) { 271 throw new RuntimeException(e); 272 } 273 } 274 createComponent(String str)275 private static JComponent createComponent(String str) { 276 JComponent comp = new JLabel(str); 277 comp.setSize(WIDTH, HEIGHT); 278 comp.putClientProperty(TextAttribute.NUMERIC_SHAPING, NUMERIC_SHAPER); 279 comp.setFont(getFont()); 280 return comp; 281 } 282 getFontName(String fn, String[] fontNames)283 private static String getFontName(String fn, String[] fontNames) { 284 String fontName = null; 285 for (String name : fontNames) { 286 if (fn.equals(name)) { 287 fontName = name; 288 break; 289 } 290 } 291 return fontName; 292 } 293 getFont()294 private static Font getFont() { 295 GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); 296 String[] fontNames = ge.getAvailableFontFamilyNames(); 297 298 // We do not have Arial on all systems so provide some reasonable fallbacks. 299 // In case the fallbacks are not available as well, choose as last fallback 300 // the first font - however this might be a problematic choice. 301 String fontName = getFontName("Arial", fontNames); 302 if (fontName == null) { 303 fontName = getFontName("Bitstream Charter", fontNames); 304 if (fontName == null) { 305 fontName = getFontName("Dialog", fontNames); 306 if (fontName == null) { 307 fontName = fontNames[0]; 308 System.out.println("warning - preferred fonts not on the system, fall back to first font " + fontName); 309 } 310 } 311 } 312 return new Font(fontName, Font.PLAIN, 30); 313 } 314 getLayoutWidth(String text, Font font, NumericShaper shaper)315 private static float getLayoutWidth(String text, Font font, NumericShaper shaper) { 316 HashMap map = new HashMap(); 317 map.put(TextAttribute.FONT, font); 318 map.put(TextAttribute.NUMERIC_SHAPING, shaper); 319 FontRenderContext frc = new FontRenderContext(null, false, false); 320 TextLayout layout = new TextLayout(text, map, frc); 321 return layout.getAdvance(); 322 } 323 checkImageIsEmpty(BufferedImage buffImage)324 private static void checkImageIsEmpty(BufferedImage buffImage) { 325 int background = BACKGROUND_COLOR.getRGB(); 326 327 for (int i = 0; i < buffImage.getWidth(); i++) { 328 for (int j = 0; j < buffImage.getHeight(); j++) { 329 if (background != buffImage.getRGB(i, j)) { 330 throw new RuntimeException("Image is not empty!"); 331 } 332 } 333 } 334 } 335 checkImageContainsSymbol(BufferedImage buffImage, int x, int intersections)336 private static void checkImageContainsSymbol(BufferedImage buffImage, 337 int x, int intersections) { 338 339 int background = BACKGROUND_COLOR.getRGB(); 340 boolean isBackground = true; 341 int backgroundChangesCount = 0; 342 343 for (int y = 0; y < buffImage.getHeight(); y++) { 344 if (!(isBackground ^ (background != buffImage.getRGB(x, y)))) { 345 isBackground = !isBackground; 346 backgroundChangesCount++; 347 } 348 } 349 350 if (backgroundChangesCount != intersections * 2) { 351 throw new RuntimeException("String is not properly drawn!"); 352 } 353 } 354 createBufferedImage(int width, int height)355 private static BufferedImage createBufferedImage(int width, int height) { 356 BufferedImage bufffImage = new BufferedImage(width, height, 357 BufferedImage.TYPE_INT_RGB); 358 359 Graphics2D g = bufffImage.createGraphics(); 360 g.setColor(BACKGROUND_COLOR); 361 g.fillRect(0, 0, width, height); 362 g.dispose(); 363 return bufffImage; 364 } 365 } 366