1/* 2 * Copyright (c) 2003, 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. 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 26package sun.font; 27 28import java.lang.ref.Reference; 29import java.awt.FontFormatException; 30import java.awt.geom.GeneralPath; 31import java.awt.geom.Point2D; 32import java.awt.geom.Rectangle2D; 33import java.io.File; 34import java.nio.ByteBuffer; 35import sun.java2d.Disposer; 36import sun.java2d.DisposerRecord; 37 38import java.io.IOException; 39import java.util.List; 40import java.security.AccessController; 41import java.security.PrivilegedActionException; 42import java.security.PrivilegedExceptionAction; 43 44public abstract class FileFont extends PhysicalFont { 45 46 protected boolean useJavaRasterizer = true; 47 48 /* I/O and file operations are always synchronized on the font 49 * object. Two threads can be accessing the font and retrieving 50 * information, and synchronized only to the extent that filesystem 51 * operations require. 52 * A limited number of files can be open at a time, to limit the 53 * absorption of file descriptors. If a file needs to be opened 54 * when there are none free, then the synchronization of all I/O 55 * ensures that any in progress operation will complete before some 56 * other thread closes the descriptor in order to allocate another one. 57 */ 58 // NB consider using a RAF. FIS has finalize method so may take a 59 // little longer to be GC'd. We don't use this stream at all anyway. 60 // In fact why increase the size of a FileFont object if the stream 61 // isn't needed .. 62 //protected FileInputStream stream; 63 //protected FileChannel channel; 64 protected int fileSize; 65 66 protected FontScaler scaler; 67 68 /* The following variables are used, (and in the case of the arrays, 69 * only initialised) for select fonts where a native scaler may be 70 * used to get glyph images and metrics. 71 * glyphToCharMap is filled in on the fly and used to do a reverse 72 * lookup when a FileFont needs to get the charcode back from a glyph 73 * code so it can re-map via a NativeGlyphMapper to get a native glyph. 74 * This isn't a big hit in time, since a boolean test is sufficient 75 * to choose the usual default path, nor in memory for fonts which take 76 * the native path, since fonts have contiguous zero-based glyph indexes, 77 * and these obviously do all exist in the font. 78 */ 79 protected boolean checkedNatives; 80 protected boolean useNatives; 81 protected NativeFont[] nativeFonts; 82 protected char[] glyphToCharMap; 83 /* 84 * @throws FontFormatException if the font can't be opened 85 */ 86 FileFont(String platname, Object nativeNames) 87 throws FontFormatException { 88 89 super(platname, nativeNames); 90 } 91 92 FontStrike createStrike(FontStrikeDesc desc) { 93 if (!checkedNatives) { 94 checkUseNatives(); 95 } 96 return new FileFontStrike(this, desc); 97 } 98 99 protected boolean checkUseNatives() { 100 checkedNatives = true; 101 return useNatives; 102 } 103 104 /* This method needs to be accessible to FontManager if there is 105 * file pool management. It may be a no-op. 106 */ 107 protected abstract void close(); 108 109 110 /* 111 * This is the public interface. The subclasses need to implement 112 * this. The returned block may be longer than the requested length. 113 */ 114 abstract ByteBuffer readBlock(int offset, int length); 115 116 public boolean canDoStyle(int style) { 117 return true; 118 } 119 120 static void setFileToRemove(List<Font2D> fonts, 121 File file, int cnt, 122 CreatedFontTracker tracker) 123 { 124 CreatedFontFileDisposerRecord dr = 125 new CreatedFontFileDisposerRecord(file, cnt, tracker); 126 127 for (Font2D f : fonts) { 128 Disposer.addObjectRecord(f, dr); 129 } 130 } 131 132 /* This is called when a font scaler is determined to 133 * be unusable (ie bad). 134 * We want to replace current scaler with NullFontScaler, so 135 * we never try to use same font scaler again. 136 * Scaler native resources could have already been disposed 137 * or they will be eventually by Java2D disposer. 138 * However, it should be safe to call dispose() explicitly here. 139 * 140 * For safety we also invalidate all strike's scaler context. 141 * So, in case they cache pointer to native scaler 142 * it will not ever be used. 143 * 144 * It also appears desirable to remove all the entries from the 145 * cache so no other code will pick them up. But we can't just 146 * 'delete' them as code may be using them. And simply dropping 147 * the reference to the cache will make the reference objects 148 * unreachable and so they will not get disposed. 149 * Since a strike may hold (via java arrays) native pointers to many 150 * rasterised glyphs, this would be a memory leak. 151 * The solution is : 152 * - to move all the entries to another map where they 153 * are no longer locatable 154 * - update FontStrikeDisposer to be able to distinguish which 155 * map they are held in via a boolean flag 156 * Since this isn't expected to be anything other than an extremely 157 * rare maybe it is not worth doing this last part. 158 */ 159 synchronized void deregisterFontAndClearStrikeCache() { 160 SunFontManager fm = SunFontManager.getInstance(); 161 fm.deRegisterBadFont(this); 162 163 for (Reference<FontStrike> strikeRef : strikeCache.values()) { 164 if (strikeRef != null) { 165 /* NB we know these are all FileFontStrike instances 166 * because the cache is on this FileFont 167 */ 168 FileFontStrike strike = (FileFontStrike)strikeRef.get(); 169 if (strike != null && strike.pScalerContext != 0L) { 170 scaler.invalidateScalerContext(strike.pScalerContext); 171 } 172 } 173 } 174 if (scaler != null) { 175 scaler.dispose(); 176 } 177 scaler = FontScaler.getNullScaler(); 178 } 179 180 StrikeMetrics getFontMetrics(long pScalerContext) { 181 try { 182 return getScaler().getFontMetrics(pScalerContext); 183 } catch (FontScalerException fe) { 184 scaler = FontScaler.getNullScaler(); 185 return getFontMetrics(pScalerContext); 186 } 187 } 188 189 float getGlyphAdvance(long pScalerContext, int glyphCode) { 190 try { 191 return getScaler().getGlyphAdvance(pScalerContext, glyphCode); 192 } catch (FontScalerException fe) { 193 scaler = FontScaler.getNullScaler(); 194 return getGlyphAdvance(pScalerContext, glyphCode); 195 } 196 } 197 198 void getGlyphMetrics(long pScalerContext, int glyphCode, Point2D.Float metrics) { 199 try { 200 getScaler().getGlyphMetrics(pScalerContext, glyphCode, metrics); 201 } catch (FontScalerException fe) { 202 scaler = FontScaler.getNullScaler(); 203 getGlyphMetrics(pScalerContext, glyphCode, metrics); 204 } 205 } 206 207 long getGlyphImage(long pScalerContext, int glyphCode) { 208 try { 209 return getScaler().getGlyphImage(pScalerContext, glyphCode); 210 } catch (FontScalerException fe) { 211 scaler = FontScaler.getNullScaler(); 212 return getGlyphImage(pScalerContext, glyphCode); 213 } 214 } 215 216 Rectangle2D.Float getGlyphOutlineBounds(long pScalerContext, int glyphCode) { 217 try { 218 return getScaler().getGlyphOutlineBounds(pScalerContext, glyphCode); 219 } catch (FontScalerException fe) { 220 scaler = FontScaler.getNullScaler(); 221 return getGlyphOutlineBounds(pScalerContext, glyphCode); 222 } 223 } 224 225 GeneralPath getGlyphOutline(long pScalerContext, int glyphCode, float x, float y) { 226 try { 227 return getScaler().getGlyphOutline(pScalerContext, glyphCode, x, y); 228 } catch (FontScalerException fe) { 229 scaler = FontScaler.getNullScaler(); 230 return getGlyphOutline(pScalerContext, glyphCode, x, y); 231 } 232 } 233 234 GeneralPath getGlyphVectorOutline(long pScalerContext, int[] glyphs, int numGlyphs, float x, float y) { 235 try { 236 return getScaler().getGlyphVectorOutline(pScalerContext, glyphs, numGlyphs, x, y); 237 } catch (FontScalerException fe) { 238 scaler = FontScaler.getNullScaler(); 239 return getGlyphVectorOutline(pScalerContext, glyphs, numGlyphs, x, y); 240 } 241 } 242 243 /* T1 & TT implementation differ so this method is abstract. 244 NB: null should not be returned here! */ 245 protected abstract FontScaler getScaler(); 246 247 protected long getUnitsPerEm() { 248 return getScaler().getUnitsPerEm(); 249 } 250 251 private static class CreatedFontFileDisposerRecord 252 implements DisposerRecord { 253 254 File fontFile = null; 255 int count = 0; // number of fonts referencing this file object. 256 CreatedFontTracker tracker; 257 258 private CreatedFontFileDisposerRecord(File file, int cnt, 259 CreatedFontTracker tracker) { 260 fontFile = file; 261 count = (cnt > 0) ? cnt : 1; 262 this.tracker = tracker; 263 } 264 265 public void dispose() { 266 java.security.AccessController.doPrivileged( 267 new java.security.PrivilegedAction<Object>() { 268 public Object run() { 269 synchronized (fontFile) { 270 count--; 271 if (count > 0) { 272 return null; 273 } 274 } 275 if (fontFile != null) { 276 try { 277 if (tracker != null) { 278 tracker.subBytes((int)fontFile.length()); 279 } 280 /* REMIND: is it possible that the file is 281 * still open? It will be closed when the 282 * font2D is disposed but could this code 283 * execute first? If so the file would not 284 * be deleted on MS-windows. 285 */ 286 fontFile.delete(); 287 /* remove from delete on exit hook list : */ 288 // FIXME: still need to be refactored 289 SunFontManager.getInstance().tmpFontFiles.remove(fontFile); 290 } catch (Exception e) { 291 } 292 } 293 return null; 294 } 295 }); 296 } 297 } 298 299 protected String getPublicFileName() { 300 SecurityManager sm = System.getSecurityManager(); 301 if (sm == null) { 302 return platName; 303 } 304 boolean canReadProperty = true; 305 306 try { 307 sm.checkPropertyAccess("java.io.tmpdir"); 308 } catch (SecurityException e) { 309 canReadProperty = false; 310 } 311 312 if (canReadProperty) { 313 return platName; 314 } 315 316 final File f = new File(platName); 317 318 Boolean isTmpFile = Boolean.FALSE; 319 try { 320 isTmpFile = AccessController.doPrivileged( 321 new PrivilegedExceptionAction<Boolean>() { 322 public Boolean run() { 323 File tmp = new File(System.getProperty("java.io.tmpdir")); 324 try { 325 String tpath = tmp.getCanonicalPath(); 326 String fpath = f.getCanonicalPath(); 327 328 return (fpath == null) || fpath.startsWith(tpath); 329 } catch (IOException e) { 330 return Boolean.TRUE; 331 } 332 } 333 } 334 ); 335 } catch (PrivilegedActionException e) { 336 // unable to verify whether value of java.io.tempdir will be 337 // exposed, so return only a name of the font file. 338 isTmpFile = Boolean.TRUE; 339 } 340 341 return isTmpFile ? "temp file" : platName; 342 } 343} 344