author | malenkov |
Tue, 29 Oct 2013 17:01:06 +0400 | |
changeset 21278 | ef8a3a2a72f2 |
parent 9035 | 1255eb81cc2f |
child 23010 | 6dadb192ad81 |
child 23288 | b7183846db97 |
permissions | -rw-r--r-- |
2 | 1 |
/* |
9035
1255eb81cc2f
7033660: Update copyright year to 2011 on any files changed in 2011
ohair
parents:
8813
diff
changeset
|
2 |
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. |
2 | 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 |
|
5506 | 7 |
* published by the Free Software Foundation. Oracle designates this |
2 | 8 |
* particular file as subject to the "Classpath" exception as provided |
5506 | 9 |
* by Oracle in the LICENSE file that accompanied this code. |
2 | 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 |
* |
|
5506 | 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. |
|
2 | 24 |
*/ |
25 |
||
26 |
package sun.font; |
|
27 |
||
28 |
import java.lang.ref.WeakReference; |
|
29 |
import java.awt.FontFormatException; |
|
30 |
import java.io.FileNotFoundException; |
|
31 |
import java.io.IOException; |
|
32 |
import java.io.RandomAccessFile; |
|
33 |
import java.io.UnsupportedEncodingException; |
|
34 |
import java.lang.ref.WeakReference; |
|
35 |
import java.nio.ByteBuffer; |
|
36 |
import java.nio.ByteOrder; |
|
37 |
import java.nio.MappedByteBuffer; |
|
38 |
import java.nio.BufferUnderflowException; |
|
39 |
import java.nio.channels.ClosedChannelException; |
|
40 |
import java.nio.channels.FileChannel; |
|
41 |
import sun.java2d.Disposer; |
|
2609
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
42 |
import sun.java2d.DisposerRecord; |
2 | 43 |
import java.util.HashSet; |
44 |
import java.util.HashMap; |
|
45 |
import java.awt.Font; |
|
46 |
||
47 |
/* |
|
48 |
* Adobe Technical Note 5040 details the format of PFB files. |
|
49 |
* the file is divided into ascii and binary sections. Each section |
|
50 |
* starts with a header |
|
51 |
* 0x8001 - start of binary data, is followed by 4 bytes length, then data |
|
52 |
* 0x8002 - start of ascii data, is followed by 4 bytes length, then data |
|
53 |
* 0x8003 - end of data segment |
|
54 |
* The length is organised as LSB->MSB. |
|
55 |
* |
|
56 |
* Note: I experimented with using a MappedByteBuffer and |
|
57 |
* there were two problems/questions. |
|
58 |
* 1. If a global buffer is used rather than one allocated in the calling |
|
59 |
* context, then we need to synchronize on all uses of that data, which |
|
60 |
* means more code would beed to be synchronized with probable repercussions |
|
61 |
* elsewhere. |
|
62 |
* 2. It is not clear whether to free the buffer when the file is closed. |
|
63 |
* If we have the contents in memory then why keep open files around? |
|
64 |
* The mmapped buffer doesn't need it. |
|
65 |
* Also regular GC is what frees the buffer. So closing the file and nulling |
|
66 |
* out the reference still needs to wait for the buffer to be GC'd to |
|
67 |
* reclaim the storage. |
|
68 |
* If the contents of the buffer are persistent there's no need |
|
69 |
* to worry about synchronization. |
|
70 |
* Perhaps could use a WeakReference, and when its referent is gone, and |
|
71 |
* need it can just reopen the file. |
|
72 |
* Type1 fonts thus don't use up file descriptor references, but can |
|
73 |
* use memory footprint in a way that's managed by the host O/S. |
|
74 |
* The main "pain" may be the different model means code needs to be written |
|
75 |
* without assumptions as to how this is handled by the different subclasses |
|
76 |
* of FileFont. |
|
77 |
*/ |
|
78 |
public class Type1Font extends FileFont { |
|
79 |
||
2609
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
80 |
private static class T1DisposerRecord implements DisposerRecord { |
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
81 |
String fileName = null; |
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
82 |
|
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
83 |
T1DisposerRecord(String name) { |
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
84 |
fileName = name; |
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
85 |
} |
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
86 |
|
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
87 |
public synchronized void dispose() { |
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
88 |
java.security.AccessController.doPrivileged( |
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
89 |
new java.security.PrivilegedAction() { |
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
90 |
public Object run() { |
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
91 |
|
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
92 |
if (fileName != null) { |
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
93 |
(new java.io.File(fileName)).delete(); |
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
94 |
} |
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
95 |
return null; |
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
96 |
} |
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
97 |
}); |
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
98 |
} |
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
99 |
} |
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
100 |
|
2 | 101 |
WeakReference bufferRef = new WeakReference(null); |
102 |
||
103 |
private String psName = null; |
|
104 |
||
105 |
static private HashMap styleAbbreviationsMapping; |
|
106 |
static private HashSet styleNameTokes; |
|
107 |
||
108 |
static { |
|
109 |
styleAbbreviationsMapping = new HashMap(); |
|
110 |
styleNameTokes = new HashSet(); |
|
111 |
||
112 |
/* These abbreviation rules are taken from Appendix 1 of Adobe Technical Note #5088 */ |
|
113 |
/* NB: this list is not complete - we did not include abbreviations which contain |
|
114 |
several capital letters because current expansion algorithm do not support this. |
|
115 |
(namely we have omited MM aka "Multiple Master", OsF aka "Oldstyle figures", |
|
116 |
OS aka "Oldstyle", SC aka "Small caps" and DS aka "Display" */ |
|
117 |
String nm[] = {"Black", "Bold", "Book", "Demi", "Heavy", "Light", |
|
118 |
"Meduium", "Nord", "Poster", "Regular", "Super", "Thin", |
|
119 |
"Compressed", "Condensed", "Compact", "Extended", "Narrow", |
|
120 |
"Inclined", "Italic", "Kursiv", "Oblique", "Upright", "Sloped", |
|
121 |
"Semi", "Ultra", "Extra", |
|
122 |
"Alternate", "Alternate", "Deutsche Fraktur", "Expert", "Inline", "Ornaments", |
|
123 |
"Outline", "Roman", "Rounded", "Script", "Shaded", "Swash", "Titling", "Typewriter"}; |
|
124 |
String abbrv[] = {"Blk", "Bd", "Bk", "Dm", "Hv", "Lt", |
|
125 |
"Md", "Nd", "Po", "Rg", "Su", "Th", |
|
126 |
"Cm", "Cn", "Ct", "Ex", "Nr", |
|
127 |
"Ic", "It", "Ks", "Obl", "Up", "Sl", |
|
128 |
"Sm", "Ult", "X", |
|
129 |
"A", "Alt", "Dfr", "Exp", "In", "Or", |
|
130 |
"Ou", "Rm", "Rd", "Scr", "Sh", "Sw", "Ti", "Typ"}; |
|
131 |
/* This is only subset of names from nm[] because we want to distinguish things |
|
132 |
like "Lucida Sans TypeWriter Bold" and "Lucida Sans Bold". |
|
133 |
Names from "Design and/or special purpose" group are omitted. */ |
|
134 |
String styleTokens[] = {"Black", "Bold", "Book", "Demi", "Heavy", "Light", |
|
135 |
"Medium", "Nord", "Poster", "Regular", "Super", "Thin", |
|
136 |
"Compressed", "Condensed", "Compact", "Extended", "Narrow", |
|
137 |
"Inclined", "Italic", "Kursiv", "Oblique", "Upright", "Sloped", "Slanted", |
|
138 |
"Semi", "Ultra", "Extra"}; |
|
139 |
||
140 |
for(int i=0; i<nm.length; i++) { |
|
141 |
styleAbbreviationsMapping.put(abbrv[i], nm[i]); |
|
142 |
} |
|
143 |
for(int i=0; i<styleTokens.length; i++) { |
|
144 |
styleNameTokes.add(styleTokens[i]); |
|
145 |
} |
|
146 |
} |
|
147 |
||
148 |
||
149 |
/** |
|
2609
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
150 |
* Constructs a Type1 Font. |
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
151 |
* @param platname - Platform identifier of the font. Typically file name. |
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
152 |
* @param nativeNames - Native names - typically XLFDs on Unix. |
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
153 |
*/ |
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
154 |
public Type1Font(String platname, Object nativeNames) |
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
155 |
throws FontFormatException { |
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
156 |
|
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
157 |
this(platname, nativeNames, false); |
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
158 |
} |
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
159 |
|
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
160 |
/** |
2 | 161 |
* - does basic verification of the file |
162 |
* - reads the names (full, family). |
|
163 |
* - determines the style of the font. |
|
164 |
* @throws FontFormatException - if the font can't be opened |
|
165 |
* or fails verification, or there's no usable cmap |
|
166 |
*/ |
|
2609
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
167 |
public Type1Font(String platname, Object nativeNames, boolean createdCopy) |
2 | 168 |
throws FontFormatException { |
169 |
super(platname, nativeNames); |
|
170 |
fontRank = Font2D.TYPE1_RANK; |
|
171 |
checkedNatives = true; |
|
2609
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
172 |
try { |
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
173 |
verify(); |
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
174 |
} catch (Throwable t) { |
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
175 |
if (createdCopy) { |
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
176 |
T1DisposerRecord ref = new T1DisposerRecord(platname); |
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
177 |
Disposer.addObjectRecord(bufferRef, ref); |
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
178 |
bufferRef = null; |
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
179 |
} |
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
180 |
if (t instanceof FontFormatException) { |
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
181 |
throw (FontFormatException)t; |
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
182 |
} else { |
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
183 |
throw new FontFormatException("Unexpected runtime exception."); |
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
184 |
} |
1db65c97bddc
2163516: Font.createFont can be persuaded to leak temporary files
prr
parents:
558
diff
changeset
|
185 |
} |
2 | 186 |
} |
187 |
||
188 |
private synchronized ByteBuffer getBuffer() throws FontFormatException { |
|
189 |
MappedByteBuffer mapBuf = (MappedByteBuffer)bufferRef.get(); |
|
190 |
if (mapBuf == null) { |
|
191 |
//System.out.println("open T1 " + platName); |
|
192 |
try { |
|
193 |
RandomAccessFile raf = (RandomAccessFile) |
|
194 |
java.security.AccessController.doPrivileged( |
|
195 |
new java.security.PrivilegedAction() { |
|
196 |
public Object run() { |
|
197 |
try { |
|
198 |
return new RandomAccessFile(platName, "r"); |
|
199 |
} catch (FileNotFoundException ffne) { |
|
200 |
} |
|
201 |
return null; |
|
202 |
} |
|
203 |
}); |
|
204 |
FileChannel fc = raf.getChannel(); |
|
205 |
fileSize = (int)fc.size(); |
|
206 |
mapBuf = fc.map(FileChannel.MapMode.READ_ONLY, 0, fileSize); |
|
207 |
mapBuf.position(0); |
|
208 |
bufferRef = new WeakReference(mapBuf); |
|
209 |
fc.close(); |
|
210 |
} catch (NullPointerException e) { |
|
211 |
throw new FontFormatException(e.toString()); |
|
212 |
} catch (ClosedChannelException e) { |
|
213 |
/* NIO I/O is interruptible, recurse to retry operation. |
|
214 |
* Clear interrupts before recursing in case NIO didn't. |
|
215 |
*/ |
|
216 |
Thread.interrupted(); |
|
217 |
return getBuffer(); |
|
218 |
} catch (IOException e) { |
|
219 |
throw new FontFormatException(e.toString()); |
|
220 |
} |
|
221 |
} |
|
222 |
return mapBuf; |
|
223 |
} |
|
224 |
||
225 |
protected void close() { |
|
226 |
} |
|
227 |
||
228 |
/* called from native code to read file into a direct byte buffer */ |
|
229 |
void readFile(ByteBuffer buffer) { |
|
230 |
RandomAccessFile raf = null; |
|
231 |
FileChannel fc; |
|
232 |
try { |
|
233 |
raf = (RandomAccessFile) |
|
234 |
java.security.AccessController.doPrivileged( |
|
235 |
new java.security.PrivilegedAction() { |
|
236 |
public Object run() { |
|
237 |
try { |
|
238 |
return new RandomAccessFile(platName, "r"); |
|
239 |
} catch (FileNotFoundException fnfe) { |
|
240 |
} |
|
241 |
return null; |
|
242 |
} |
|
243 |
}); |
|
244 |
fc = raf.getChannel(); |
|
245 |
while (buffer.remaining() > 0 && fc.read(buffer) != -1) {} |
|
246 |
} catch (NullPointerException npe) { |
|
247 |
} catch (ClosedChannelException e) { |
|
248 |
try { |
|
249 |
if (raf != null) { |
|
250 |
raf.close(); |
|
251 |
raf = null; |
|
252 |
} |
|
253 |
} catch (IOException ioe) { |
|
254 |
} |
|
255 |
/* NIO I/O is interruptible, recurse to retry operation. |
|
256 |
* Clear interrupts before recursing in case NIO didn't. |
|
257 |
*/ |
|
258 |
Thread.interrupted(); |
|
259 |
readFile(buffer); |
|
260 |
} catch (IOException e) { |
|
261 |
} finally { |
|
262 |
if (raf != null) { |
|
263 |
try { |
|
264 |
raf.close(); |
|
265 |
} catch (IOException e) { |
|
266 |
} |
|
267 |
} |
|
268 |
} |
|
269 |
} |
|
270 |
||
271 |
public synchronized ByteBuffer readBlock(int offset, int length) { |
|
272 |
ByteBuffer mappedBuf = null; |
|
273 |
try { |
|
274 |
mappedBuf = getBuffer(); |
|
275 |
if (offset > fileSize) { |
|
276 |
offset = fileSize; |
|
277 |
} |
|
278 |
mappedBuf.position(offset); |
|
279 |
return mappedBuf.slice(); |
|
280 |
} catch (FontFormatException e) { |
|
281 |
return null; |
|
282 |
} |
|
283 |
} |
|
284 |
||
285 |
private void verify() throws FontFormatException { |
|
286 |
/* Normal usage should not call getBuffer(), as its state |
|
287 |
* ie endianness, position etc, are shared. verify() can do |
|
288 |
* this as its called only from within the constructor before |
|
289 |
* there are other users of this object. |
|
290 |
*/ |
|
291 |
ByteBuffer bb = getBuffer(); |
|
292 |
if (bb.capacity() < 6) { |
|
293 |
throw new FontFormatException("short file"); |
|
294 |
} |
|
295 |
int val = bb.get(0) & 0xff; |
|
296 |
if ((bb.get(0) & 0xff) == 0x80) { |
|
297 |
verifyPFB(bb); |
|
298 |
bb.position(6); |
|
299 |
} else { |
|
300 |
verifyPFA(bb); |
|
301 |
bb.position(0); |
|
302 |
} |
|
303 |
initNames(bb); |
|
304 |
if (familyName == null || fullName == null) { |
|
305 |
throw new FontFormatException("Font name not found"); |
|
306 |
} |
|
307 |
setStyle(); |
|
308 |
} |
|
309 |
||
310 |
public int getFileSize() { |
|
311 |
if (fileSize == 0) { |
|
312 |
try { |
|
313 |
getBuffer(); |
|
314 |
} catch (FontFormatException e) { |
|
315 |
} |
|
316 |
} |
|
317 |
return fileSize; |
|
318 |
} |
|
319 |
||
320 |
private void verifyPFA(ByteBuffer bb) throws FontFormatException { |
|
321 |
if (bb.getShort() != 0x2521) { // 0x2521 is %! |
|
322 |
throw new FontFormatException("bad pfa font"); |
|
323 |
} |
|
324 |
// remind - additional verification needed? |
|
325 |
} |
|
326 |
||
327 |
private void verifyPFB(ByteBuffer bb) throws FontFormatException { |
|
328 |
||
329 |
int pos = 0; |
|
330 |
while (true) { |
|
331 |
try { |
|
332 |
int segType = bb.getShort(pos) & 0xffff; |
|
333 |
if (segType == 0x8001 || segType == 0x8002) { |
|
334 |
bb.order(ByteOrder.LITTLE_ENDIAN); |
|
335 |
int segLen = bb.getInt(pos+2); |
|
336 |
bb.order(ByteOrder.BIG_ENDIAN); |
|
337 |
if (segLen <= 0) { |
|
338 |
throw new FontFormatException("bad segment length"); |
|
339 |
} |
|
340 |
pos += segLen+6; |
|
341 |
} else if (segType == 0x8003) { |
|
342 |
return; |
|
343 |
} else { |
|
344 |
throw new FontFormatException("bad pfb file"); |
|
345 |
} |
|
346 |
} catch (BufferUnderflowException bue) { |
|
347 |
throw new FontFormatException(bue.toString()); |
|
348 |
} catch (Exception e) { |
|
349 |
throw new FontFormatException(e.toString()); |
|
350 |
} |
|
351 |
} |
|
352 |
} |
|
353 |
||
354 |
private static final int PSEOFTOKEN = 0; |
|
355 |
private static final int PSNAMETOKEN = 1; |
|
356 |
private static final int PSSTRINGTOKEN = 2; |
|
357 |
||
358 |
/* Need to parse the ascii contents of the Type1 font file, |
|
359 |
* looking for FullName, FamilyName and FontName. |
|
360 |
* If explicit names are not found then extract them from first text line. |
|
361 |
* Operating on bytes so can't use Java String utilities, which |
|
362 |
* is a large part of why this is a hack. |
|
363 |
* |
|
364 |
* Also check for mandatory FontType and verify if it is supported. |
|
365 |
*/ |
|
366 |
private void initNames(ByteBuffer bb) throws FontFormatException { |
|
367 |
boolean eof = false; |
|
368 |
String fontType = null; |
|
369 |
try { |
|
370 |
//Parse font looking for explicit FullName, FamilyName and FontName |
|
21278 | 371 |
// (according to Type1 spec they are optional) |
2 | 372 |
while ((fullName == null || familyName == null || psName == null || fontType == null) && !eof) { |
373 |
int tokenType = nextTokenType(bb); |
|
374 |
if (tokenType == PSNAMETOKEN) { |
|
375 |
int pos = bb.position(); |
|
376 |
if (bb.get(pos) == 'F') { |
|
377 |
String s = getSimpleToken(bb); |
|
378 |
if ("FullName".equals(s)) { |
|
379 |
if (nextTokenType(bb)==PSSTRINGTOKEN) { |
|
380 |
fullName = getString(bb); |
|
381 |
} |
|
382 |
} else if ("FamilyName".equals(s)) { |
|
383 |
if (nextTokenType(bb)==PSSTRINGTOKEN) { |
|
384 |
familyName = getString(bb); |
|
385 |
} |
|
386 |
} else if ("FontName".equals(s)) { |
|
387 |
if (nextTokenType(bb)==PSNAMETOKEN) { |
|
388 |
psName = getSimpleToken(bb); |
|
389 |
} |
|
390 |
} else if ("FontType".equals(s)) { |
|
391 |
/* look for |
|
392 |
/FontType id def |
|
393 |
*/ |
|
394 |
String token = getSimpleToken(bb); |
|
395 |
if ("def".equals(getSimpleToken(bb))) { |
|
396 |
fontType = token; |
|
397 |
} |
|
398 |
} |
|
399 |
} else { |
|
400 |
while (bb.get() > ' '); // skip token |
|
401 |
} |
|
402 |
} else if (tokenType == PSEOFTOKEN) { |
|
403 |
eof = true; |
|
404 |
} |
|
405 |
} |
|
406 |
} catch (Exception e) { |
|
407 |
throw new FontFormatException(e.toString()); |
|
408 |
} |
|
409 |
||
410 |
/* Ignore all fonts besides Type1 (e.g. Type3 fonts) */ |
|
411 |
if (!"1".equals(fontType)) { |
|
412 |
throw new FontFormatException("Unsupported font type"); |
|
413 |
} |
|
414 |
||
415 |
if (psName == null) { //no explicit FontName |
|
416 |
// Try to extract font name from the first text line. |
|
417 |
// According to Type1 spec first line consist of |
|
418 |
// "%!FontType1-SpecVersion: FontName FontVersion" |
|
419 |
// or |
|
420 |
// "%!PS-AdobeFont-1.0: FontName version" |
|
421 |
bb.position(0); |
|
422 |
if (bb.getShort() != 0x2521) { //if pfb (do not start with "%!") |
|
423 |
//skip segment header and "%!" |
|
424 |
bb.position(8); |
|
425 |
//NB: assume that first segment is ASCII one |
|
426 |
// (is it possible to have valid Type1 font with first binary segment?) |
|
427 |
} |
|
428 |
String formatType = getSimpleToken(bb); |
|
429 |
if (!formatType.startsWith("FontType1-") && !formatType.startsWith("PS-AdobeFont-")) { |
|
430 |
throw new FontFormatException("Unsupported font format [" + formatType + "]"); |
|
431 |
} |
|
432 |
psName = getSimpleToken(bb); |
|
433 |
} |
|
434 |
||
435 |
//if we got to the end of file then we did not find at least one of FullName or FamilyName |
|
436 |
//Try to deduce missing names from present ones |
|
437 |
//NB: At least psName must be already initialized by this moment |
|
438 |
if (eof) { |
|
439 |
//if we find fullName or familyName then use it as another name too |
|
440 |
if (fullName != null) { |
|
441 |
familyName = fullName2FamilyName(fullName); |
|
442 |
} else if (familyName != null) { |
|
443 |
fullName = familyName; |
|
444 |
} else { //fallback - use postscript font name to deduce full and family names |
|
445 |
fullName = psName2FullName(psName); |
|
446 |
familyName = psName2FamilyName(psName); |
|
447 |
} |
|
448 |
} |
|
449 |
} |
|
450 |
||
451 |
private String fullName2FamilyName(String name) { |
|
452 |
String res, token; |
|
453 |
int len, start, end; //length of family name part |
|
454 |
||
455 |
//FamilyName is truncated version of FullName |
|
456 |
//Truncated tail must contain only style modifiers |
|
457 |
||
458 |
end = name.length(); |
|
459 |
||
460 |
while (end > 0) { |
|
461 |
start = end - 1; |
|
462 |
while (start > 0 && name.charAt(start) != ' ') |
|
463 |
start--; |
|
464 |
//as soon as we meet first non style token truncate |
|
465 |
// current tail and return |
|
466 |
if (!isStyleToken(name.substring(start+1, end))) { |
|
467 |
return name.substring(0, end); |
|
468 |
} |
|
469 |
end = start; |
|
470 |
} |
|
471 |
||
472 |
return name; //should not happen |
|
473 |
} |
|
474 |
||
475 |
private String expandAbbreviation(String abbr) { |
|
476 |
if (styleAbbreviationsMapping.containsKey(abbr)) |
|
477 |
return (String) styleAbbreviationsMapping.get(abbr); |
|
478 |
return abbr; |
|
479 |
} |
|
480 |
||
481 |
private boolean isStyleToken(String token) { |
|
482 |
return styleNameTokes.contains(token); |
|
483 |
} |
|
484 |
||
485 |
private String psName2FullName(String name) { |
|
486 |
String res; |
|
487 |
int pos; |
|
488 |
||
489 |
//According to Adobe technical note #5088 psName (aka FontName) has form |
|
490 |
// <Family Name><VendorID>-<Weight><Width><Slant><Character Set> |
|
491 |
//where spaces are not allowed. |
|
492 |
||
493 |
//Conversion: Expand abbreviations in style portion (everything after '-'), |
|
494 |
// replace '-' with space and insert missing spaces |
|
495 |
pos = name.indexOf("-"); |
|
496 |
if (pos >= 0) { |
|
497 |
res = expandName(name.substring(0, pos), false); |
|
498 |
res += " " + expandName(name.substring(pos+1), true); |
|
499 |
} else { |
|
500 |
res = expandName(name, false); |
|
501 |
} |
|
502 |
||
503 |
return res; |
|
504 |
} |
|
505 |
||
506 |
private String psName2FamilyName(String name) { |
|
507 |
String tmp = name; |
|
508 |
||
509 |
//According to Adobe technical note #5088 psName (aka FontName) has form |
|
510 |
// <Family Name><VendorID>-<Weight><Width><Slant><Character Set> |
|
511 |
//where spaces are not allowed. |
|
512 |
||
513 |
//Conversion: Truncate style portion (everything after '-') |
|
514 |
// and insert missing spaces |
|
515 |
||
516 |
if (tmp.indexOf("-") > 0) { |
|
517 |
tmp = tmp.substring(0, tmp.indexOf("-")); |
|
518 |
} |
|
519 |
||
520 |
return expandName(tmp, false); |
|
521 |
} |
|
522 |
||
523 |
private int nextCapitalLetter(String s, int off) { |
|
524 |
for (; (off >=0) && off < s.length(); off++) { |
|
525 |
if (s.charAt(off) >= 'A' && s.charAt(off) <= 'Z') |
|
526 |
return off; |
|
527 |
} |
|
528 |
return -1; |
|
529 |
} |
|
530 |
||
531 |
private String expandName(String s, boolean tryExpandAbbreviations) { |
|
532 |
StringBuffer res = new StringBuffer(s.length() + 10); |
|
533 |
int start=0, end; |
|
534 |
||
535 |
while(start < s.length()) { |
|
536 |
end = nextCapitalLetter(s, start + 1); |
|
537 |
if (end < 0) { |
|
538 |
end = s.length(); |
|
539 |
} |
|
540 |
||
541 |
if (start != 0) { |
|
542 |
res.append(" "); |
|
543 |
} |
|
544 |
||
545 |
if (tryExpandAbbreviations) { |
|
546 |
res.append(expandAbbreviation(s.substring(start, end))); |
|
547 |
} else { |
|
548 |
res.append(s.substring(start, end)); |
|
549 |
} |
|
550 |
start = end; |
|
551 |
} |
|
552 |
||
553 |
return res.toString(); |
|
554 |
} |
|
555 |
||
556 |
/* skip lines beginning with "%" and leading white space on a line */ |
|
557 |
private byte skip(ByteBuffer bb) { |
|
558 |
byte b = bb.get(); |
|
559 |
while (b == '%') { |
|
560 |
while (true) { |
|
561 |
b = bb.get(); |
|
562 |
if (b == '\r' || b == '\n') { |
|
563 |
break; |
|
564 |
} |
|
565 |
} |
|
566 |
} |
|
567 |
while (b <= ' ') { |
|
568 |
b = bb.get(); |
|
569 |
} |
|
570 |
return b; |
|
571 |
} |
|
572 |
||
573 |
/* |
|
574 |
* Token types: |
|
575 |
* PSNAMETOKEN - / |
|
576 |
* PSSTRINGTOKEN - literal text string |
|
577 |
*/ |
|
578 |
private int nextTokenType(ByteBuffer bb) { |
|
579 |
||
580 |
try { |
|
581 |
byte b = skip(bb); |
|
582 |
||
583 |
while (true) { |
|
584 |
if (b == (byte)'/') { // PS defined name follows. |
|
585 |
return PSNAMETOKEN; |
|
586 |
} else if (b == (byte)'(') { // PS string follows |
|
587 |
return PSSTRINGTOKEN; |
|
588 |
} else if ((b == (byte)'\r') || (b == (byte)'\n')) { |
|
589 |
b = skip(bb); |
|
590 |
} else { |
|
591 |
b = bb.get(); |
|
592 |
} |
|
593 |
} |
|
594 |
} catch (BufferUnderflowException e) { |
|
595 |
return PSEOFTOKEN; |
|
596 |
} |
|
597 |
} |
|
598 |
||
599 |
/* Read simple token (sequence of non-whitespace characters) |
|
600 |
starting from the current position. |
|
601 |
Skip leading whitespaces (if any). */ |
|
602 |
private String getSimpleToken(ByteBuffer bb) { |
|
603 |
while (bb.get() <= ' '); |
|
604 |
int pos1 = bb.position()-1; |
|
605 |
while (bb.get() > ' '); |
|
606 |
int pos2 = bb.position(); |
|
607 |
byte[] nameBytes = new byte[pos2-pos1-1]; |
|
608 |
bb.position(pos1); |
|
609 |
bb.get(nameBytes); |
|
610 |
try { |
|
611 |
return new String(nameBytes, "US-ASCII"); |
|
612 |
} catch (UnsupportedEncodingException e) { |
|
613 |
return new String(nameBytes); |
|
614 |
} |
|
615 |
} |
|
616 |
||
617 |
private String getString(ByteBuffer bb) { |
|
618 |
int pos1 = bb.position(); |
|
619 |
while (bb.get() != ')'); |
|
620 |
int pos2 = bb.position(); |
|
621 |
byte[] nameBytes = new byte[pos2-pos1-1]; |
|
622 |
bb.position(pos1); |
|
623 |
bb.get(nameBytes); |
|
624 |
try { |
|
625 |
return new String(nameBytes, "US-ASCII"); |
|
626 |
} catch (UnsupportedEncodingException e) { |
|
627 |
return new String(nameBytes); |
|
628 |
} |
|
629 |
} |
|
630 |
||
631 |
||
632 |
public String getPostscriptName() { |
|
633 |
return psName; |
|
634 |
} |
|
635 |
||
636 |
protected synchronized FontScaler getScaler() { |
|
637 |
if (scaler == null) { |
|
3928 | 638 |
scaler = FontScaler.getScaler(this, 0, false, fileSize); |
2 | 639 |
} |
640 |
||
641 |
return scaler; |
|
642 |
} |
|
643 |
||
644 |
CharToGlyphMapper getMapper() { |
|
645 |
if (mapper == null) { |
|
646 |
mapper = new Type1GlyphMapper(this); |
|
647 |
} |
|
648 |
return mapper; |
|
649 |
} |
|
650 |
||
651 |
public int getNumGlyphs() { |
|
652 |
try { |
|
653 |
return getScaler().getNumGlyphs(); |
|
654 |
} catch (FontScalerException e) { |
|
3928 | 655 |
scaler = FontScaler.getNullScaler(); |
2 | 656 |
return getNumGlyphs(); |
657 |
} |
|
658 |
} |
|
659 |
||
660 |
public int getMissingGlyphCode() { |
|
661 |
try { |
|
662 |
return getScaler().getMissingGlyphCode(); |
|
663 |
} catch (FontScalerException e) { |
|
3928 | 664 |
scaler = FontScaler.getNullScaler(); |
2 | 665 |
return getMissingGlyphCode(); |
666 |
} |
|
667 |
} |
|
668 |
||
669 |
public int getGlyphCode(char charCode) { |
|
670 |
try { |
|
671 |
return getScaler().getGlyphCode(charCode); |
|
672 |
} catch (FontScalerException e) { |
|
3928 | 673 |
scaler = FontScaler.getNullScaler(); |
2 | 674 |
return getGlyphCode(charCode); |
675 |
} |
|
676 |
} |
|
677 |
||
678 |
public String toString() { |
|
679 |
return "** Type1 Font: Family="+familyName+ " Name="+fullName+ |
|
8813
d15a9204c2f0
6985453: Font.createFont may expose some system properties in exception text
bae
parents:
5506
diff
changeset
|
680 |
" style="+style+" fileName="+getPublicFileName(); |
2 | 681 |
} |
682 |
} |