| /* |
| * 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.util.List; |
| 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; |
| } |
| |
| static void setFileToRemove(List<Font2D> fonts, |
| File file, int cnt, |
| CreatedFontTracker tracker) |
| { |
| CreatedFontFileDisposerRecord dr = |
| new CreatedFontFileDisposerRecord(file, cnt, tracker); |
| |
| for (Font2D f : fonts) { |
| Disposer.addObjectRecord(f, dr); |
| } |
| } |
| |
| /* 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<FontStrike> 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; |
| int count = 0; // number of fonts referencing this file object. |
| CreatedFontTracker tracker; |
| |
| private CreatedFontFileDisposerRecord(File file, int cnt, |
| CreatedFontTracker tracker) { |
| fontFile = file; |
| count = (cnt > 0) ? cnt : 1; |
| this.tracker = tracker; |
| } |
| |
| public void dispose() { |
| java.security.AccessController.doPrivileged( |
| new java.security.PrivilegedAction<Object>() { |
| public Object run() { |
| synchronized (fontFile) { |
| count--; |
| if (count > 0) { |
| return null; |
| } |
| } |
| 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<Boolean>() { |
| 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; |
| } |
| } |