1 /* 2 * Copyright (c) 2003, 2006, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.font; 27 28 import java.awt.Font; 29 import java.awt.FontFormatException; 30 import java.awt.GraphicsEnvironment; 31 import java.awt.font.FontRenderContext; 32 import java.awt.geom.GeneralPath; 33 import java.awt.geom.Point2D; 34 import java.awt.geom.Rectangle2D; 35 import java.io.UnsupportedEncodingException; 36 import java.lang.ref.WeakReference; 37 import java.util.Locale; 38 39 /* 40 * Ideally there would be no native fonts used, and this class would be 41 * unneeded and removed. Presently it is still needed until such time 42 * as font configuration files (or the implementation equivalent) can have 43 * all references to fonts that are not handled via Java 2D removed. 44 * Currently there are two cases where this class is needed, both on 45 * Unix, primarily Solaris, but useful on Linux too if fonts have moved. 46 * 1. Some legacy F3 fonts are still referenced so that AWT "X/Motif" 47 * can get dingbats and symbols from them. This can be dispensed with when 48 * either AWT is based on 2D, or when the X font path is known to always 49 * contain a Type1 or TrueType font that can be used in font configuration 50 * files to replace the F3 fonts. 51 * 2. When location of font files by 2D fails, because of some system 52 * configuration problem, it is desirable to have a fall back to some 53 * functionality that lessens the immediate impact on users. Being able 54 * to perform limited operations by using bitmaps from X11 helps here. 55 */ 56 57 public class NativeFont extends PhysicalFont { 58 59 String encoding; 60 61 private int numGlyphs = -1; 62 boolean isBitmapDelegate; 63 PhysicalFont delegateFont; 64 65 /** 66 * Verifies native font is accessible. 67 * @throws FontFormatException - if the font can't be located. 68 */ NativeFont(String platName, boolean bitmapDelegate)69 public NativeFont(String platName, boolean bitmapDelegate) 70 throws FontFormatException { 71 super(platName, null); 72 73 /* This is set true if this is an instance of a NativeFont 74 * created by some other font, to get native bitmaps. 75 * The delegating font will call this font only for "basic" 76 * cases - ie non-rotated, uniform scale, monochrome bitmaps. 77 * If this is false, then this instance may need to itself 78 * delegate to another font for non-basic cases. Since 79 * NativeFonts are used in that way only for symbol and dingbats 80 * we know its safe to delegate these to the JRE's default 81 * physical font (Lucida Sans Regular). 82 */ 83 isBitmapDelegate = bitmapDelegate; 84 85 if (GraphicsEnvironment.isHeadless()) { 86 throw new FontFormatException("Native font in headless toolkit"); 87 } 88 fontRank = Font2D.NATIVE_RANK; 89 initNames(); 90 if (getNumGlyphs() == 0) { 91 throw new FontFormatException("Couldn't locate font" + platName); 92 } 93 } 94 initNames()95 private void initNames() throws FontFormatException { 96 /* Valid XLFD has exactly 14 "-" chars. 97 * First run over the string to verify have at least this many 98 * At the same time record the locations of the hyphens 99 * so we can just pick the right substring later on 100 */ 101 int[] hPos = new int[14]; 102 int hyphenCnt = 1; 103 int pos = 1; 104 105 String xlfd = platName.toLowerCase(Locale.ENGLISH); 106 if (xlfd.startsWith("-")) { 107 while (pos != -1 && hyphenCnt < 14) { 108 pos = xlfd.indexOf('-', pos); 109 if (pos != -1) { 110 hPos[hyphenCnt++] = pos; 111 pos++; 112 } 113 } 114 } 115 116 if (hyphenCnt == 14 && pos != -1) { 117 118 /* Capitalise words in the Family name */ 119 String tmpFamily = xlfd.substring(hPos[1]+1, hPos[2]); 120 StringBuilder sBuffer = new StringBuilder(tmpFamily); 121 char ch = Character.toUpperCase(sBuffer.charAt(0)); 122 sBuffer.replace(0, 1, String.valueOf(ch)); 123 for (int i=1;i<sBuffer.length()-1; i++) { 124 if (sBuffer.charAt(i) == ' ') { 125 ch = Character.toUpperCase(sBuffer.charAt(i+1)); 126 sBuffer.replace(i+1, i+2, String.valueOf(ch)); 127 } 128 } 129 familyName = sBuffer.toString(); 130 131 String tmpWeight = xlfd.substring(hPos[2]+1, hPos[3]); 132 String tmpSlant = xlfd.substring(hPos[3]+1, hPos[4]); 133 134 String styleStr = null; 135 136 if (tmpWeight.indexOf("bold") >= 0 || 137 tmpWeight.indexOf("demi") >= 0) { 138 style |= Font.BOLD; 139 styleStr = "Bold"; 140 } 141 142 if (tmpSlant.equals("i") || 143 tmpSlant.indexOf("italic") >= 0) { 144 style |= Font.ITALIC; 145 146 if (styleStr == null) { 147 styleStr = "Italic"; 148 } else { 149 styleStr = styleStr + " Italic"; 150 } 151 } 152 else if (tmpSlant.equals("o") || 153 tmpSlant.indexOf("oblique") >= 0) { 154 style |= Font.ITALIC; 155 if (styleStr == null) { 156 styleStr = "Oblique"; 157 } else { 158 styleStr = styleStr + " Oblique"; 159 } 160 } 161 162 if (styleStr == null) { 163 fullName = familyName; 164 } else { 165 fullName = familyName + " " + styleStr; 166 } 167 168 encoding = xlfd.substring(hPos[12]+1); 169 if (encoding.startsWith("-")) { 170 encoding = xlfd.substring(hPos[13]+1); 171 } 172 if (encoding.indexOf("fontspecific") >= 0) { 173 if (tmpFamily.indexOf("dingbats") >= 0) { 174 encoding = "dingbats"; 175 } else if (tmpFamily.indexOf("symbol") >= 0) { 176 encoding = "symbol"; 177 } else { 178 encoding = "iso8859-1"; 179 } 180 } 181 } else { 182 throw new FontFormatException("Bad native name " + platName); 183 // familyName = "Unknown"; 184 // fullName = "Unknown"; 185 // style = Font.PLAIN; 186 // encoding = "iso8859-1"; 187 } 188 } 189 190 /* Wildcard all the size fields in the XLFD and retrieve a list of 191 * XLFD's that match. 192 * We only look for scaleable fonts, so we can just replace the 0's 193 * with *'s and see what we get back 194 * No matches means even the scaleable version wasn't found. This is 195 * means the X font path isn't set up for this font at all. 196 * One match means only the scaleable version we started with was found 197 * -monotype-arial-bold-i-normal--0-0-0-0-p-0-iso8859-1 198 * Two matches apparently means as well as the above, a scaleable 199 * specified for 72 dpi is found, not that there are bitmaps : eg 200 * -monotype-arial-bold-i-normal--0-0-72-72-p-0-iso8859-1 201 * So require at least 3 matches (no need to parse) to determine that 202 * there are external bitmaps. 203 */ hasExternalBitmaps(String platName)204 static boolean hasExternalBitmaps(String platName) { 205 /* Turn -monotype-arial-bold-i-normal--0-0-0-0-p-0-iso8859-1 206 * into -monotype-arial-bold-i-normal--*-*-*-*-p-*-iso8859-1 207 * by replacing all -0- substrings with -*- 208 */ 209 StringBuilder sb = new StringBuilder(platName); 210 int pos = sb.indexOf("-0-"); 211 while (pos >=0) { 212 sb.replace(pos+1, pos+2, "*"); 213 pos = sb.indexOf("-0-", pos); 214 }; 215 String xlfd = sb.toString(); 216 byte[] bytes = null; 217 try { 218 bytes = xlfd.getBytes("UTF-8"); 219 } catch (UnsupportedEncodingException e) { 220 bytes = xlfd.getBytes(); 221 } 222 return haveBitmapFonts(bytes); 223 } 224 fontExists(String xlfd)225 public static boolean fontExists(String xlfd) { 226 byte[] bytes = null; 227 try { 228 bytes = xlfd.getBytes("UTF-8"); 229 } catch (UnsupportedEncodingException e) { 230 bytes = xlfd.getBytes(); 231 } 232 return fontExists(bytes); 233 } 234 haveBitmapFonts(byte[] xlfd)235 private static native boolean haveBitmapFonts(byte[] xlfd); fontExists(byte[] xlfd)236 private static native boolean fontExists(byte[] xlfd); 237 getMapper()238 public CharToGlyphMapper getMapper() { 239 if (mapper == null) { 240 if (isBitmapDelegate) { 241 /* we are a delegate */ 242 mapper = new NativeGlyphMapper(this); 243 } else { 244 /* we need to delegate */ 245 SunFontManager fm = SunFontManager.getInstance(); 246 delegateFont = fm.getDefaultPhysicalFont(); 247 mapper = delegateFont.getMapper(); 248 } 249 } 250 return mapper; 251 } 252 createStrike(FontStrikeDesc desc)253 FontStrike createStrike(FontStrikeDesc desc) { 254 if (isBitmapDelegate) { 255 return new NativeStrike(this, desc); 256 } else { 257 if (delegateFont == null) { 258 SunFontManager fm = SunFontManager.getInstance(); 259 delegateFont = fm.getDefaultPhysicalFont(); 260 } 261 /* If no FileFont's are found, delegate font may be 262 * a NativeFont, so we need to avoid recursing here. 263 */ 264 if (delegateFont instanceof NativeFont) { 265 return new NativeStrike((NativeFont)delegateFont, desc); 266 } 267 FontStrike delegate = delegateFont.createStrike(desc); 268 return new DelegateStrike(this, desc, delegate); 269 } 270 } 271 getMaxCharBounds(FontRenderContext frc)272 public Rectangle2D getMaxCharBounds(FontRenderContext frc) { 273 return null; 274 } 275 getFontMetrics(long pScalerContext)276 native StrikeMetrics getFontMetrics(long pScalerContext); 277 getGlyphAdvance(long pContext, int glyphCode)278 native float getGlyphAdvance(long pContext, int glyphCode); 279 getGlyphOutlineBounds(long pScalerContext, int glyphCode)280 Rectangle2D.Float getGlyphOutlineBounds(long pScalerContext, 281 int glyphCode) { 282 return new Rectangle2D.Float(0f, 0f, 0f, 0f); 283 } 284 getGlyphOutline(long pScalerContext, int glyphCode, float x, float y)285 public GeneralPath getGlyphOutline(long pScalerContext, 286 int glyphCode, 287 float x, 288 float y) { 289 return null; 290 } 291 getGlyphImage(long pScalerContext, int glyphCode)292 native long getGlyphImage(long pScalerContext, int glyphCode); 293 getGlyphImageNoDefault(long pScalerContext, int glyphCode)294 native long getGlyphImageNoDefault(long pScalerContext, int glyphCode); 295 getGlyphMetrics(long pScalerContext, int glyphCode, Point2D.Float metrics)296 void getGlyphMetrics(long pScalerContext, int glyphCode, 297 Point2D.Float metrics) { 298 throw new RuntimeException("this should be called on the strike"); 299 } 300 getGlyphVectorOutline(long pScalerContext, int[] glyphs, int numGlyphs, float x, float y)301 public GeneralPath getGlyphVectorOutline(long pScalerContext, 302 int[] glyphs, int numGlyphs, 303 float x, float y) { 304 return null; 305 } 306 countGlyphs(byte[] platformNameBytes, int ptSize)307 private native int countGlyphs(byte[] platformNameBytes, int ptSize); 308 getNumGlyphs()309 public int getNumGlyphs() { 310 if (numGlyphs == -1) { 311 byte[] bytes = getPlatformNameBytes(8); 312 numGlyphs = countGlyphs(bytes, 8); 313 } 314 return numGlyphs; 315 } 316 getDelegateFont()317 PhysicalFont getDelegateFont() { 318 if (delegateFont == null) { 319 SunFontManager fm = SunFontManager.getInstance(); 320 delegateFont = fm.getDefaultPhysicalFont(); 321 } 322 return delegateFont; 323 } 324 325 /* Specify that the dpi is 72x72, as this corresponds to JDK's 326 * default user space. These are the 10th and 11th fields in the XLFD. 327 * ptSize in XLFD is in 10th's of a point so multiply by 10, 328 * Replace the 9th field in the XLFD (ie after the 8th hyphen) 329 * with this pt size (this corresponds to the field that's "%d" in the 330 * font configuration files). Wild card the other numeric fields. 331 * ie to request 12 pt Times New Roman italic font, use an XLFD like : 332 * -monotype-times new roman-regular-i---*-120-72-72-p-*-iso8859-1 333 */ getPlatformNameBytes(int ptSize)334 byte[] getPlatformNameBytes(int ptSize) { 335 int[] hPos = new int[14]; 336 int hyphenCnt = 1; 337 int pos = 1; 338 339 while (pos != -1 && hyphenCnt < 14) { 340 pos = platName.indexOf('-', pos); 341 if (pos != -1) { 342 hPos[hyphenCnt++] = pos; 343 pos++; 344 } 345 } 346 String sizeStr = Integer.toString((int)Math.abs(ptSize)*10); 347 StringBuilder sb = new StringBuilder(platName); 348 /* work backwards so as to not invalidate the positions. */ 349 sb.replace(hPos[11]+1, hPos[12], "*"); 350 351 sb.replace(hPos[9]+1, hPos[10], "72"); 352 353 sb.replace(hPos[8]+1, hPos[9], "72"); 354 355 /* replace the 3 lines above with the next 3 lines to get the 1.4.2 356 * behaviour 357 */ 358 // sb.replace(hPos[11]+1, hPos[12], "0"); 359 // sb.replace(hPos[9]+1, hPos[10], "0"); 360 // sb.replace(hPos[8]+1, hPos[9], "0"); 361 362 sb.replace(hPos[7]+1, hPos[8], sizeStr); 363 364 sb.replace(hPos[6]+1, hPos[7], "*"); 365 366 /* replace the 1 line above with the next line to get the 1.4.2 367 * behaviour 368 */ 369 // sb.replace(hPos[6]+1, hPos[7], "0"); 370 371 /* comment out this block to the the 1.4.2 behaviour */ 372 if (hPos[0] == 0 && hPos[1] == 1) { 373 /* null foundry name : some linux font configuration files have 374 * symbol font entries like this and its just plain wrong. 375 * Replace with a wild card. (Although those fonts should be 376 * located via disk access rather than X11). 377 */ 378 sb.replace(hPos[0]+1, hPos[1], "*"); 379 } 380 381 String xlfd = sb.toString(); 382 byte[] bytes = null; 383 try { 384 bytes = xlfd.getBytes("UTF-8"); 385 } catch (UnsupportedEncodingException e) { 386 bytes = xlfd.getBytes(); 387 } 388 return bytes; 389 } 390 toString()391 public String toString() { 392 return " ** Native Font: Family="+familyName+ " Name="+fullName+ 393 " style="+style+" nativeName="+platName; 394 } 395 } 396