1 /*
2 * Copyright (c) 2003, 2014, 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
26 package sun.font;
27
28 import java.awt.Font;
29 import java.awt.FontFormatException;
30 import java.awt.GraphicsEnvironment;
31 import java.awt.font.FontRenderContext;
32 import java.awt.geom.GeneralPath;
33 import java.awt.geom.Point2D;
34 import java.awt.geom.Rectangle2D;
35 import java.io.UnsupportedEncodingException;
36 import java.lang.ref.WeakReference;
37 import java.util.Locale;
38
39 /*
40 * Ideally there would be no native fonts used, and this class would be
41 * unneeded and removed. Presently it is still needed until such time
42 * as font configuration files (or the implementation equivalent) can have
43 * all references to fonts that are not handled via Java 2D removed.
44 * Currently there are two cases where this class is needed, both on
45 * Unix, primarily Solaris, but useful on Linux too if fonts have moved.
46 * 1. Some legacy F3 fonts are still referenced so that AWT "X/Motif"
47 * can get dingbats and symbols from them. This can be dispensed with when
48 * either AWT is based on 2D, or when the X font path is known to always
49 * contain a Type1 or TrueType font that can be used in font configuration
50 * files to replace the F3 fonts.
51 * 2. When location of font files by 2D fails, because of some system
52 * configuration problem, it is desirable to have a fall back to some
53 * functionality that lessens the immediate impact on users. Being able
54 * to perform limited operations by using bitmaps from X11 helps here.
55 */
56
57 public class NativeFont extends PhysicalFont {
58
59 String encoding;
60
61 private int numGlyphs = -1;
62 boolean isBitmapDelegate;
63 PhysicalFont delegateFont;
64
65 /**
66 * Verifies native font is accessible.
67 * @throws FontFormatException - if the font can't be located.
68 */
69 public NativeFont(String platName, boolean bitmapDelegate)
70 throws FontFormatException {
71 super(platName, null);
72
73 /* This is set true if this is an instance of a NativeFont
74 * created by some other font, to get native bitmaps.
75 * The delegating font will call this font only for "basic"
76 * cases - ie non-rotated, uniform scale, monochrome bitmaps.
77 * If this is false, then this instance may need to itself
78 * delegate to another font for non-basic cases. Since
79 * NativeFonts are used in that way only for symbol and dingbats
80 * we know its safe to delegate these to the JRE's default
81 * physical font (Lucida Sans Regular).
82 */
83 isBitmapDelegate = bitmapDelegate;
84
85 if (GraphicsEnvironment.isHeadless()) {
86 throw new FontFormatException("Native font in headless toolkit");
87 }
88 fontRank = Font2D.NATIVE_RANK;
89 initNames();
90 if (getNumGlyphs() == 0) {
91 throw new FontFormatException("Couldn't locate font" + platName);
92 }
93 }
94
95 private void initNames() throws FontFormatException {
96 /* Valid XLFD has exactly 14 "-" chars.
97 * First run over the string to verify have at least this many
98 * At the same time record the locations of the hyphens
99 * so we can just pick the right substring later on
100 */
101 int[] hPos = new int[14];
102 int hyphenCnt = 1;
103 int pos = 1;
104
105 String xlfd = platName.toLowerCase(Locale.ENGLISH);
106 if (xlfd.startsWith("-")) {
107 while (pos != -1 && hyphenCnt < 14) {
108 pos = xlfd.indexOf('-', pos);
109 if (pos != -1) {
110 hPos[hyphenCnt++] = pos;
111 pos++;
112 }
113 }
114 }
115
116 if (hyphenCnt == 14 && pos != -1) {
117
118 /* Capitalise words in the Family name */
119 String tmpFamily = xlfd.substring(hPos[1]+1, hPos[2]);
120 StringBuilder sBuffer = new StringBuilder(tmpFamily);
121 char ch = Character.toUpperCase(sBuffer.charAt(0));
122 sBuffer.replace(0, 1, String.valueOf(ch));
123 for (int i=1;i<sBuffer.length()-1; i++) {
124 if (sBuffer.charAt(i) == ' ') {
125 ch = Character.toUpperCase(sBuffer.charAt(i+1));
126 sBuffer.replace(i+1, i+2, String.valueOf(ch));
127 }
128 }
129 familyName = sBuffer.toString();
130
131 String tmpWeight = xlfd.substring(hPos[2]+1, hPos[3]);
132 String tmpSlant = xlfd.substring(hPos[3]+1, hPos[4]);
133
134 String styleStr = null;
135
136 if (tmpWeight.indexOf("bold") >= 0 ||
137 tmpWeight.indexOf("demi") >= 0) {
138 style |= Font.BOLD;
139 styleStr = "Bold";
140 }
141
142 if (tmpSlant.equals("i") ||
143 tmpSlant.indexOf("italic") >= 0) {
144 style |= Font.ITALIC;
145
146 if (styleStr == null) {
147 styleStr = "Italic";
148 } else {
149 styleStr = styleStr + " Italic";
150 }
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 String xlfd = sb.toString();
216 byte[] bytes = null;
217 try {
218 bytes = xlfd.getBytes("UTF-8");
219 } catch (UnsupportedEncodingException e) {
220 bytes = xlfd.getBytes();
221 }
222 return haveBitmapFonts(bytes);
223 }
224
225 public static boolean fontExists(String xlfd) {
226 byte[] bytes = null;
227 try {
228 bytes = xlfd.getBytes("UTF-8");
229 } catch (UnsupportedEncodingException e) {
230 bytes = xlfd.getBytes();
231 }
232 return fontExists(bytes);
233 }
234
235 private static native boolean haveBitmapFonts(byte[] xlfd);
236 private static native boolean fontExists(byte[] xlfd);
237
238 public CharToGlyphMapper getMapper() {
239 if (mapper == null) {
240 if (isBitmapDelegate) {
241 /* we are a delegate */
242 mapper = new NativeGlyphMapper(this);
243 } else {
244 /* we need to delegate */
245 SunFontManager fm = SunFontManager.getInstance();
246 delegateFont = fm.getDefaultPhysicalFont();
247 mapper = delegateFont.getMapper();
248 }
249 }
250 return mapper;
251 }
252
253 FontStrike createStrike(FontStrikeDesc desc) {
254 if (isBitmapDelegate) {
255 return new NativeStrike(this, desc);
256 } else {
257 if (delegateFont == null) {
258 SunFontManager fm = SunFontManager.getInstance();
259 delegateFont = fm.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,
287 float x,
288 float y) {
289 return null;
290 }
291
292 native long getGlyphImage(long pScalerContext, int glyphCode);
293
294 native long getGlyphImageNoDefault(long pScalerContext, int glyphCode);
295
296 void getGlyphMetrics(long pScalerContext, int glyphCode,
297 Point2D.Float metrics) {
298 throw new RuntimeException("this should be called on the strike");
299 }
300
301 public GeneralPath getGlyphVectorOutline(long pScalerContext,
302 int[] glyphs, int numGlyphs,
303 float x, float y) {
304 return null;
305 }
306
307 private native int countGlyphs(byte[] platformNameBytes, int ptSize);
308
309 public int getNumGlyphs() {
310 if (numGlyphs == -1) {
311 byte[] bytes = getPlatformNameBytes(8);
312 numGlyphs = countGlyphs(bytes, 8);
313 }
314 return numGlyphs;
315 }
316
317 PhysicalFont getDelegateFont() {
318 if (delegateFont == null) {
319 SunFontManager fm = SunFontManager.getInstance();
320 delegateFont = fm.getDefaultPhysicalFont();
321 }
322 return delegateFont;
323 }
324
325 /* Specify that the dpi is 72x72, as this corresponds to JDK's
326 * default user space. These are the 10th and 11th fields in the XLFD.
327 * ptSize in XLFD is in 10th's of a point so multiply by 10,
328 * Replace the 9th field in the XLFD (ie after the 8th hyphen)
329 * with this pt size (this corresponds to the field that's "%d" in the
330 * font configuration files). Wild card the other numeric fields.
331 * ie to request 12 pt Times New Roman italic font, use an XLFD like :
332 * -monotype-times new roman-regular-i---*-120-72-72-p-*-iso8859-1
333 */
334 @SuppressWarnings("cast")
335 byte[] getPlatformNameBytes(int ptSize) {
336 int[] hPos = new int[14];
337 int hyphenCnt = 1;
338 int pos = 1;
339
340 while (pos != -1 && hyphenCnt < 14) {
341 pos = platName.indexOf('-', pos);
342 if (pos != -1) {
343 hPos[hyphenCnt++] = pos;
344 pos++;
345 }
346 }
347 String sizeStr = Integer.toString((int)Math.abs(ptSize)*10);
348 StringBuilder sb = new StringBuilder(platName);
349 /* work backwards so as to not invalidate the positions. */
350 sb.replace(hPos[11]+1, hPos[12], "*");
351
352 sb.replace(hPos[9]+1, hPos[10], "72");
353
354 sb.replace(hPos[8]+1, hPos[9], "72");
355
356 /* replace the 3 lines above with the next 3 lines to get the 1.4.2
357 * behaviour
358 */
359 // sb.replace(hPos[11]+1, hPos[12], "0");
360 // sb.replace(hPos[9]+1, hPos[10], "0");
361 // sb.replace(hPos[8]+1, hPos[9], "0");
362
363 sb.replace(hPos[7]+1, hPos[8], sizeStr);
364
365 sb.replace(hPos[6]+1, hPos[7], "*");
366
367 /* replace the 1 line above with the next line to get the 1.4.2
368 * behaviour
369 */
370 // sb.replace(hPos[6]+1, hPos[7], "0");
371
372 /* comment out this block to the 1.4.2 behaviour */
373 if (hPos[0] == 0 && hPos[1] == 1) {
374 /* null foundry name : some linux font configuration files have
375 * symbol font entries like this and its just plain wrong.
376 * Replace with a wild card. (Although those fonts should be
377 * located via disk access rather than X11).
378 */
379 sb.replace(hPos[0]+1, hPos[1], "*");
380 }
381
382 String xlfd = sb.toString();
383 byte[] bytes = null;
384 try {
385 bytes = xlfd.getBytes("UTF-8");
386 } catch (UnsupportedEncodingException e) {
387 bytes = xlfd.getBytes();
388 }
389 return bytes;
390 }
391
392 public String toString() {
393 return " ** Native Font: Family="+familyName+ " Name="+fullName+
394 " style="+style+" nativeName="+platName;
395 }
396 }
--- EOF ---