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 }