1 /* 2 * Copyright (c) 2003, 2020, 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.util.List; 40 import java.security.AccessController; 41 import java.security.PrivilegedActionException; 42 import java.security.PrivilegedExceptionAction; 43 44 public 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 NativeFont[] nativeFonts; 80 protected char[] glyphToCharMap; 81 /* 82 * @throws FontFormatException if the font can't be opened 83 */ 84 FileFont(String platname, Object nativeNames) 85 throws FontFormatException { 86 87 super(platname, nativeNames); 88 } 89 90 FontStrike createStrike(FontStrikeDesc desc) { 91 return new FileFontStrike(this, desc); 92 } 93 94 /* This method needs to be accessible to FontManager if there is 95 * file pool management. It may be a no-op. 96 */ 97 protected abstract void close(); 98 99 100 /* 101 * This is the public interface. The subclasses need to implement 102 * this. The returned block may be longer than the requested length. 103 */ 104 abstract ByteBuffer readBlock(int offset, int length); 105 106 public boolean canDoStyle(int style) { 107 return true; 108 } 109 110 static void setFileToRemove(List<Font2D> fonts, 111 File file, int cnt, 112 CreatedFontTracker tracker) 113 { 114 CreatedFontFileDisposerRecord dr = 115 new CreatedFontFileDisposerRecord(file, cnt, tracker); 116 117 for (Font2D f : fonts) { 118 Disposer.addObjectRecord(f, dr); 119 } 120 } 121 122 /* This is called when a font scaler is determined to 123 * be unusable (ie bad). 124 * We want to replace current scaler with NullFontScaler, so 125 * we never try to use same font scaler again. 126 * Scaler native resources could have already been disposed 127 * or they will be eventually by Java2D disposer. 128 * However, it should be safe to call dispose() explicitly here. 129 * 130 * For safety we also invalidate all strike's scaler context. 131 * So, in case they cache pointer to native scaler 132 * it will not ever be used. 133 * 134 * It also appears desirable to remove all the entries from the 135 * cache so no other code will pick them up. But we can't just 136 * 'delete' them as code may be using them. And simply dropping 137 * the reference to the cache will make the reference objects 138 * unreachable and so they will not get disposed. 139 * Since a strike may hold (via java arrays) native pointers to many 140 * rasterised glyphs, this would be a memory leak. 141 * The solution is : 142 * - to move all the entries to another map where they 143 * are no longer locatable 144 * - update FontStrikeDisposer to be able to distinguish which 145 * map they are held in via a boolean flag 146 * Since this isn't expected to be anything other than an extremely 147 * rare maybe it is not worth doing this last part. 148 */ 149 synchronized void deregisterFontAndClearStrikeCache() { 150 SunFontManager fm = SunFontManager.getInstance(); 151 fm.deRegisterBadFont(this); 152 153 for (Reference<FontStrike> strikeRef : strikeCache.values()) { 154 if (strikeRef != null) { 155 /* NB we know these are all FileFontStrike instances 156 * because the cache is on this FileFont 157 */ 158 FileFontStrike strike = (FileFontStrike)strikeRef.get(); 159 if (strike != null && strike.pScalerContext != 0L) { 160 scaler.invalidateScalerContext(strike.pScalerContext); 161 } 162 } 163 } 164 if (scaler != null) { 165 scaler.disposeScaler(); 166 } 167 scaler = FontScaler.getNullScaler(); 168 } 169 170 StrikeMetrics getFontMetrics(long pScalerContext) { 171 try { 172 return getScaler().getFontMetrics(pScalerContext); 173 } catch (FontScalerException fe) { 174 scaler = FontScaler.getNullScaler(); 175 return getFontMetrics(pScalerContext); 176 } 177 } 178 179 float getGlyphAdvance(long pScalerContext, int glyphCode) { 180 try { 181 return getScaler().getGlyphAdvance(pScalerContext, glyphCode); 182 } catch (FontScalerException fe) { 183 scaler = FontScaler.getNullScaler(); 184 return getGlyphAdvance(pScalerContext, glyphCode); 185 } 186 } 187 188 void getGlyphMetrics(long pScalerContext, int glyphCode, Point2D.Float metrics) { 189 try { 190 getScaler().getGlyphMetrics(pScalerContext, glyphCode, metrics); 191 } catch (FontScalerException fe) { 192 scaler = FontScaler.getNullScaler(); 193 getGlyphMetrics(pScalerContext, glyphCode, metrics); 194 } 195 } 196 197 long getGlyphImage(long pScalerContext, int glyphCode) { 198 try { 199 return getScaler().getGlyphImage(pScalerContext, glyphCode); 200 } catch (FontScalerException fe) { 201 scaler = FontScaler.getNullScaler(); 202 return getGlyphImage(pScalerContext, glyphCode); 203 } 204 } 205 206 Rectangle2D.Float getGlyphOutlineBounds(long pScalerContext, int glyphCode) { 207 try { 208 return getScaler().getGlyphOutlineBounds(pScalerContext, glyphCode); 209 } catch (FontScalerException fe) { 210 scaler = FontScaler.getNullScaler(); 211 return getGlyphOutlineBounds(pScalerContext, glyphCode); 212 } 213 } 214 215 GeneralPath getGlyphOutline(long pScalerContext, int glyphCode, float x, float y) { 216 try { 217 return getScaler().getGlyphOutline(pScalerContext, glyphCode, x, y); 218 } catch (FontScalerException fe) { 219 scaler = FontScaler.getNullScaler(); 220 return getGlyphOutline(pScalerContext, glyphCode, x, y); 221 } 222 } 223 224 GeneralPath getGlyphVectorOutline(long pScalerContext, int[] glyphs, int numGlyphs, float x, float y) { 225 try { 226 return getScaler().getGlyphVectorOutline(pScalerContext, glyphs, numGlyphs, x, y); 227 } catch (FontScalerException fe) { 228 scaler = FontScaler.getNullScaler(); 229 return getGlyphVectorOutline(pScalerContext, glyphs, numGlyphs, x, y); 230 } 231 } 232 233 /* T1 & TT implementation differ so this method is abstract. 234 NB: null should not be returned here! */ 235 protected abstract FontScaler getScaler(); 236 237 protected long getUnitsPerEm() { 238 return getScaler().getUnitsPerEm(); 239 } 240 241 private static class CreatedFontFileDisposerRecord 242 implements DisposerRecord { 243 244 File fontFile = null; 245 int count = 0; // number of fonts referencing this file object. 246 CreatedFontTracker tracker; 247 248 private CreatedFontFileDisposerRecord(File file, int cnt, 249 CreatedFontTracker tracker) { 250 fontFile = file; 251 count = (cnt > 0) ? cnt : 1; 252 this.tracker = tracker; 253 } 254 255 public void dispose() { 256 java.security.AccessController.doPrivileged( 257 new java.security.PrivilegedAction<Object>() { 258 public Object run() { 259 synchronized (fontFile) { 260 count--; 261 if (count > 0) { 262 return null; 263 } 264 } 265 if (fontFile != null) { 266 try { 267 if (tracker != null) { 268 tracker.subBytes((int)fontFile.length()); 269 } 270 /* REMIND: is it possible that the file is 271 * still open? It will be closed when the 272 * font2D is disposed but could this code 273 * execute first? If so the file would not 274 * be deleted on MS-windows. 275 */ 276 fontFile.delete(); 277 /* remove from delete on exit hook list : */ 278 // FIXME: still need to be refactored 279 SunFontManager.getInstance().tmpFontFiles.remove(fontFile); 280 } catch (Exception e) { 281 } 282 } 283 return null; 284 } 285 }); 286 } 287 } 288 289 protected String getPublicFileName() { 290 SecurityManager sm = System.getSecurityManager(); 291 if (sm == null) { 292 return platName; 293 } 294 boolean canReadProperty = true; 295 296 try { 297 sm.checkPropertyAccess("java.io.tmpdir"); 298 } catch (SecurityException e) { 299 canReadProperty = false; 300 } 301 302 if (canReadProperty) { 303 return platName; 304 } 305 306 final File f = new File(platName); 307 308 Boolean isTmpFile = Boolean.FALSE; 309 try { 310 isTmpFile = AccessController.doPrivileged( 311 new PrivilegedExceptionAction<Boolean>() { 312 public Boolean run() { 313 File tmp = new File(System.getProperty("java.io.tmpdir")); 314 try { 315 String tpath = tmp.getCanonicalPath(); 316 String fpath = f.getCanonicalPath(); 317 318 return (fpath == null) || fpath.startsWith(tpath); 319 } catch (IOException e) { 320 return Boolean.TRUE; 321 } 322 } 323 } 324 ); 325 } catch (PrivilegedActionException e) { 326 // unable to verify whether value of java.io.tempdir will be 327 // exposed, so return only a name of the font file. 328 isTmpFile = Boolean.TRUE; 329 } 330 331 return isTmpFile ? "temp file" : platName; 332 } 333 }