1 /* 2 * Copyright 2003-2008 Sun Microsystems, Inc. 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. Sun designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 22 * CA 95054 USA or visit www.sun.com if you need additional information or 23 * have any 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 java.nio.channels.FileChannel; 36 import sun.java2d.Disposer; 37 import sun.java2d.DisposerRecord; 38 39 import java.lang.ref.WeakReference; 40 import java.io.FileNotFoundException; 41 import java.io.IOException; 42 import java.io.RandomAccessFile; 43 import java.io.UnsupportedEncodingException; 44 import java.nio.ByteOrder; 45 import java.nio.MappedByteBuffer; 46 import java.nio.BufferUnderflowException; 47 import java.nio.channels.ClosedChannelException; 48 import java.util.HashSet; 49 import java.util.HashMap; 50 import java.awt.Font; 51 52 public abstract class FileFont extends PhysicalFont { 53 54 protected boolean useJavaRasterizer = true; 55 56 /* I/O and file operations are always synchronized on the font 57 * object. Two threads can be accessing the font and retrieving 58 * information, and synchronized only to the extent that filesystem 59 * operations require. 60 * A limited number of files can be open at a time, to limit the 61 * absorption of file descriptors. If a file needs to be opened 62 * when there are none free, then the synchronization of all I/O 63 * ensures that any in progress operation will complete before some 64 * other thread closes the descriptor in order to allocate another one. 65 */ 66 // NB consider using a RAF. FIS has finalize method so may take a 67 // little longer to be GC'd. We don't use this stream at all anyway. 68 // In fact why increase the size of a FileFont object if the stream 69 // isn't needed .. 70 //protected FileInputStream stream; 71 //protected FileChannel channel; 72 protected int fileSize; 73 74 protected FontScaler scaler; 75 76 /* The following variables are used, (and in the case of the arrays, 77 * only initialised) for select fonts where a native scaler may be 78 * used to get glyph images and metrics. 79 * glyphToCharMap is filled in on the fly and used to do a reverse 80 * lookup when a FileFont needs to get the charcode back from a glyph 81 * code so it can re-map via a NativeGlyphMapper to get a native glyph. 82 * This isn't a big hit in time, since a boolean test is sufficient 83 * to choose the usual default path, nor in memory for fonts which take 84 * the native path, since fonts have contiguous zero-based glyph indexes, 85 * and these obviously do all exist in the font. 86 */ 87 protected boolean checkedNatives; 88 protected boolean useNatives; 89 protected NativeFont[] nativeFonts; 90 protected char[] glyphToCharMap; 91 /* 92 * @throws FontFormatException - if the font can't be opened 93 */ 94 FileFont(String platname, Object nativeNames) 95 throws FontFormatException { 96 97 super(platname, nativeNames); 98 } 99 100 FontStrike createStrike(FontStrikeDesc desc) { 101 if (!checkedNatives) { 102 checkUseNatives(); 103 } 104 return new FileFontStrike(this, desc); 105 } 106 107 protected boolean checkUseNatives() { 108 checkedNatives = true; 109 return useNatives; 110 } 111 112 /* This method needs to be accessible to FontManager if there is 113 * file pool management. It may be a no-op. 114 */ 115 protected abstract void close(); 116 117 118 /* 119 * This is the public interface. The subclasses need to implement 120 * this. The returned block may be longer than the requested length. 121 */ 122 abstract ByteBuffer readBlock(int offset, int length); 123 124 public boolean canDoStyle(int style) { 125 return true; 126 } 127 128 void setFileToRemove(File file, CreatedFontTracker tracker) { 129 Disposer.addObjectRecord(this, 130 new CreatedFontFileDisposerRecord(file, tracker)); 131 } 132 133 /* This is called when a font scaler is determined to 134 * be unusable (ie bad). 135 * We want to replace current scaler with NullFontScaler, so 136 * we never try to use same font scaler again. 137 * Scaler native resources could have already been disposed 138 * or they will be eventually by Java2D disposer. 139 * However, it should be safe to call dispose() explicitly here. 140 * 141 * For safety we also invalidate all strike's scaler context. 142 * So, in case they cache pointer to native scaler 143 * it will not ever be used. 144 * 145 * It also appears desirable to remove all the entries from the 146 * cache so no other code will pick them up. But we can't just 147 * 'delete' them as code may be using them. And simply dropping 148 * the reference to the cache will make the reference objects 149 * unreachable and so they will not get disposed. 150 * Since a strike may hold (via java arrays) native pointers to many 151 * rasterised glyphs, this would be a memory leak. 152 * The solution is : 153 * - to move all the entries to another map where they 154 * are no longer locatable 155 * - update FontStrikeDisposer to be able to distinguish which 156 * map they are held in via a boolean flag 157 * Since this isn't expected to be anything other than an extremely 158 * rare maybe it is not worth doing this last part. 159 */ 160 synchronized void deregisterFontAndClearStrikeCache() { 161 FontManager.deRegisterBadFont(this); 162 163 for (Reference 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 scaler.dispose(); 175 scaler = FontManager.getNullScaler(); 176 } 177 178 StrikeMetrics getFontMetrics(long pScalerContext) { 179 try { 180 return getScaler().getFontMetrics(pScalerContext); 181 } catch (FontScalerException fe) { 182 scaler = FontManager.getNullScaler(); 183 return getFontMetrics(pScalerContext); 184 } 185 } 186 187 float getGlyphAdvance(long pScalerContext, int glyphCode) { 188 try { 189 return getScaler().getGlyphAdvance(pScalerContext, glyphCode); 190 } catch (FontScalerException fe) { 191 scaler = FontManager.getNullScaler(); 192 return getGlyphAdvance(pScalerContext, glyphCode); 193 } 194 } 195 196 void getGlyphMetrics(long pScalerContext, int glyphCode, Point2D.Float metrics) { 197 try { 198 getScaler().getGlyphMetrics(pScalerContext, glyphCode, metrics); 199 } catch (FontScalerException fe) { 200 scaler = FontManager.getNullScaler(); 201 getGlyphMetrics(pScalerContext, glyphCode, metrics); 202 } 203 } 204 205 long getGlyphImage(long pScalerContext, int glyphCode) { 206 try { 207 return getScaler().getGlyphImage(pScalerContext, glyphCode); 208 } catch (FontScalerException fe) { 209 scaler = FontManager.getNullScaler(); 210 return getGlyphImage(pScalerContext, glyphCode); 211 } 212 } 213 214 Rectangle2D.Float getGlyphOutlineBounds(long pScalerContext, int glyphCode) { 215 try { 216 return getScaler().getGlyphOutlineBounds(pScalerContext, glyphCode); 217 } catch (FontScalerException fe) { 218 scaler = FontManager.getNullScaler(); 219 return getGlyphOutlineBounds(pScalerContext, glyphCode); 220 } 221 } 222 223 GeneralPath getGlyphOutline(long pScalerContext, int glyphCode, float x, float y) { 224 try { 225 return getScaler().getGlyphOutline(pScalerContext, glyphCode, x, y); 226 } catch (FontScalerException fe) { 227 scaler = FontManager.getNullScaler(); 228 return getGlyphOutline(pScalerContext, glyphCode, x, y); 229 } 230 } 231 232 GeneralPath getGlyphVectorOutline(long pScalerContext, int[] glyphs, int numGlyphs, float x, float y) { 233 try { 234 return getScaler().getGlyphVectorOutline(pScalerContext, glyphs, numGlyphs, x, y); 235 } catch (FontScalerException fe) { 236 scaler = FontManager.getNullScaler(); 237 return getGlyphVectorOutline(pScalerContext, glyphs, numGlyphs, x, y); 238 } 239 } 240 241 /* T1 & TT implementation differ so this method is abstract. 242 NB: null should not be returned here! */ 243 protected abstract FontScaler getScaler(); 244 245 protected long getUnitsPerEm() { 246 return getScaler().getUnitsPerEm(); 247 } 248 249 private static class CreatedFontFileDisposerRecord 250 implements DisposerRecord { 251 252 File fontFile = null; 253 CreatedFontTracker tracker; 254 255 private CreatedFontFileDisposerRecord(File file, 256 CreatedFontTracker tracker) { 257 fontFile = file; 258 this.tracker = tracker; 259 } 260 261 public void dispose() { 262 java.security.AccessController.doPrivileged( 263 new java.security.PrivilegedAction() { 264 public Object run() { 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 FontManager.tmpFontFiles.remove(fontFile); 279 } catch (Exception e) { 280 } 281 } 282 return null; 283 } 284 }); 285 } 286 } 287 }