PageRenderTime 914ms CodeModel.GetById 60ms RepoModel.GetById 13ms app.codeStats 1ms

/jdk/src/share/classes/sun/font/FontManager.java

https://bitbucket.org/nkabir/jdk-6
Java | 3759 lines | 2341 code | 303 blank | 1115 comment | 618 complexity | 7f65ada8e47b359c1aca140ca4c22011 MD5 | raw file
Possible License(s): LGPL-3.0, GPL-2.0, BSD-3-Clause-No-Nuclear-License-2014, BSD-3-Clause

Large files files are truncated, but you can click here to view the full file

  1. /*
  2. * Copyright (c) 2003, 2010, 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. package sun.font;
  26. import java.awt.Font;
  27. import java.awt.GraphicsEnvironment;
  28. import java.awt.FontFormatException;
  29. import java.io.File;
  30. import java.io.FilenameFilter;
  31. import java.security.AccessController;
  32. import java.security.PrivilegedAction;
  33. import java.util.ArrayList;
  34. import java.util.HashMap;
  35. import java.util.HashSet;
  36. import java.util.Hashtable;
  37. import java.util.Iterator;
  38. import java.util.Locale;
  39. import java.util.Map;
  40. import java.util.NoSuchElementException;
  41. import java.util.StringTokenizer;
  42. import java.util.TreeMap;
  43. import java.util.Vector;
  44. import java.util.concurrent.ConcurrentHashMap;
  45. import java.util.logging.Level;
  46. import java.util.logging.Logger;
  47. import javax.swing.plaf.FontUIResource;
  48. import sun.awt.AppContext;
  49. import sun.awt.FontConfiguration;
  50. import sun.awt.SunHints;
  51. import sun.awt.SunToolkit;
  52. import sun.java2d.HeadlessGraphicsEnvironment;
  53. import sun.java2d.SunGraphicsEnvironment;
  54. import java.awt.geom.GeneralPath;
  55. import java.awt.geom.Point2D;
  56. import java.awt.geom.Rectangle2D;
  57. import java.lang.reflect.Constructor;
  58. import sun.java2d.Disposer;
  59. /*
  60. * Interface between Java Fonts (java.awt.Font) and the underlying
  61. * font files/native font resources and the Java and native font scalers.
  62. */
  63. public final class FontManager {
  64. public static final int FONTFORMAT_NONE = -1;
  65. public static final int FONTFORMAT_TRUETYPE = 0;
  66. public static final int FONTFORMAT_TYPE1 = 1;
  67. public static final int FONTFORMAT_T2K = 2;
  68. public static final int FONTFORMAT_TTC = 3;
  69. public static final int FONTFORMAT_COMPOSITE = 4;
  70. public static final int FONTFORMAT_NATIVE = 5;
  71. public static final int NO_FALLBACK = 0;
  72. public static final int PHYSICAL_FALLBACK = 1;
  73. public static final int LOGICAL_FALLBACK = 2;
  74. public static final int QUADPATHTYPE = 1;
  75. public static final int CUBICPATHTYPE = 2;
  76. /* Pool of 20 font file channels chosen because some UTF-8 locale
  77. * composite fonts can use up to 16 platform fonts (including the
  78. * Lucida fall back). This should prevent channel thrashing when
  79. * dealing with one of these fonts.
  80. * The pool array stores the fonts, rather than directly referencing
  81. * the channels, as the font needs to do the open/close work.
  82. */
  83. private static final int CHANNELPOOLSIZE = 20;
  84. private static int lastPoolIndex = 0;
  85. private static int poolSize = 0;
  86. private static FileFont fontFileCache[] = new FileFont[CHANNELPOOLSIZE];
  87. /* Need to implement a simple linked list scheme for fast
  88. * traversal and lookup.
  89. * Also want to "fast path" dialog so there's minimal overhead.
  90. */
  91. /* There are at exactly 20 composite fonts: 5 faces (but some are not
  92. * usually different), in 4 styles. The array may be auto-expanded
  93. * later if more are needed, eg for user-defined composites or locale
  94. * variants.
  95. */
  96. private static int maxCompFont = 0;
  97. private static CompositeFont [] compFonts = new CompositeFont[20];
  98. private static ConcurrentHashMap<String, CompositeFont>
  99. compositeFonts = new ConcurrentHashMap<String, CompositeFont>();
  100. private static ConcurrentHashMap<String, PhysicalFont>
  101. physicalFonts = new ConcurrentHashMap<String, PhysicalFont>();
  102. private static ConcurrentHashMap<String, PhysicalFont>
  103. registeredFontFiles = new ConcurrentHashMap<String, PhysicalFont>();
  104. /* given a full name find the Font. Remind: there's duplication
  105. * here in that this contains the content of compositeFonts +
  106. * physicalFonts.
  107. */
  108. private static ConcurrentHashMap<String, Font2D>
  109. fullNameToFont = new ConcurrentHashMap<String, Font2D>();
  110. /* TrueType fonts have localised names. Support searching all
  111. * of these before giving up on a name.
  112. */
  113. private static HashMap<String, TrueTypeFont> localeFullNamesToFont;
  114. private static PhysicalFont defaultPhysicalFont;
  115. /* deprecated, unsupported hack - actually invokes a bug! */
  116. private static boolean usePlatformFontMetrics = false;
  117. public static Logger logger = null;
  118. public static boolean logging;
  119. static boolean longAddresses;
  120. static String osName;
  121. static boolean useT2K;
  122. static boolean isWindows;
  123. static boolean isSolaris;
  124. public static boolean isSolaris8; // needed to check for JA wavedash fix.
  125. public static boolean isSolaris9; // needed to check for songti font usage.
  126. private static boolean loaded1dot0Fonts = false;
  127. static SunGraphicsEnvironment sgEnv;
  128. static boolean loadedAllFonts = false;
  129. static boolean loadedAllFontFiles = false;
  130. static TrueTypeFont eudcFont;
  131. static HashMap<String,String> jreFontMap;
  132. static HashSet<String> jreLucidaFontFiles;
  133. static String[] jreOtherFontFiles;
  134. static boolean noOtherJREFontFiles = false; // initial assumption.
  135. /* Used to indicate required return type from toArray(..); */
  136. private static String[] STR_ARRAY = new String[0];
  137. private static void initJREFontMap() {
  138. /* Key is familyname+style value as an int.
  139. * Value is filename containing the font.
  140. * If no mapping exists, it means there is no font file for the style
  141. * If the mapping exists but the file doesn't exist in the deferred
  142. * list then it means its not installed.
  143. * This looks like a lot of code and strings but if it saves even
  144. * a single file being opened at JRE start-up there's a big payoff.
  145. * Lucida Sans is probably the only important case as the others
  146. * are rarely used. Consider removing the other mappings if there's
  147. * no evidence they are useful in practice.
  148. */
  149. jreFontMap = new HashMap<String,String>();
  150. jreLucidaFontFiles = new HashSet<String>();
  151. if (SunGraphicsEnvironment.isOpenJDK()) {
  152. return;
  153. }
  154. /* Lucida Sans Family */
  155. jreFontMap.put("lucida sans0", "LucidaSansRegular.ttf");
  156. jreFontMap.put("lucida sans1", "LucidaSansDemiBold.ttf");
  157. /* Lucida Sans full names (map Bold and DemiBold to same file) */
  158. jreFontMap.put("lucida sans regular0", "LucidaSansRegular.ttf");
  159. jreFontMap.put("lucida sans regular1", "LucidaSansDemiBold.ttf");
  160. jreFontMap.put("lucida sans bold1", "LucidaSansDemiBold.ttf");
  161. jreFontMap.put("lucida sans demibold1", "LucidaSansDemiBold.ttf");
  162. /* Lucida Sans Typewriter Family */
  163. jreFontMap.put("lucida sans typewriter0",
  164. "LucidaTypewriterRegular.ttf");
  165. jreFontMap.put("lucida sans typewriter1", "LucidaTypewriterBold.ttf");
  166. /* Typewriter full names (map Bold and DemiBold to same file) */
  167. jreFontMap.put("lucida sans typewriter regular0",
  168. "LucidaTypewriter.ttf");
  169. jreFontMap.put("lucida sans typewriter regular1",
  170. "LucidaTypewriterBold.ttf");
  171. jreFontMap.put("lucida sans typewriter bold1",
  172. "LucidaTypewriterBold.ttf");
  173. jreFontMap.put("lucida sans typewriter demibold1",
  174. "LucidaTypewriterBold.ttf");
  175. /* Lucida Bright Family */
  176. jreFontMap.put("lucida bright0", "LucidaBrightRegular.ttf");
  177. jreFontMap.put("lucida bright1", "LucidaBrightDemiBold.ttf");
  178. jreFontMap.put("lucida bright2", "LucidaBrightItalic.ttf");
  179. jreFontMap.put("lucida bright3", "LucidaBrightDemiItalic.ttf");
  180. /* Lucida Bright full names (map Bold and DemiBold to same file) */
  181. jreFontMap.put("lucida bright regular0", "LucidaBrightRegular.ttf");
  182. jreFontMap.put("lucida bright regular1", "LucidaBrightDemiBold.ttf");
  183. jreFontMap.put("lucida bright regular2", "LucidaBrightItalic.ttf");
  184. jreFontMap.put("lucida bright regular3", "LucidaBrightDemiItalic.ttf");
  185. jreFontMap.put("lucida bright bold1", "LucidaBrightDemiBold.ttf");
  186. jreFontMap.put("lucida bright bold3", "LucidaBrightDemiItalic.ttf");
  187. jreFontMap.put("lucida bright demibold1", "LucidaBrightDemiBold.ttf");
  188. jreFontMap.put("lucida bright demibold3","LucidaBrightDemiItalic.ttf");
  189. jreFontMap.put("lucida bright italic2", "LucidaBrightItalic.ttf");
  190. jreFontMap.put("lucida bright italic3", "LucidaBrightDemiItalic.ttf");
  191. jreFontMap.put("lucida bright bold italic3",
  192. "LucidaBrightDemiItalic.ttf");
  193. jreFontMap.put("lucida bright demibold italic3",
  194. "LucidaBrightDemiItalic.ttf");
  195. for (String ffile : jreFontMap.values()) {
  196. jreLucidaFontFiles.add(ffile);
  197. }
  198. }
  199. static {
  200. if (SunGraphicsEnvironment.debugFonts) {
  201. logger = Logger.getLogger("sun.java2d", null);
  202. logging = logger.getLevel() != Level.OFF;
  203. }
  204. initJREFontMap();
  205. java.security.AccessController.doPrivileged(
  206. new java.security.PrivilegedAction() {
  207. public Object run() {
  208. FontManagerNativeLibrary.load();
  209. // JNI throws an exception if a class/method/field is not found,
  210. // so there's no need to do anything explicit here.
  211. initIDs();
  212. switch (StrikeCache.nativeAddressSize) {
  213. case 8: longAddresses = true; break;
  214. case 4: longAddresses = false; break;
  215. default: throw new RuntimeException("Unexpected address size");
  216. }
  217. osName = System.getProperty("os.name", "unknownOS");
  218. isSolaris = osName.startsWith("SunOS");
  219. if (isSolaris) {
  220. String t2kStr= System.getProperty("sun.java2d.font.scaler");
  221. useT2K = "t2k".equals(t2kStr);
  222. String version = System.getProperty("os.version", "unk");
  223. isSolaris8 = version.equals("5.8");
  224. isSolaris9 = version.equals("5.9");
  225. } else {
  226. isWindows = osName.startsWith("Windows");
  227. if (isWindows) {
  228. String eudcFile =
  229. SunGraphicsEnvironment.eudcFontFileName;
  230. if (eudcFile != null) {
  231. try {
  232. eudcFont = new TrueTypeFont(eudcFile, null, 0,
  233. true);
  234. } catch (FontFormatException e) {
  235. }
  236. }
  237. String prop =
  238. System.getProperty("java2d.font.usePlatformFont");
  239. if (("true".equals(prop) || getPlatformFontVar())) {
  240. usePlatformFontMetrics = true;
  241. System.out.println("Enabling platform font metrics for win32. This is an unsupported option.");
  242. System.out.println("This yields incorrect composite font metrics as reported by 1.1.x releases.");
  243. System.out.println("It is appropriate only for use by applications which do not use any Java 2");
  244. System.out.println("functionality. This property will be removed in a later release.");
  245. }
  246. }
  247. }
  248. return null;
  249. }
  250. });
  251. }
  252. /* Initialise ptrs used by JNI methods */
  253. private static native void initIDs();
  254. public static void addToPool(FileFont font) {
  255. boolean added = false;
  256. synchronized (fontFileCache) {
  257. /* use poolSize to quickly detect if there's any free slots.
  258. * This is a performance tweak based on the assumption that
  259. * if this is executed at all often, its because there are many
  260. * fonts being used and the pool will be full, and we will save
  261. * a fruitless iteration
  262. */
  263. if (poolSize < CHANNELPOOLSIZE) {
  264. for (int i=0; i<CHANNELPOOLSIZE; i++) {
  265. if (fontFileCache[i] == null) {
  266. fontFileCache[i] = font;
  267. poolSize++;
  268. added = true;
  269. break;
  270. }
  271. }
  272. assert added;
  273. } else {
  274. // is it possible for this to be the same font?
  275. assert fontFileCache[lastPoolIndex] != font;
  276. /* replace with new font, poolSize is unchanged. */
  277. fontFileCache[lastPoolIndex].close();
  278. fontFileCache[lastPoolIndex] = font;
  279. /* lastPoolIndex is updated so that the least recently opened
  280. * file will be closed next.
  281. */
  282. lastPoolIndex = (lastPoolIndex+1) % CHANNELPOOLSIZE;
  283. }
  284. }
  285. }
  286. /*
  287. * In the normal course of events, the pool of fonts can remain open
  288. * ready for quick access to their contents. The pool is sized so
  289. * that it is not an excessive consumer of system resources whilst
  290. * facilitating performance by providing ready access to the most
  291. * recently used set of font files.
  292. * The only reason to call removeFromPool(..) is for a Font that
  293. * you want to to have GC'd. Currently this would apply only to fonts
  294. * created with java.awt.Font.createFont(..).
  295. * In this case, the caller is expected to have arranged for the file
  296. * to be closed.
  297. * REMIND: consider how to know when a createFont created font should
  298. * be closed.
  299. */
  300. public static void removeFromPool(FileFont font) {
  301. synchronized (fontFileCache) {
  302. for (int i=0; i<CHANNELPOOLSIZE; i++) {
  303. if (fontFileCache[i] == font) {
  304. fontFileCache[i] = null;
  305. poolSize--;
  306. }
  307. }
  308. }
  309. }
  310. /**
  311. * This method is provided for internal and exclusive use by Swing.
  312. *
  313. * @param font representing a physical font.
  314. * @return true if the underlying font is a TrueType or OpenType font
  315. * that claims to support the Microsoft Windows encoding corresponding to
  316. * the default file.encoding property of this JRE instance.
  317. * This narrow value is useful for Swing to decide if the font is useful
  318. * for the the Windows Look and Feel, or, if a composite font should be
  319. * used instead.
  320. * The information used to make the decision is obtained from
  321. * the ulCodePageRange fields in the font.
  322. * A caller can use isLogicalFont(Font) in this class before calling
  323. * this method and would not need to call this method if that
  324. * returns true.
  325. */
  326. // static boolean fontSupportsDefaultEncoding(Font font) {
  327. // String encoding =
  328. // (String) java.security.AccessController.doPrivileged(
  329. // new sun.security.action.GetPropertyAction("file.encoding"));
  330. // if (encoding == null || font == null) {
  331. // return false;
  332. // }
  333. // encoding = encoding.toLowerCase(Locale.ENGLISH);
  334. // return FontManager.fontSupportsEncoding(font, encoding);
  335. // }
  336. /* Revise the implementation to in fact mean "font is a composite font.
  337. * This ensures that Swing components will always benefit from the
  338. * fall back fonts
  339. */
  340. public static boolean fontSupportsDefaultEncoding(Font font) {
  341. return getFont2D(font) instanceof CompositeFont;
  342. }
  343. /**
  344. * This method is provided for internal and exclusive use by Swing.
  345. *
  346. * It may be used in conjunction with fontSupportsDefaultEncoding(Font)
  347. * In the event that a desktop properties font doesn't directly
  348. * support the default encoding, (ie because the host OS supports
  349. * adding support for the current locale automatically for native apps),
  350. * then Swing calls this method to get a font which uses the specified
  351. * font for the code points it covers, but also supports this locale
  352. * just as the standard composite fonts do.
  353. * Note: this will over-ride any setting where an application
  354. * specifies it prefers locale specific composite fonts.
  355. * The logic for this, is that this method is used only where the user or
  356. * application has specified that the native L&F be used, and that
  357. * we should honour that request to use the same font as native apps use.
  358. *
  359. * The behaviour of this method is to construct a new composite
  360. * Font object that uses the specified physical font as its first
  361. * component, and adds all the components of "dialog" as fall back
  362. * components.
  363. * The method currently assumes that only the size and style attributes
  364. * are set on the specified font. It doesn't copy the font transform or
  365. * other attributes because they aren't set on a font created from
  366. * the desktop. This will need to be fixed if use is broadened.
  367. *
  368. * Operations such as Font.deriveFont will work properly on the
  369. * font returned by this method for deriving a different point size.
  370. * Additionally it tries to support a different style by calling
  371. * getNewComposite() below. That also supports replacing slot zero
  372. * with a different physical font but that is expected to be "rare".
  373. * Deriving with a different style is needed because its been shown
  374. * that some applications try to do this for Swing FontUIResources.
  375. * Also operations such as new Font(font.getFontName(..), Font.PLAIN, 14);
  376. * will NOT yield the same result, as the new underlying CompositeFont
  377. * cannot be "looked up" in the font registry.
  378. * This returns a FontUIResource as that is the Font sub-class needed
  379. * by Swing.
  380. * Suggested usage is something like :
  381. * FontUIResource fuir;
  382. * Font desktopFont = getDesktopFont(..);
  383. * // NOTE even if fontSupportsDefaultEncoding returns true because
  384. * // you get Tahoma and are running in an English locale, you may
  385. * // still want to just call getCompositeFontUIResource() anyway
  386. * // as only then will you get fallback fonts - eg for CJK.
  387. * if (FontManager.fontSupportsDefaultEncoding(desktopFont)) {
  388. * fuir = new FontUIResource(..);
  389. * } else {
  390. * fuir = FontManager.getCompositeFontUIResource(desktopFont);
  391. * }
  392. * return fuir;
  393. */
  394. public static FontUIResource getCompositeFontUIResource(Font font) {
  395. FontUIResource fuir = new FontUIResource(font);
  396. Font2D font2D = getFont2D(font);
  397. if (!(font2D instanceof PhysicalFont)) {
  398. /* Swing should only be calling this when a font is obtained
  399. * from desktop properties, so should generally be a physical font,
  400. * an exception might be for names like "MS Serif" which are
  401. * automatically mapped to "Serif", so there's no need to do
  402. * anything special in that case. But note that suggested usage
  403. * is first to call fontSupportsDefaultEncoding(Font) and this
  404. * method should not be called if that were to return true.
  405. */
  406. return fuir;
  407. }
  408. CompositeFont dialog2D =
  409. (CompositeFont)findFont2D("dialog", font.getStyle(), NO_FALLBACK);
  410. if (dialog2D == null) { /* shouldn't happen */
  411. return fuir;
  412. }
  413. PhysicalFont physicalFont = (PhysicalFont)font2D;
  414. CompositeFont compFont = new CompositeFont(physicalFont, dialog2D);
  415. setFont2D(fuir, compFont.handle);
  416. /* marking this as a created font is needed as only created fonts
  417. * copy their creator's handles.
  418. */
  419. setCreatedFont(fuir);
  420. return fuir;
  421. }
  422. public static Font2DHandle getNewComposite(String family, int style,
  423. Font2DHandle handle) {
  424. if (!(handle.font2D instanceof CompositeFont)) {
  425. return handle;
  426. }
  427. CompositeFont oldComp = (CompositeFont)handle.font2D;
  428. PhysicalFont oldFont = oldComp.getSlotFont(0);
  429. if (family == null) {
  430. family = oldFont.getFamilyName(null);
  431. }
  432. if (style == -1) {
  433. style = oldComp.getStyle();
  434. }
  435. Font2D newFont = findFont2D(family, style, NO_FALLBACK);
  436. if (!(newFont instanceof PhysicalFont)) {
  437. newFont = oldFont;
  438. }
  439. PhysicalFont physicalFont = (PhysicalFont)newFont;
  440. CompositeFont dialog2D =
  441. (CompositeFont)findFont2D("dialog", style, NO_FALLBACK);
  442. if (dialog2D == null) { /* shouldn't happen */
  443. return handle;
  444. }
  445. CompositeFont compFont = new CompositeFont(physicalFont, dialog2D);
  446. Font2DHandle newHandle = new Font2DHandle(compFont);
  447. return newHandle;
  448. }
  449. public static native void setFont2D(Font font, Font2DHandle font2DHandle);
  450. private static native boolean isCreatedFont(Font font);
  451. private static native void setCreatedFont(Font font);
  452. public static void registerCompositeFont(String compositeName,
  453. String[] componentFileNames,
  454. String[] componentNames,
  455. int numMetricsSlots,
  456. int[] exclusionRanges,
  457. int[] exclusionMaxIndex,
  458. boolean defer) {
  459. CompositeFont cf = new CompositeFont(compositeName,
  460. componentFileNames,
  461. componentNames,
  462. numMetricsSlots,
  463. exclusionRanges,
  464. exclusionMaxIndex, defer);
  465. addCompositeToFontList(cf, Font2D.FONT_CONFIG_RANK);
  466. synchronized (compFonts) {
  467. compFonts[maxCompFont++] = cf;
  468. }
  469. }
  470. /* This variant is used only when the application specifies
  471. * a variant of composite fonts which prefers locale specific or
  472. * proportional fonts.
  473. */
  474. public static void registerCompositeFont(String compositeName,
  475. String[] componentFileNames,
  476. String[] componentNames,
  477. int numMetricsSlots,
  478. int[] exclusionRanges,
  479. int[] exclusionMaxIndex,
  480. boolean defer,
  481. ConcurrentHashMap<String, Font2D>
  482. altNameCache) {
  483. CompositeFont cf = new CompositeFont(compositeName,
  484. componentFileNames,
  485. componentNames,
  486. numMetricsSlots,
  487. exclusionRanges,
  488. exclusionMaxIndex, defer);
  489. /* if the cache has an existing composite for this case, make
  490. * its handle point to this new font.
  491. * This ensures that when the altNameCache that is passed in
  492. * is the global mapNameCache - ie we are running as an application -
  493. * that any statically created java.awt.Font instances which already
  494. * have a Font2D instance will have that re-directed to the new Font
  495. * on subsequent uses. This is particularly important for "the"
  496. * default font instance, or similar cases where a UI toolkit (eg
  497. * Swing) has cached a java.awt.Font. Note that if Swing is using
  498. * a custom composite APIs which update the standard composites have
  499. * no effect - this is typically the case only when using the Windows
  500. * L&F where these APIs would conflict with that L&F anyway.
  501. */
  502. Font2D oldFont = (Font2D)
  503. altNameCache.get(compositeName.toLowerCase(Locale.ENGLISH));
  504. if (oldFont instanceof CompositeFont) {
  505. oldFont.handle.font2D = cf;
  506. }
  507. altNameCache.put(compositeName.toLowerCase(Locale.ENGLISH), cf);
  508. }
  509. private static void addCompositeToFontList(CompositeFont f, int rank) {
  510. if (logging) {
  511. logger.info("Add to Family "+ f.familyName +
  512. ", Font " + f.fullName + " rank="+rank);
  513. }
  514. f.setRank(rank);
  515. compositeFonts.put(f.fullName, f);
  516. fullNameToFont.put(f.fullName.toLowerCase(Locale.ENGLISH), f);
  517. FontFamily family = FontFamily.getFamily(f.familyName);
  518. if (family == null) {
  519. family = new FontFamily(f.familyName, true, rank);
  520. }
  521. family.setFont(f, f.style);
  522. }
  523. /*
  524. * Systems may have fonts with the same name.
  525. * We want to register only one of such fonts (at least until
  526. * such time as there might be APIs which can accommodate > 1).
  527. * Rank is 1) font configuration fonts, 2) JRE fonts, 3) OT/TT fonts,
  528. * 4) Type1 fonts, 5) native fonts.
  529. *
  530. * If the new font has the same name as the old font, the higher
  531. * ranked font gets added, replacing the lower ranked one.
  532. * If the fonts are of equal rank, then make a special case of
  533. * font configuration rank fonts, which are on closer inspection,
  534. * OT/TT fonts such that the larger font is registered. This is
  535. * a heuristic since a font may be "larger" in the sense of more
  536. * code points, or be a larger "file" because it has more bitmaps.
  537. * So it is possible that using filesize may lead to less glyphs, and
  538. * using glyphs may lead to lower quality display. Probably number
  539. * of glyphs is the ideal, but filesize is information we already
  540. * have and is good enough for the known cases.
  541. * Also don't want to register fonts that match JRE font families
  542. * but are coming from a source other than the JRE.
  543. * This will ensure that we will algorithmically style the JRE
  544. * plain font and get the same set of glyphs for all styles.
  545. *
  546. * Note that this method returns a value
  547. * if it returns the same object as its argument that means this
  548. * font was newly registered.
  549. * If it returns a different object it means this font already exists,
  550. * and you should use that one.
  551. * If it returns null means this font was not registered and none
  552. * in that name is registered. The caller must find a substitute
  553. */
  554. private static PhysicalFont addToFontList(PhysicalFont f, int rank) {
  555. String fontName = f.fullName;
  556. String familyName = f.familyName;
  557. if (fontName == null || "".equals(fontName)) {
  558. return null;
  559. }
  560. if (compositeFonts.containsKey(fontName)) {
  561. /* Don't register any font that has the same name as a composite */
  562. return null;
  563. }
  564. f.setRank(rank);
  565. if (!physicalFonts.containsKey(fontName)) {
  566. if (logging) {
  567. logger.info("Add to Family "+familyName +
  568. ", Font " + fontName + " rank="+rank);
  569. }
  570. physicalFonts.put(fontName, f);
  571. FontFamily family = FontFamily.getFamily(familyName);
  572. if (family == null) {
  573. family = new FontFamily(familyName, false, rank);
  574. family.setFont(f, f.style);
  575. } else if (family.getRank() >= rank) {
  576. family.setFont(f, f.style);
  577. }
  578. fullNameToFont.put(fontName.toLowerCase(Locale.ENGLISH), f);
  579. return f;
  580. } else {
  581. PhysicalFont newFont = f;
  582. PhysicalFont oldFont = physicalFonts.get(fontName);
  583. if (oldFont == null) {
  584. return null;
  585. }
  586. /* If the new font is of an equal or higher rank, it is a
  587. * candidate to replace the current one, subject to further tests.
  588. */
  589. if (oldFont.getRank() >= rank) {
  590. /* All fonts initialise their mapper when first
  591. * used. If the mapper is non-null then this font
  592. * has been accessed at least once. In that case
  593. * do not replace it. This may be overly stringent,
  594. * but its probably better not to replace a font that
  595. * someone is already using without a compelling reason.
  596. * Additionally the primary case where it is known
  597. * this behaviour is important is in certain composite
  598. * fonts, and since all the components of a given
  599. * composite are usually initialised together this
  600. * is unlikely. For this to be a problem, there would
  601. * have to be a case where two different composites used
  602. * different versions of the same-named font, and they
  603. * were initialised and used at separate times.
  604. * In that case we continue on and allow the new font to
  605. * be installed, but replaceFont will continue to allow
  606. * the original font to be used in Composite fonts.
  607. */
  608. if (oldFont.mapper != null && rank > Font2D.FONT_CONFIG_RANK) {
  609. return oldFont;
  610. }
  611. /* Normally we require a higher rank to replace a font,
  612. * but as a special case, if the two fonts are the same rank,
  613. * and are instances of TrueTypeFont we want the
  614. * more complete (larger) one.
  615. */
  616. if (oldFont.getRank() == rank) {
  617. if (oldFont instanceof TrueTypeFont &&
  618. newFont instanceof TrueTypeFont) {
  619. TrueTypeFont oldTTFont = (TrueTypeFont)oldFont;
  620. TrueTypeFont newTTFont = (TrueTypeFont)newFont;
  621. if (oldTTFont.fileSize >= newTTFont.fileSize) {
  622. return oldFont;
  623. }
  624. } else {
  625. return oldFont;
  626. }
  627. }
  628. /* Don't replace ever JRE fonts.
  629. * This test is in case a font configuration references
  630. * a Lucida font, which has been mapped to a Lucida
  631. * from the host O/S. The assumption here is that any
  632. * such font configuration file is probably incorrect, or
  633. * the host O/S version is for the use of AWT.
  634. * In other words if we reach here, there's a possible
  635. * problem with our choice of font configuration fonts.
  636. */
  637. if (oldFont.platName.startsWith(
  638. SunGraphicsEnvironment.jreFontDirName)) {
  639. if (logging) {
  640. logger.warning("Unexpected attempt to replace a JRE " +
  641. " font " + fontName + " from " +
  642. oldFont.platName +
  643. " with " + newFont.platName);
  644. }
  645. return oldFont;
  646. }
  647. if (logging) {
  648. logger.info("Replace in Family " + familyName +
  649. ",Font " + fontName + " new rank="+rank +
  650. " from " + oldFont.platName +
  651. " with " + newFont.platName);
  652. }
  653. replaceFont(oldFont, newFont);
  654. physicalFonts.put(fontName, newFont);
  655. fullNameToFont.put(fontName.toLowerCase(Locale.ENGLISH),
  656. newFont);
  657. FontFamily family = FontFamily.getFamily(familyName);
  658. if (family == null) {
  659. family = new FontFamily(familyName, false, rank);
  660. family.setFont(newFont, newFont.style);
  661. } else if (family.getRank() >= rank) {
  662. family.setFont(newFont, newFont.style);
  663. }
  664. return newFont;
  665. } else {
  666. return oldFont;
  667. }
  668. }
  669. }
  670. public static Font2D[] getRegisteredFonts() {
  671. PhysicalFont[] physFonts = getPhysicalFonts();
  672. int mcf = maxCompFont; /* for MT-safety */
  673. Font2D[] regFonts = new Font2D[physFonts.length+mcf];
  674. System.arraycopy(compFonts, 0, regFonts, 0, mcf);
  675. System.arraycopy(physFonts, 0, regFonts, mcf, physFonts.length);
  676. return regFonts;
  677. }
  678. public static PhysicalFont[] getPhysicalFonts() {
  679. return physicalFonts.values().toArray(new PhysicalFont[0]);
  680. }
  681. /* The class FontRegistrationInfo is used when a client says not
  682. * to register a font immediately. This mechanism is used to defer
  683. * initialisation of all the components of composite fonts at JRE
  684. * start-up. The CompositeFont class is "aware" of this and when it
  685. * is first used it asks for the registration of its components.
  686. * Also in the event that any physical font is requested the
  687. * deferred fonts are initialised before triggering a search of the
  688. * system.
  689. * Two maps are used. One to track the deferred fonts. The
  690. * other to track the fonts that have been initialised through this
  691. * mechanism.
  692. */
  693. private static final class FontRegistrationInfo {
  694. String fontFilePath;
  695. String[] nativeNames;
  696. int fontFormat;
  697. boolean javaRasterizer;
  698. int fontRank;
  699. FontRegistrationInfo(String fontPath, String[] names, int format,
  700. boolean useJavaRasterizer, int rank) {
  701. this.fontFilePath = fontPath;
  702. this.nativeNames = names;
  703. this.fontFormat = format;
  704. this.javaRasterizer = useJavaRasterizer;
  705. this.fontRank = rank;
  706. }
  707. }
  708. private static final ConcurrentHashMap<String, FontRegistrationInfo>
  709. deferredFontFiles =
  710. new ConcurrentHashMap<String, FontRegistrationInfo>();
  711. private static final ConcurrentHashMap<String, Font2DHandle>
  712. initialisedFonts = new ConcurrentHashMap<String, Font2DHandle>();
  713. /* Remind: possibly enhance initialiseDeferredFonts() to be
  714. * optionally given a name and a style and it could stop when it
  715. * finds that font - but this would be a problem if two of the
  716. * fonts reference the same font face name (cf the Solaris
  717. * euro fonts).
  718. */
  719. public static synchronized void initialiseDeferredFonts() {
  720. for (String fileName : deferredFontFiles.keySet()) {
  721. initialiseDeferredFont(fileName);
  722. }
  723. }
  724. public static synchronized void registerDeferredJREFonts(String jreDir) {
  725. for (FontRegistrationInfo info : deferredFontFiles.values()) {
  726. if (info.fontFilePath != null &&
  727. info.fontFilePath.startsWith(jreDir)) {
  728. initialiseDeferredFont(info.fontFilePath);
  729. }
  730. }
  731. }
  732. /* We keep a map of the files which contain the Lucida fonts so we
  733. * don't need to search for them.
  734. * But since we know what fonts these files contain, we can also avoid
  735. * opening them to look for a font name we don't recognise - see
  736. * findDeferredFont().
  737. * For typical cases where the font isn't a JRE one the overhead is
  738. * this method call, HashMap.get() and null reference test, then
  739. * a boolean test of noOtherJREFontFiles.
  740. */
  741. private static PhysicalFont findJREDeferredFont(String name, int style) {
  742. PhysicalFont physicalFont;
  743. String nameAndStyle = name.toLowerCase(Locale.ENGLISH) + style;
  744. String fileName = jreFontMap.get(nameAndStyle);
  745. if (fileName != null) {
  746. initSGEnv(); /* ensure jreFontDirName is initialised */
  747. fileName = SunGraphicsEnvironment.jreFontDirName +
  748. File.separator + fileName;
  749. if (deferredFontFiles.get(fileName) != null) {
  750. physicalFont = initialiseDeferredFont(fileName);
  751. if (physicalFont != null &&
  752. (physicalFont.getFontName(null).equalsIgnoreCase(name) ||
  753. physicalFont.getFamilyName(null).equalsIgnoreCase(name))
  754. && physicalFont.style == style) {
  755. return physicalFont;
  756. }
  757. }
  758. }
  759. /* Iterate over the deferred font files looking for any in the
  760. * jre directory that we didn't recognise, open each of these.
  761. * In almost all installations this will quickly fall through
  762. * because only the Lucidas will be present and jreOtherFontFiles
  763. * will be empty.
  764. * noOtherJREFontFiles is used so we can skip this block as soon
  765. * as its determined that its not needed - almost always after the
  766. * very first time through.
  767. */
  768. if (noOtherJREFontFiles) {
  769. return null;
  770. }
  771. synchronized (jreLucidaFontFiles) {
  772. if (jreOtherFontFiles == null) {
  773. HashSet<String> otherFontFiles = new HashSet<String>();
  774. for (String deferredFile : deferredFontFiles.keySet()) {
  775. File file = new File(deferredFile);
  776. String dir = file.getParent();
  777. String fname = file.getName();
  778. /* skip names which aren't absolute, aren't in the JRE
  779. * directory, or are known Lucida fonts.
  780. */
  781. if (dir == null ||
  782. !dir.equals(SunGraphicsEnvironment.jreFontDirName) ||
  783. jreLucidaFontFiles.contains(fname)) {
  784. continue;
  785. }
  786. otherFontFiles.add(deferredFile);
  787. }
  788. jreOtherFontFiles = otherFontFiles.toArray(STR_ARRAY);
  789. if (jreOtherFontFiles.length == 0) {
  790. noOtherJREFontFiles = true;
  791. }
  792. }
  793. for (int i=0; i<jreOtherFontFiles.length;i++) {
  794. fileName = jreOtherFontFiles[i];
  795. if (fileName == null) {
  796. continue;
  797. }
  798. jreOtherFontFiles[i] = null;
  799. physicalFont = initialiseDeferredFont(fileName);
  800. if (physicalFont != null &&
  801. (physicalFont.getFontName(null).equalsIgnoreCase(name) ||
  802. physicalFont.getFamilyName(null).equalsIgnoreCase(name))
  803. && physicalFont.style == style) {
  804. return physicalFont;
  805. }
  806. }
  807. }
  808. return null;
  809. }
  810. /* This skips JRE installed fonts. */
  811. private static PhysicalFont findOtherDeferredFont(String name, int style) {
  812. for (String fileName : deferredFontFiles.keySet()) {
  813. File file = new File(fileName);
  814. String dir = file.getParent();
  815. String fname = file.getName();
  816. if (dir != null &&
  817. dir.equals(SunGraphicsEnvironment.jreFontDirName) &&
  818. jreLucidaFontFiles.contains(fname)) {
  819. continue;
  820. }
  821. PhysicalFont physicalFont = initialiseDeferredFont(fileName);
  822. if (physicalFont != null &&
  823. (physicalFont.getFontName(null).equalsIgnoreCase(name) ||
  824. physicalFont.getFamilyName(null).equalsIgnoreCase(name)) &&
  825. physicalFont.style == style) {
  826. return physicalFont;
  827. }
  828. }
  829. return null;
  830. }
  831. private static PhysicalFont findDeferredFont(String name, int style) {
  832. PhysicalFont physicalFont = findJREDeferredFont(name, style);
  833. if (physicalFont != null) {
  834. return physicalFont;
  835. } else {
  836. return findOtherDeferredFont(name, style);
  837. }
  838. }
  839. public static void registerDeferredFont(String fileNameKey,
  840. String fullPathName,
  841. String[] nativeNames,
  842. int fontFormat,
  843. boolean useJavaRasterizer,
  844. int fontRank) {
  845. FontRegistrationInfo regInfo =
  846. new FontRegistrationInfo(fullPathName, nativeNames, fontFormat,
  847. useJavaRasterizer, fontRank);
  848. deferredFontFiles.put(fileNameKey, regInfo);
  849. }
  850. public static synchronized
  851. PhysicalFont initialiseDeferredFont(String fileNameKey) {
  852. if (fileNameKey == null) {
  853. return null;
  854. }
  855. if (logging) {
  856. logger.info("Opening deferred font file " + fileNameKey);
  857. }
  858. PhysicalFont physicalFont;
  859. FontRegistrationInfo regInfo = deferredFontFiles.get(fileNameKey);
  860. if (regInfo != null) {
  861. deferredFontFiles.remove(fileNameKey);
  862. physicalFont = registerFontFile(regInfo.fontFilePath,
  863. regInfo.nativeNames,
  864. regInfo.fontFormat,
  865. regInfo.javaRasterizer,
  866. regInfo.fontRank);
  867. if (physicalFont != null) {
  868. /* Store the handle, so that if a font is bad, we
  869. * retrieve the substituted font.
  870. */
  871. initialisedFonts.put(fileNameKey, physicalFont.handle);
  872. } else {
  873. initialisedFonts.put(fileNameKey,
  874. getDefaultPhysicalFont().handle);
  875. }
  876. } else {
  877. Font2DHandle handle = initialisedFonts.get(fileNameKey);
  878. if (handle == null) {
  879. /* Probably shouldn't happen, but just in case */
  880. physicalFont = getDefaultPhysicalFont();
  881. } else {
  882. physicalFont = (PhysicalFont)(handle.font2D);
  883. }
  884. }
  885. return physicalFont;
  886. }
  887. /* Note that the return value from this method is not always
  888. * derived from this file, and may be null. See addToFontList for
  889. * some explanation of this.
  890. */
  891. public static PhysicalFont registerFontFile(String fileName,
  892. String[] nativeNames,
  893. int fontFormat,
  894. boolean useJavaRasterizer,
  895. int fontRank) {
  896. PhysicalFont regFont = registeredFontFiles.get(fileName);
  897. if (regFont != null) {
  898. return regFont;
  899. }
  900. PhysicalFont physicalFont = null;
  901. try {
  902. String name;
  903. switch (fontFormat) {
  904. case FontManager.FONTFORMAT_TRUETYPE:
  905. int fn = 0;
  906. TrueTypeFont ttf;
  907. do {
  908. ttf = new TrueTypeFont(fileName, nativeNames, fn++,
  909. useJavaRasterizer);
  910. PhysicalFont pf = addToFontList(ttf, fontRank);
  911. if (physicalFont == null) {
  912. physicalFont = pf;
  913. }
  914. }
  915. while (fn < ttf.getFontCount());
  916. break;
  917. case FontManager.FONTFORMAT_TYPE1:
  918. Type1Font t1f = new Type1Font(fileName, nativeNames);
  919. physicalFont = addToFontList(t1f, fontRank);
  920. break;
  921. case FontManager.FONTFORMAT_NATIVE:
  922. NativeFont nf = new NativeFont(fileName, false);
  923. physicalFont = addToFontList(nf, fontRank);
  924. default:
  925. }
  926. if (logging) {
  927. logger.info("Registered file " + fileName + " as font " +
  928. physicalFont + " rank=" + fontRank);
  929. }
  930. } catch (FontFormatException ffe) {
  931. if (logging) {
  932. logger.warning("Unusable font: " +
  933. fileName + " " + ffe.toString());
  934. }
  935. }
  936. if (physicalFont != null &&
  937. fontFormat != FontManager.FONTFORMAT_NATIVE) {
  938. registeredFontFiles.put(fileName, physicalFont);
  939. }
  940. return physicalFont;
  941. }
  942. public static void registerFonts(String[] fileNames,
  943. String[][] nativeNames,
  944. int fontCount,
  945. int fontFormat,
  946. boolean useJavaRasterizer,
  947. int fontRank, boolean defer) {
  948. for (int i=0; i < fontCount; i++) {
  949. if (defer) {
  950. registerDeferredFont(fileNames[i],fileNames[i], nativeNames[i],
  951. fontFormat, useJavaRasterizer, fontRank);
  952. } else {
  953. registerFontFile(fileNames[i], nativeNames[i],
  954. fontFormat, useJavaRasterizer, fontRank);
  955. }
  956. }
  957. }
  958. /*
  959. * This is the Physical font used when some other font on the system
  960. * can't be located. There has to be at least one font or the font
  961. * system is not useful and the graphics environment cannot sustain
  962. * the Java platform.
  963. */
  964. public static PhysicalFont getDefaultPhysicalFont() {
  965. if (defaultPhysicalFont == null) {
  966. /* findFont2D will load all fonts before giving up the search.
  967. * If the JRE Lucida isn't found (eg because the JRE fonts
  968. * directory is missing), it could find another version of Lucida
  969. * from the host system. This is OK because at that point we are
  970. * trying to gracefully handle/recover from a system
  971. * misconfiguration and this is probably a reasonable substitution.
  972. */
  973. defaultPhysicalFont = (PhysicalFont)
  974. findFont2D("Lucida Sans Regular", Font.PLAIN, NO_FALLBACK);
  975. if (defaultPhysicalFont == null) {
  976. defaultPhysicalFont = (PhysicalFont)
  977. findFont2D("Arial", Font.PLAIN, NO_FALLBACK);
  978. }
  979. if (defaultPhysicalFont == null) {
  980. /* Because of the findFont2D call above, if we reach here, we
  981. * know all fonts have already been loaded, just accept any
  982. * match at this point. If this fails we are in real trouble
  983. * and I don't know how to recover from there being absolutely
  984. * no fonts anywhere on the system.
  985. */
  986. Iterator i = physicalFonts.values().iterator();
  987. if (i.hasNext()) {
  988. defaultPhysicalFont = (PhysicalFont)i.next();
  989. } else {
  990. throw new Error("Probable fatal error:No fonts found.");
  991. }
  992. }
  993. }
  994. return defaultPhysicalFont;
  995. }
  996. public static CompositeFont getDefaultLogicalFont(int style) {
  997. return (CompositeFont)findFont2D("dialog", style, NO_FALLBACK);
  998. }
  999. /*
  1000. * return String representation of style prepended with "."
  1001. * This is useful for performance to avoid unnecessary string operations.
  1002. */
  1003. private static String dotStyleStr(int num) {
  1004. switch(num){
  1005. case Font.BOLD:
  1006. return ".bold";
  1007. case Font.ITALIC:
  1008. return ".italic";
  1009. case Font.ITALIC | Font.BOLD:
  1010. return ".bolditalic";
  1011. default:
  1012. return ".plain";
  1013. }
  1014. }
  1015. static void initSGEnv() {
  1016. if (sgEnv == null) {
  1017. GraphicsEnvironment ge =
  1018. GraphicsEnvironment.getLocalGraphicsEnvironment();
  1019. if (ge instanceof HeadlessGraphicsEnvironment) {
  1020. HeadlessGraphicsEnvironment hgEnv =
  1021. (HeadlessGraphicsEnvironment)ge;
  1022. sgEnv = (SunGraphicsEnvironment)
  1023. hgEnv.getSunGraphicsEnvironment();
  1024. } else {
  1025. sgEnv = (SunGraphicsEnvironment)ge;
  1026. }
  1027. }
  1028. }
  1029. /* This is implemented only on windows and is called from code that
  1030. * executes only on windows. This isn't pretty but its not a precedent
  1031. * in t…

Large files files are truncated, but you can click here to view the full file