/* * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.font; import java.lang.ref.Reference; import java.awt.FontFormatException; import java.awt.geom.GeneralPath; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.File; import java.nio.ByteBuffer; import sun.java2d.Disposer; import sun.java2d.DisposerRecord; import java.io.IOException; import java.security.AccessController; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; public abstract class FileFont extends PhysicalFont { protected boolean useJavaRasterizer = true; /* I/O and file operations are always synchronized on the font * object. Two threads can be accessing the font and retrieving * information, and synchronized only to the extent that filesystem * operations require. * A limited number of files can be open at a time, to limit the * absorption of file descriptors. If a file needs to be opened * when there are none free, then the synchronization of all I/O * ensures that any in progress operation will complete before some * other thread closes the descriptor in order to allocate another one. */ // NB consider using a RAF. FIS has finalize method so may take a // little longer to be GC'd. We don't use this stream at all anyway. // In fact why increase the size of a FileFont object if the stream // isn't needed .. //protected FileInputStream stream; //protected FileChannel channel; protected int fileSize; protected FontScaler scaler; /* The following variables are used, (and in the case of the arrays, * only initialised) for select fonts where a native scaler may be * used to get glyph images and metrics. * glyphToCharMap is filled in on the fly and used to do a reverse * lookup when a FileFont needs to get the charcode back from a glyph * code so it can re-map via a NativeGlyphMapper to get a native glyph. * This isn't a big hit in time, since a boolean test is sufficient * to choose the usual default path, nor in memory for fonts which take * the native path, since fonts have contiguous zero-based glyph indexes, * and these obviously do all exist in the font. */ protected boolean checkedNatives; protected boolean useNatives; protected NativeFont[] nativeFonts; protected char[] glyphToCharMap; /* * @throws FontFormatException - if the font can't be opened */ FileFont(String platname, Object nativeNames) throws FontFormatException { super(platname, nativeNames); } FontStrike createStrike(FontStrikeDesc desc) { if (!checkedNatives) { checkUseNatives(); } return new FileFontStrike(this, desc); } protected boolean checkUseNatives() { checkedNatives = true; return useNatives; } /* This method needs to be accessible to FontManager if there is * file pool management. It may be a no-op. */ protected abstract void close(); /* * This is the public interface. The subclasses need to implement * this. The returned block may be longer than the requested length. */ abstract ByteBuffer readBlock(int offset, int length); public boolean canDoStyle(int style) { return true; } void setFileToRemove(File file, CreatedFontTracker tracker) { Disposer.addObjectRecord(this, new CreatedFontFileDisposerRecord(file, tracker)); } // MACOSX begin -- Make this static so that we can pass in CFont static void setFileToRemove(Object font, File file, CreatedFontTracker tracker) { Disposer.addObjectRecord(font, new CreatedFontFileDisposerRecord(file, tracker)); } // MACOSX - end /* This is called when a font scaler is determined to * be unusable (ie bad). * We want to replace current scaler with NullFontScaler, so * we never try to use same font scaler again. * Scaler native resources could have already been disposed * or they will be eventually by Java2D disposer. * However, it should be safe to call dispose() explicitly here. * * For safety we also invalidate all strike's scaler context. * So, in case they cache pointer to native scaler * it will not ever be used. * * It also appears desirable to remove all the entries from the * cache so no other code will pick them up. But we can't just * 'delete' them as code may be using them. And simply dropping * the reference to the cache will make the reference objects * unreachable and so they will not get disposed. * Since a strike may hold (via java arrays) native pointers to many * rasterised glyphs, this would be a memory leak. * The solution is : * - to move all the entries to another map where they * are no longer locatable * - update FontStrikeDisposer to be able to distinguish which * map they are held in via a boolean flag * Since this isn't expected to be anything other than an extremely * rare maybe it is not worth doing this last part. */ synchronized void deregisterFontAndClearStrikeCache() { SunFontManager fm = SunFontManager.getInstance(); fm.deRegisterBadFont(this); for (Reference strikeRef : strikeCache.values()) { if (strikeRef != null) { /* NB we know these are all FileFontStrike instances * because the cache is on this FileFont */ FileFontStrike strike = (FileFontStrike)strikeRef.get(); if (strike != null && strike.pScalerContext != 0L) { scaler.invalidateScalerContext(strike.pScalerContext); } } } if (scaler != null) { scaler.dispose(); } scaler = FontScaler.getNullScaler(); } StrikeMetrics getFontMetrics(long pScalerContext) { try { return getScaler().getFontMetrics(pScalerContext); } catch (FontScalerException fe) { scaler = FontScaler.getNullScaler(); return getFontMetrics(pScalerContext); } } float getGlyphAdvance(long pScalerContext, int glyphCode) { try { return getScaler().getGlyphAdvance(pScalerContext, glyphCode); } catch (FontScalerException fe) { scaler = FontScaler.getNullScaler(); return getGlyphAdvance(pScalerContext, glyphCode); } } void getGlyphMetrics(long pScalerContext, int glyphCode, Point2D.Float metrics) { try { getScaler().getGlyphMetrics(pScalerContext, glyphCode, metrics); } catch (FontScalerException fe) { scaler = FontScaler.getNullScaler(); getGlyphMetrics(pScalerContext, glyphCode, metrics); } } long getGlyphImage(long pScalerContext, int glyphCode) { try { return getScaler().getGlyphImage(pScalerContext, glyphCode); } catch (FontScalerException fe) { scaler = FontScaler.getNullScaler(); return getGlyphImage(pScalerContext, glyphCode); } } Rectangle2D.Float getGlyphOutlineBounds(long pScalerContext, int glyphCode) { try { return getScaler().getGlyphOutlineBounds(pScalerContext, glyphCode); } catch (FontScalerException fe) { scaler = FontScaler.getNullScaler(); return getGlyphOutlineBounds(pScalerContext, glyphCode); } } GeneralPath getGlyphOutline(long pScalerContext, int glyphCode, float x, float y) { try { return getScaler().getGlyphOutline(pScalerContext, glyphCode, x, y); } catch (FontScalerException fe) { scaler = FontScaler.getNullScaler(); return getGlyphOutline(pScalerContext, glyphCode, x, y); } } GeneralPath getGlyphVectorOutline(long pScalerContext, int[] glyphs, int numGlyphs, float x, float y) { try { return getScaler().getGlyphVectorOutline(pScalerContext, glyphs, numGlyphs, x, y); } catch (FontScalerException fe) { scaler = FontScaler.getNullScaler(); return getGlyphVectorOutline(pScalerContext, glyphs, numGlyphs, x, y); } } /* T1 & TT implementation differ so this method is abstract. NB: null should not be returned here! */ protected abstract FontScaler getScaler(); protected long getUnitsPerEm() { return getScaler().getUnitsPerEm(); } private static class CreatedFontFileDisposerRecord implements DisposerRecord { File fontFile = null; CreatedFontTracker tracker; private CreatedFontFileDisposerRecord(File file, CreatedFontTracker tracker) { fontFile = file; this.tracker = tracker; } public void dispose() { java.security.AccessController.doPrivileged( new java.security.PrivilegedAction() { public Object run() { if (fontFile != null) { try { if (tracker != null) { tracker.subBytes((int)fontFile.length()); } /* REMIND: is it possible that the file is * still open? It will be closed when the * font2D is disposed but could this code * execute first? If so the file would not * be deleted on MS-windows. */ fontFile.delete(); /* remove from delete on exit hook list : */ // FIXME: still need to be refactored SunFontManager.getInstance().tmpFontFiles.remove(fontFile); } catch (Exception e) { } } return null; } }); } } protected String getPublicFileName() { SecurityManager sm = System.getSecurityManager(); if (sm == null) { return platName; } boolean canReadProperty = true; try { sm.checkPropertyAccess("java.io.tmpdir"); } catch (SecurityException e) { canReadProperty = false; } if (canReadProperty) { return platName; } final File f = new File(platName); Boolean isTmpFile = Boolean.FALSE; try { isTmpFile = AccessController.doPrivileged( new PrivilegedExceptionAction() { public Boolean run() { File tmp = new File(System.getProperty("java.io.tmpdir")); try { String tpath = tmp.getCanonicalPath(); String fpath = f.getCanonicalPath(); return (fpath == null) || fpath.startsWith(tpath); } catch (IOException e) { return Boolean.TRUE; } } } ); } catch (PrivilegedActionException e) { // unable to verify whether value of java.io.tempdir will be // exposed, so return only a name of the font file. isTmpFile = Boolean.TRUE; } return isTmpFile ? "temp file" : platName; } }