blob: 0bf23547ad0cfd58af50f0016d62ce78cc1f2337 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2003-2006 Sun Microsystems, Inc. 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. 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
26package sun.font;
27
28import java.awt.Font;
29import java.awt.font.FontRenderContext;
30import java.awt.geom.AffineTransform;
31import java.lang.ref.Reference;
32import java.lang.ref.SoftReference;
33import java.util.concurrent.ConcurrentHashMap;
34import java.util.Locale;
35
36public abstract class Font2D {
37
38 /* Note: JRE and FONT_CONFIG ranks are identical. I don't know of a reason
39 * to distingish these. Possibly if a user adds fonts to the JRE font
40 * directory that are the same font as the ones specified in the font
41 * configuration but that is more likely to be the legitimate intention
42 * than a problem. One reason why these should be the same is that on
43 * Linux the JRE fonts ARE the font configuration fonts, and although I
44 * believe all are assigned FONT_CONFIG rank, it is conceivable that if
45 * this were not so, that some JRE font would not be allowed to joint the
46 * family of its siblings which were assigned FONT_CONFIG rank. Giving
47 * them the same rank is the easy solution for now at least.
48 */
49 public static final int FONT_CONFIG_RANK = 2;
50 public static final int JRE_RANK = 2;
51 public static final int TTF_RANK = 3;
52 public static final int TYPE1_RANK = 4;
53 public static final int NATIVE_RANK = 5;
54 public static final int UNKNOWN_RANK = 6;
55 public static final int DEFAULT_RANK = 4;
56
57 private static final String[] boldNames = {
58 "bold", "demibold", "demi-bold", "demi bold", "negreta", "demi", };
59
60 private static final String[] italicNames = {
61 "italic", "cursiva", "oblique", "inclined", };
62
63 private static final String[] boldItalicNames = {
64 "bolditalic", "bold-italic", "bold italic",
65 "boldoblique", "bold-oblique", "bold oblique",
66 "demibold italic", "negreta cursiva","demi oblique", };
67
68 private static final FontRenderContext DEFAULT_FRC =
69 new FontRenderContext(null, false, false);
70
71 public Font2DHandle handle;
72 protected String familyName; /* Family font name (english) */
73 protected String fullName; /* Full font name (english) */
74 protected int style = Font.PLAIN;
75 protected FontFamily family;
76 protected int fontRank = DEFAULT_RANK;
77
78 /*
79 * A mapper can be independent of the strike.
80 * Perhaps the reference to the mapper ought to be held on the
81 * scaler, as it may be implemented via scaler functionality anyway
82 * and so the mapper would be useless if its native portion was
83 * freed when the scaler was GC'd.
84 */
85 protected CharToGlyphMapper mapper;
86
87 /*
88 * The strike cache is maintained per "Font2D" as that is the
89 * principal object by which you look up fonts.
90 * It means more Hashmaps, but look ups can be quicker because
91 * the map will have fewer entries, and there's no need to try to
92 * make the Font2D part of the key.
93 */
94 protected ConcurrentHashMap<FontStrikeDesc, Reference>
95 strikeCache = new ConcurrentHashMap<FontStrikeDesc, Reference>();
96
97 /* Store the last Strike in a Reference object.
98 * Similarly to the strike that was stored on a C++ font object,
99 * this is an optimisation which helps if multiple clients (ie
100 * typically SunGraphics2D instances) are using the same font, then
101 * as may be typical of many UIs, they are probably using it in the
102 * same style, so it can be a win to first quickly check if the last
103 * strike obtained from this Font2D satifies the needs of the next
104 * client too.
105 * This pre-supposes that a FontStrike is a shareable object, which
106 * it should.
107 */
108 protected Reference lastFontStrike = new SoftReference(null);
109
110 /*
111 * POSSIBLE OPTIMISATION:
112 * Array of length 1024 elements of 64 bits indicating if a font
113 * contains these. This kind of information can be shared between
114 * all point sizes.
115 * if corresponding bit in knownBitmaskMap is set then canDisplayBitmaskMap
116 * is valid. This is 16Kbytes of data per composite font style.
117 * What about UTF-32 and surrogates?
118 * REMIND: This is too much storage. Probably can only cache this
119 * information for latin range, although possibly OK to store all
120 * for just the "logical" fonts.
121 * Or instead store arrays of subranges of 1024 bits (128 bytes) in
122 * the range below surrogate pairs.
123 */
124// protected long[] knownBitmaskMap;
125// protected long[] canDisplayBitmaskMap;
126
127 /* Returns the "real" style of this Font2D. Eg the font face
128 * Lucida Sans Bold" has a real style of Font.BOLD, even though
129 * it may be able to used to simulate bold italic
130 */
131 public int getStyle() {
132 return style;
133 }
134 protected void setStyle() {
135
136 String fName = fullName.toLowerCase();
137
138 for (int i=0; i < boldItalicNames.length; i++) {
139 if (fName.indexOf(boldItalicNames[i]) != -1) {
140 style = Font.BOLD|Font.ITALIC;
141 return;
142 }
143 }
144
145 for (int i=0; i < italicNames.length; i++) {
146 if (fName.indexOf(italicNames[i]) != -1) {
147 style = Font.ITALIC;
148 return;
149 }
150 }
151
152 for (int i=0; i < boldNames.length; i++) {
153 if (fName.indexOf(boldNames[i]) != -1 ) {
154 style = Font.BOLD;
155 return;
156 }
157 }
158 }
159
160
161 int getRank() {
162 return fontRank;
163 }
164
165 void setRank(int rank) {
166 fontRank = rank;
167 }
168
169 abstract CharToGlyphMapper getMapper();
170
171
172
173 /* This isn't very efficient but its infrequently used.
174 * StandardGlyphVector uses it when the client assigns the glyph codes.
175 * These may not be valid. This validates them substituting the missing
176 * glyph elsewhere.
177 */
178 protected int getValidatedGlyphCode(int glyphCode) {
179 if (glyphCode < 0 || glyphCode >= getMapper().getNumGlyphs()) {
180 glyphCode = getMapper().getMissingGlyphCode();
181 }
182 return glyphCode;
183 }
184
185 /*
186 * Creates an appropriate strike for the Font2D subclass
187 */
188 abstract FontStrike createStrike(FontStrikeDesc desc);
189
190 /* this may be useful for APIs like canDisplay where the answer
191 * is dependent on the font and its scaler, but not the strike.
192 * If no strike has ever been returned, then create a one that matches
193 * this font with the default FRC. It will become the lastStrike and
194 * there's a good chance that the next call will be to get exactly that
195 * strike.
196 */
197 public FontStrike getStrike(Font font) {
198 FontStrike strike = (FontStrike)lastFontStrike.get();
199 if (strike != null) {
200 return strike;
201 } else {
202 return getStrike(font, DEFAULT_FRC);
203 }
204 }
205
206 /* SunGraphics2D has font, tx, aa and fm. From this info
207 * can get a Strike object from the cache, creating it if necessary.
208 * This code is designed for multi-threaded access.
209 * For that reason it creates a local FontStrikeDesc rather than filling
210 * in a shared one. Up to two AffineTransforms and one FontStrikeDesc will
211 * be created by every lookup. This appears to perform more than
212 * adequately. But it may make sense to expose FontStrikeDesc
213 * as a parameter so a caller can use its own.
214 * In such a case if a FontStrikeDesc is stored as a key then
215 * we would need to use a private copy.
216 *
217 * Note that this code doesn't prevent two threads from creating
218 * two different FontStrike instances and having one of the threads
219 * overwrite the other in the map. This is likely to be a rare
220 * occurrence and the only consequence is that these callers will have
221 * different instances of the strike, and there'd be some duplication of
222 * population of the strikes. However since users of these strikes are
223 * transient, then the one that was overwritten would soon be freed.
224 * If there is any problem then a small synchronized block would be
225 * required with its attendant consequences for MP scaleability.
226 */
227 public FontStrike getStrike(Font font, AffineTransform devTx,
228 int aa, int fm) {
229
230 /* Create the descriptor which is used to identify a strike
231 * in the strike cache/map. A strike is fully described by
232 * the attributes of this descriptor.
233 */
234 /* REMIND: generating garbage and doing computation here in order
235 * to include pt size in the tx just for a lookup! Figure out a
236 * better way.
237 */
238 double ptSize = font.getSize2D();
239 AffineTransform glyphTx = (AffineTransform)devTx.clone();
240 glyphTx.scale(ptSize, ptSize);
241 if (font.isTransformed()) {
242 glyphTx.concatenate(font.getTransform());
243 }
244 FontStrikeDesc desc = new FontStrikeDesc(devTx, glyphTx,
245 font.getStyle(), aa, fm);
246 return getStrike(desc, false);
247 }
248
249 public FontStrike getStrike(Font font, AffineTransform devTx,
250 AffineTransform glyphTx,
251 int aa, int fm) {
252
253 /* Create the descriptor which is used to identify a strike
254 * in the strike cache/map. A strike is fully described by
255 * the attributes of this descriptor.
256 */
257 FontStrikeDesc desc = new FontStrikeDesc(devTx, glyphTx,
258 font.getStyle(), aa, fm);
259 return getStrike(desc, false);
260 }
261
262 public FontStrike getStrike(Font font, FontRenderContext frc) {
263
264 AffineTransform at = frc.getTransform();
265 double ptSize = font.getSize2D();
266 at.scale(ptSize, ptSize);
267 if (font.isTransformed()) {
268 at.concatenate(font.getTransform());
269 }
270 int aa = FontStrikeDesc.getAAHintIntVal(this, font, frc);
271 int fm = FontStrikeDesc.getFMHintIntVal(frc.getFractionalMetricsHint());
272 FontStrikeDesc desc = new FontStrikeDesc(frc.getTransform(),
273 at, font.getStyle(),
274 aa, fm);
275 return getStrike(desc, false);
276 }
277
278 FontStrike getStrike(FontStrikeDesc desc) {
279 return getStrike(desc, true);
280 }
281
282 private FontStrike getStrike(FontStrikeDesc desc, boolean copy) {
283 /* Before looking in the map, see if the descriptor matches the
284 * last strike returned from this Font2D. This should often be a win
285 * since its common for the same font, in the same size to be
286 * used frequently, for example in many parts of a UI.
287 *
288 * If its not the same then we use the descriptor to locate a
289 * Reference to the strike. If it exists and points to a strike,
290 * then we update the last strike to refer to that and return it.
291 *
292 * If the key isn't in the map, or its reference object has been
293 * collected, then we create a new strike, put it in the map and
294 * set it to be the last strike.
295 */
296 FontStrike strike = (FontStrike)lastFontStrike.get();
297 if (strike != null && desc.equals(strike.desc)) {
298 //strike.lastlookupTime = System.currentTimeMillis();
299 return strike;
300 } else {
301 Reference strikeRef = strikeCache.get(desc);
302 if (strikeRef != null) {
303 strike = (FontStrike)strikeRef.get();
304 if (strike != null) {
305 //strike.lastlookupTime = System.currentTimeMillis();
306 lastFontStrike = new SoftReference(strike);
307 StrikeCache.refStrike(strike);
308 return strike;
309 } else {
310 /* We have found a cleared reference that has not yet
311 * been removed by the disposer.
312 * If we make this reference unreachable by removing it
313 * from the map,or overwriting it with a new reference to
314 * a new strike, then it is possible it may never be
315 * enqueued for disposal. That is the implication of
316 * the docs for java.lang.ref. So on finding a cleared
317 * reference, we need to dispose the native resources,
318 * if they haven't already been freed.
319 * The reference object needs to have a reference to
320 * the disposer instance for this to occur.
321 */
322 ((StrikeCache.DisposableStrike)strikeRef)
323 .getDisposer().dispose();
324 }
325 }
326 /* When we create a new FontStrike instance, we *must*
327 * ask the StrikeCache for a reference. We must then ensure
328 * this reference remains reachable, by storing it in the
329 * Font2D's strikeCache map.
330 * So long as the Reference is there (reachable) then if the
331 * reference is cleared, it will be enqueued for disposal.
332 * If for some reason we explicitly remove this reference, it
333 * must only be done when holding a strong reference to the
334 * referent (the FontStrike), or if the reference is cleared,
335 * then we must explicitly "dispose" of the native resources.
336 * The only place this currently happens is in this same method,
337 * where we find a cleared reference and need to overwrite it
338 * here with a new reference.
339 * Clearing the whilst holding a strong reference, should only
340 * be done if the
341 */
342 if (copy) {
343 desc = new FontStrikeDesc(desc);
344 }
345 strike = createStrike(desc);
346 //StrikeCache.addStrike();
347 strikeRef = StrikeCache.getStrikeRef(strike);
348 strikeCache.put(desc, strikeRef);
349 //strike.lastlookupTime = System.currentTimeMillis();
350 lastFontStrike = new SoftReference(strike);
351 StrikeCache.refStrike(strike);
352 return strike;
353 }
354 }
355
356 void removeFromCache(FontStrikeDesc desc) {
357 Reference ref = strikeCache.get(desc);
358 if (ref != null) {
359 Object o = ref.get();
360 if (o == null) {
361 strikeCache.remove(desc);
362 }
363 }
364 }
365
366 /**
367 * The length of the metrics array must be >= 8. This method will
368 * store the following elements in that array before returning:
369 * metrics[0]: ascent
370 * metrics[1]: descent
371 * metrics[2]: leading
372 * metrics[3]: max advance
373 * metrics[4]: strikethrough offset
374 * metrics[5]: strikethrough thickness
375 * metrics[6]: underline offset
376 * metrics[7]: underline thickness
377 */
378 public void getFontMetrics(Font font, AffineTransform at,
379 Object aaHint, Object fmHint,
380 float metrics[]) {
381 /* This is called in just one place in Font with "at" == identity.
382 * Perhaps this can be eliminated.
383 */
384 int aa = FontStrikeDesc.getAAHintIntVal(aaHint, this, font.getSize());
385 int fm = FontStrikeDesc.getFMHintIntVal(fmHint);
386 FontStrike strike = getStrike(font, at, aa, fm);
387 StrikeMetrics strikeMetrics = strike.getFontMetrics();
388 metrics[0] = strikeMetrics.getAscent();
389 metrics[1] = strikeMetrics.getDescent();
390 metrics[2] = strikeMetrics.getLeading();
391 metrics[3] = strikeMetrics.getMaxAdvance();
392
393 getStyleMetrics(font.getSize2D(), metrics, 4);
394 }
395
396 /**
397 * The length of the metrics array must be >= offset+4, and offset must be
398 * >= 0. Typically offset is 4. This method will
399 * store the following elements in that array before returning:
400 * metrics[off+0]: strikethrough offset
401 * metrics[off+1]: strikethrough thickness
402 * metrics[off+2]: underline offset
403 * metrics[off+3]: underline thickness
404 *
405 * Note that this implementation simply returns default values;
406 * subclasses can override this method to provide more accurate values.
407 */
408 public void getStyleMetrics(float pointSize, float[] metrics, int offset) {
409 metrics[offset] = -metrics[0] / 2.5f;
410 metrics[offset+1] = pointSize / 12;
411 metrics[offset+2] = metrics[offset+1] / 1.5f;
412 metrics[offset+3] = metrics[offset+1];
413 }
414
415 /**
416 * The length of the metrics array must be >= 4. This method will
417 * store the following elements in that array before returning:
418 * metrics[0]: ascent
419 * metrics[1]: descent
420 * metrics[2]: leading
421 * metrics[3]: max advance
422 */
423 public void getFontMetrics(Font font, FontRenderContext frc,
424 float metrics[]) {
425 StrikeMetrics strikeMetrics = getStrike(font, frc).getFontMetrics();
426 metrics[0] = strikeMetrics.getAscent();
427 metrics[1] = strikeMetrics.getDescent();
428 metrics[2] = strikeMetrics.getLeading();
429 metrics[3] = strikeMetrics.getMaxAdvance();
430 }
431
432 /* Currently the layout code calls this. May be better for layout code
433 * to check the font class before attempting to run, rather than needing
434 * to promote this method up from TrueTypeFont
435 */
436 byte[] getTableBytes(int tag) {
437 return null;
438 }
439
440 /* for layout code */
441 protected long getUnitsPerEm() {
442 return 2048;
443 }
444
445 boolean supportsEncoding(String encoding) {
446 return false;
447 }
448
449 public boolean canDoStyle(int style) {
450 return (style == this.style);
451 }
452
453 /*
454 * All the important subclasses override this which is principally for
455 * the TrueType 'gasp' table.
456 */
457 public boolean useAAForPtSize(int ptsize) {
458 return true;
459 }
460
461 public boolean hasSupplementaryChars() {
462 return false;
463 }
464
465 /* The following methods implement public methods on java.awt.Font */
466 public String getPostscriptName() {
467 return fullName;
468 }
469
470 public String getFontName(Locale l) {
471 return fullName;
472 }
473
474 public String getFamilyName(Locale l) {
475 return familyName;
476 }
477
478 public int getNumGlyphs() {
479 return getMapper().getNumGlyphs();
480 }
481
482 public int charToGlyph(int wchar) {
483 return getMapper().charToGlyph(wchar);
484 }
485
486 public int getMissingGlyphCode() {
487 return getMapper().getMissingGlyphCode();
488 }
489
490 public boolean canDisplay(char c) {
491 return getMapper().canDisplay(c);
492 }
493
494 public boolean canDisplay(int cp) {
495 return getMapper().canDisplay(cp);
496 }
497
498 public byte getBaselineFor(char c) {
499 return Font.ROMAN_BASELINE;
500 }
501
502 public float getItalicAngle(Font font, AffineTransform at,
503 Object aaHint, Object fmHint) {
504 /* hardwire psz=12 as that's typical and AA vs non-AA for 'gasp' mode
505 * isn't important for the caret slope of this rarely used API.
506 */
507 int aa = FontStrikeDesc.getAAHintIntVal(aaHint, this, 12);
508 int fm = FontStrikeDesc.getFMHintIntVal(fmHint);
509 FontStrike strike = getStrike(font, at, aa, fm);
510 StrikeMetrics metrics = strike.getFontMetrics();
511 if (metrics.ascentY == 0 || metrics.ascentX == 0) {
512 return 0f;
513 } else {
514 /* ascent is "up" from the baseline so its typically
515 * a negative value, so we need to compensate
516 */
517 return metrics.ascentX/-metrics.ascentY;
518 }
519 }
520
521}