001: /*
002: * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.font;
027:
028: import java.awt.Font;
029: import java.awt.FontFormatException;
030: import java.awt.GraphicsEnvironment;
031: import java.awt.font.FontRenderContext;
032: import java.awt.geom.GeneralPath;
033: import java.awt.geom.Point2D;
034: import java.awt.geom.Rectangle2D;
035: import java.io.UnsupportedEncodingException;
036: import java.lang.ref.WeakReference;
037: import java.util.Locale;
038:
039: /*
040: * Ideally there would be no native fonts used, and this class would be
041: * unneeded and removed. Presently it is still needed until such time
042: * as font configuration files (or the implementation equivalent) can have
043: * all references to fonts that are not handled via Java 2D removed.
044: * Currently there are two cases where this class is needed, both on
045: * Unix, primarily Solaris, but useful on Linux too if fonts have moved.
046: * 1. Some legacy F3 fonts are still referenced so that AWT "X/Motif"
047: * can get dingbats and symbols from them. This can be dispensed with when
048: * either AWT is based on 2D, or when the X font path is known to always
049: * contain a Type1 or TrueType font that can be used in font configuration
050: * files to replace the F3 fonts.
051: * 2. When location of font files by 2D fails, because of some system
052: * configuration problem, it is desirable to have a fall back to some
053: * functionality that lessens the immediate impact on users. Being able
054: * to perform limited operations by using bitmaps from X11 helps here.
055: */
056:
057: public class NativeFont extends PhysicalFont {
058:
059: String encoding;
060:
061: private int numGlyphs = -1;
062: boolean isBitmapDelegate;
063: PhysicalFont delegateFont;
064:
065: /**
066: * Verifies native font is accessible.
067: * @throws FontFormatException - if the font can't be located.
068: */
069: public NativeFont(String platName, boolean bitmapDelegate)
070: throws FontFormatException {
071: super (platName, null);
072:
073: /* This is set true if this is an instance of a NativeFont
074: * created by some other font, to get native bitmaps.
075: * The delegating font will call this font only for "basic"
076: * cases - ie non-rotated, uniform scale, monochrome bitmaps.
077: * If this is false, then this instance may need to itself
078: * delegate to another font for non-basic cases. Since
079: * NativeFonts are used in that way only for symbol and dingbats
080: * we know its safe to delegate these to the JRE's default
081: * physical font (Lucida Sans Regular).
082: */
083: isBitmapDelegate = bitmapDelegate;
084:
085: if (GraphicsEnvironment.isHeadless()) {
086: throw new FontFormatException(
087: "Native font in headless toolkit");
088: }
089: fontRank = Font2D.NATIVE_RANK;
090: initNames();
091: if (getNumGlyphs() == 0) {
092: throw new FontFormatException("Couldn't locate font"
093: + platName);
094: }
095: }
096:
097: private void initNames() throws FontFormatException {
098: /* Valid XLFD has exactly 14 "-" chars.
099: * First run over the string to verify have at least this many
100: * At the same time record the locations of the hyphens
101: * so we can just pick the right substring later on
102: */
103: int[] hPos = new int[14];
104: int hyphenCnt = 1;
105: int pos = 1;
106:
107: String xlfd = platName.toLowerCase(Locale.ENGLISH);
108: if (xlfd.startsWith("-")) {
109: while (pos != -1 && hyphenCnt < 14) {
110: pos = xlfd.indexOf('-', pos);
111: if (pos != -1) {
112: hPos[hyphenCnt++] = pos;
113: pos++;
114: }
115: }
116: }
117:
118: if (hyphenCnt == 14 && pos != -1) {
119:
120: /* Capitalise words in the Family name */
121: String tmpFamily = xlfd.substring(hPos[1] + 1, hPos[2]);
122: StringBuilder sBuffer = new StringBuilder(tmpFamily);
123: char ch = Character.toUpperCase(sBuffer.charAt(0));
124: sBuffer.replace(0, 1, String.valueOf(ch));
125: for (int i = 1; i < sBuffer.length() - 1; i++) {
126: if (sBuffer.charAt(i) == ' ') {
127: ch = Character.toUpperCase(sBuffer.charAt(i + 1));
128: sBuffer.replace(i + 1, i + 2, String.valueOf(ch));
129: }
130: }
131: familyName = sBuffer.toString();
132:
133: String tmpWeight = xlfd.substring(hPos[2] + 1, hPos[3]);
134: String tmpSlant = xlfd.substring(hPos[3] + 1, hPos[4]);
135:
136: String styleStr = null;
137:
138: if (tmpWeight.indexOf("bold") >= 0
139: || tmpWeight.indexOf("demi") >= 0) {
140: style |= Font.BOLD;
141: styleStr = "Bold";
142: }
143:
144: if (tmpSlant.equals("i") || tmpSlant.indexOf("italic") >= 0) {
145: style |= Font.ITALIC;
146:
147: if (styleStr == null) {
148: styleStr = "Italic";
149: } else {
150: styleStr = styleStr + " Italic";
151: }
152: } else if (tmpSlant.equals("o")
153: || tmpSlant.indexOf("oblique") >= 0) {
154: style |= Font.ITALIC;
155: if (styleStr == null) {
156: styleStr = "Oblique";
157: } else {
158: styleStr = styleStr + " Oblique";
159: }
160: }
161:
162: if (styleStr == null) {
163: fullName = familyName;
164: } else {
165: fullName = familyName + " " + styleStr;
166: }
167:
168: encoding = xlfd.substring(hPos[12] + 1);
169: if (encoding.startsWith("-")) {
170: encoding = xlfd.substring(hPos[13] + 1);
171: }
172: if (encoding.indexOf("fontspecific") >= 0) {
173: if (tmpFamily.indexOf("dingbats") >= 0) {
174: encoding = "dingbats";
175: } else if (tmpFamily.indexOf("symbol") >= 0) {
176: encoding = "symbol";
177: } else {
178: encoding = "iso8859-1";
179: }
180: }
181: } else {
182: throw new FontFormatException("Bad native name " + platName);
183: // familyName = "Unknown";
184: // fullName = "Unknown";
185: // style = Font.PLAIN;
186: // encoding = "iso8859-1";
187: }
188: }
189:
190: /* Wildcard all the size fields in the XLFD and retrieve a list of
191: * XLFD's that match.
192: * We only look for scaleable fonts, so we can just replace the 0's
193: * with *'s and see what we get back
194: * No matches means even the scaleable version wasn't found. This is
195: * means the X font path isn't set up for this font at all.
196: * One match means only the scaleable version we started with was found
197: * -monotype-arial-bold-i-normal--0-0-0-0-p-0-iso8859-1
198: * Two matches apparently means as well as the above, a scaleable
199: * specified for 72 dpi is found, not that there are bitmaps : eg
200: * -monotype-arial-bold-i-normal--0-0-72-72-p-0-iso8859-1
201: * So require at least 3 matches (no need to parse) to determine that
202: * there are external bitmaps.
203: */
204: static boolean hasExternalBitmaps(String platName) {
205: /* Turn -monotype-arial-bold-i-normal--0-0-0-0-p-0-iso8859-1
206: * into -monotype-arial-bold-i-normal--*-*-*-*-p-*-iso8859-1
207: * by replacing all -0- substrings with -*-
208: */
209: StringBuilder sb = new StringBuilder(platName);
210: int pos = sb.indexOf("-0-");
211: while (pos >= 0) {
212: sb.replace(pos + 1, pos + 2, "*");
213: pos = sb.indexOf("-0-", pos);
214: }
215: ;
216: String xlfd = sb.toString();
217: byte[] bytes = null;
218: try {
219: bytes = xlfd.getBytes("UTF-8");
220: } catch (UnsupportedEncodingException e) {
221: bytes = xlfd.getBytes();
222: }
223: return haveBitmapFonts(bytes);
224: }
225:
226: public static boolean fontExists(String xlfd) {
227: byte[] bytes = null;
228: try {
229: bytes = xlfd.getBytes("UTF-8");
230: } catch (UnsupportedEncodingException e) {
231: bytes = xlfd.getBytes();
232: }
233: return fontExists(bytes);
234: }
235:
236: private static native boolean haveBitmapFonts(byte[] xlfd);
237:
238: private static native boolean fontExists(byte[] xlfd);
239:
240: public CharToGlyphMapper getMapper() {
241: if (mapper == null) {
242: if (isBitmapDelegate) {
243: /* we are a delegate */
244: mapper = new NativeGlyphMapper(this );
245: } else {
246: /* we need to delegate */
247: delegateFont = FontManager.getDefaultPhysicalFont();
248: mapper = delegateFont.getMapper();
249: }
250: }
251: return mapper;
252: }
253:
254: FontStrike createStrike(FontStrikeDesc desc) {
255: if (isBitmapDelegate) {
256: return new NativeStrike(this , desc);
257: } else {
258: if (delegateFont == null) {
259: delegateFont = FontManager.getDefaultPhysicalFont();
260: }
261: /* If no FileFont's are found, delegate font may be
262: * a NativeFont, so we need to avoid recursing here.
263: */
264: if (delegateFont instanceof NativeFont) {
265: return new NativeStrike((NativeFont) delegateFont, desc);
266: }
267: FontStrike delegate = delegateFont.createStrike(desc);
268: return new DelegateStrike(this , desc, delegate);
269: }
270: }
271:
272: public Rectangle2D getMaxCharBounds(FontRenderContext frc) {
273: return null;
274: }
275:
276: native StrikeMetrics getFontMetrics(long pScalerContext);
277:
278: native float getGlyphAdvance(long pContext, int glyphCode);
279:
280: Rectangle2D.Float getGlyphOutlineBounds(long pScalerContext,
281: int glyphCode) {
282: return new Rectangle2D.Float(0f, 0f, 0f, 0f);
283: }
284:
285: public GeneralPath getGlyphOutline(long pScalerContext,
286: int glyphCode, float x, float y) {
287: return null;
288: }
289:
290: native long getGlyphImage(long pScalerContext, int glyphCode);
291:
292: native long getGlyphImageNoDefault(long pScalerContext,
293: int glyphCode);
294:
295: void getGlyphMetrics(long pScalerContext, int glyphCode,
296: Point2D.Float metrics) {
297: throw new RuntimeException(
298: "this should be called on the strike");
299: }
300:
301: public GeneralPath getGlyphVectorOutline(long pScalerContext,
302: int[] glyphs, int numGlyphs, float x, float y) {
303: return null;
304: }
305:
306: private native int countGlyphs(byte[] platformNameBytes, int ptSize);
307:
308: public int getNumGlyphs() {
309: if (numGlyphs == -1) {
310: byte[] bytes = getPlatformNameBytes(8);
311: numGlyphs = countGlyphs(bytes, 8);
312: }
313: return numGlyphs;
314: }
315:
316: PhysicalFont getDelegateFont() {
317: if (delegateFont == null) {
318: delegateFont = FontManager.getDefaultPhysicalFont();
319: }
320: return delegateFont;
321: }
322:
323: /* Specify that the dpi is 72x72, as this corresponds to JDK's
324: * default user space. These are the 10th and 11th fields in the XLFD.
325: * ptSize in XLFD is in 10th's of a point so multiply by 10,
326: * Replace the 9th field in the XLFD (ie after the 8th hyphen)
327: * with this pt size (this corresponds to the field that's "%d" in the
328: * font configuration files). Wild card the other numeric fields.
329: * ie to request 12 pt Times New Roman italic font, use an XLFD like :
330: * -monotype-times new roman-regular-i---*-120-72-72-p-*-iso8859-1
331: */
332: byte[] getPlatformNameBytes(int ptSize) {
333: int[] hPos = new int[14];
334: int hyphenCnt = 1;
335: int pos = 1;
336:
337: while (pos != -1 && hyphenCnt < 14) {
338: pos = platName.indexOf('-', pos);
339: if (pos != -1) {
340: hPos[hyphenCnt++] = pos;
341: pos++;
342: }
343: }
344: String sizeStr = Integer.toString((int) Math.abs(ptSize) * 10);
345: StringBuilder sb = new StringBuilder(platName);
346: /* work backwards so as to not invalidate the positions. */
347: sb.replace(hPos[11] + 1, hPos[12], "*");
348:
349: sb.replace(hPos[9] + 1, hPos[10], "72");
350:
351: sb.replace(hPos[8] + 1, hPos[9], "72");
352:
353: /* replace the 3 lines above with the next 3 lines to get the 1.4.2
354: * behaviour
355: */
356: // sb.replace(hPos[11]+1, hPos[12], "0");
357: // sb.replace(hPos[9]+1, hPos[10], "0");
358: // sb.replace(hPos[8]+1, hPos[9], "0");
359: sb.replace(hPos[7] + 1, hPos[8], sizeStr);
360:
361: sb.replace(hPos[6] + 1, hPos[7], "*");
362:
363: /* replace the 1 line above with the next line to get the 1.4.2
364: * behaviour
365: */
366: // sb.replace(hPos[6]+1, hPos[7], "0");
367: /* comment out this block to the the 1.4.2 behaviour */
368: if (hPos[0] == 0 && hPos[1] == 1) {
369: /* null foundry name : some linux font configuration files have
370: * symbol font entries like this and its just plain wrong.
371: * Replace with a wild card. (Although those fonts should be
372: * located via disk access rather than X11).
373: */
374: sb.replace(hPos[0] + 1, hPos[1], "*");
375: }
376:
377: String xlfd = sb.toString();
378: byte[] bytes = null;
379: try {
380: bytes = xlfd.getBytes("UTF-8");
381: } catch (UnsupportedEncodingException e) {
382: bytes = xlfd.getBytes();
383: }
384: return bytes;
385: }
386:
387: public String toString() {
388: return " ** Native Font: Family=" + familyName + " Name="
389: + fullName + " style=" + style + " nativeName="
390: + platName;
391: }
392: }
|