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