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