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