1 /* 2 * Copyright (c) 2011, 2016, 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 com.sun.javafx.font; 27 28 import com.sun.javafx.scene.text.FontHelper; 29 import javafx.scene.text.*; 30 import com.sun.javafx.tk.*; 31 import java.lang.reflect.Method; 32 import java.net.URL; 33 import java.io.InputStream; 34 import java.util.Arrays; 35 import java.util.Enumeration; 36 import java.util.List; 37 import java.util.Properties; 38 39 public class PrismFontLoader extends FontLoader { 40 private static PrismFontLoader theInstance = new PrismFontLoader(); 41 public static PrismFontLoader getInstance() { return theInstance; } 42 43 /** 44 * Flag to keep track whether the fontCache map has been initialized with 45 * the embedded fonts. 46 */ 47 private boolean embeddedFontsLoaded = false; 48 49 Properties loadEmbeddedFontDefinitions() { 50 Properties map = new Properties(); 51 // locate the META-INF directory and search for a fonts.mf 52 // located there 53 ClassLoader loader = Thread.currentThread().getContextClassLoader(); 54 URL u = loader.getResource("META-INF/fonts.mf"); 55 if (u == null) return map; 56 57 // read in the contents of the file 58 try (InputStream in = u.openStream()) { 59 map.load(in); 60 } catch (Exception e) { 61 e.printStackTrace(); 62 } 63 return map; 64 } 65 66 private void loadEmbeddedFonts() { 67 if (!embeddedFontsLoaded) { 68 FontFactory fontFactory = getFontFactoryFromPipeline(); 69 if (!fontFactory.hasPermission()) { 70 embeddedFontsLoaded = true; 71 return; 72 } 73 Properties map = loadEmbeddedFontDefinitions(); 74 Enumeration<?> names = map.keys(); 75 ClassLoader loader = Thread.currentThread().getContextClassLoader(); 76 while (names.hasMoreElements()) { 77 String n = (String)names.nextElement(); 78 String p = map.getProperty(n); 79 if (p.startsWith("/")) { 80 p = p.substring(1); 81 try (InputStream in = loader.getResourceAsStream(p)) { 82 fontFactory.loadEmbeddedFont(n, in, 0, true, false); 83 } catch (Exception e) { 84 } 85 } 86 } 87 embeddedFontsLoaded = true; 88 } 89 } 90 91 private Font[] createFonts(PGFont[] fonts) { 92 if (fonts == null || fonts.length == 0) { 93 return null; 94 } 95 Font[] fxFonts = new Font[fonts.length]; 96 for (int i=0; i<fonts.length; i++) { 97 fxFonts[i] = createFont(fonts[i]); 98 } 99 return fxFonts; 100 } 101 102 @Override public Font[] loadFont(InputStream in, 103 double size, 104 boolean loadAll) { 105 106 FontFactory factory = getFontFactoryFromPipeline(); 107 PGFont[] fonts = 108 factory.loadEmbeddedFont(null, in, (float)size, true, loadAll); 109 return createFonts(fonts); 110 } 111 112 @Override public Font[] loadFont(String path, 113 double size, 114 boolean loadAll) { 115 116 FontFactory factory = getFontFactoryFromPipeline(); 117 PGFont[] fonts = 118 factory.loadEmbeddedFont(null, path, (float)size, true, loadAll); 119 return createFonts(fonts); 120 } 121 122 @SuppressWarnings("deprecation") 123 private Font createFont(PGFont font) { 124 return FontHelper.nativeFont(font, 125 font.getName(), 126 font.getFamilyName(), 127 font.getStyleName(), 128 font.getSize()); 129 } 130 131 /** 132 * Gets all the font families installed on the user's system, including any 133 * embedded fonts or SDK fonts. 134 * 135 * @profile common 136 */ 137 @Override public List<String> getFamilies() { 138 loadEmbeddedFonts(); 139 return Arrays.asList(getFontFactoryFromPipeline(). 140 getFontFamilyNames()); 141 } 142 143 /** 144 * Gets the names of all fonts that are installed on the users system, 145 * including any embedded fonts and SDK fonts. 146 * 147 * @profile common 148 */ 149 @Override public List<String> getFontNames() { 150 loadEmbeddedFonts(); 151 return Arrays.asList(getFontFactoryFromPipeline().getFontFullNames()); 152 } 153 154 /** 155 * Gets the names of all fonts in the specified font family that are 156 * installed on the users system, including any embedded fonts and 157 * SDK fonts. 158 * 159 * @profile common 160 */ 161 @Override public List<String> getFontNames(String family) { 162 loadEmbeddedFonts(); 163 return Arrays.asList(getFontFactoryFromPipeline(). 164 getFontFullNames(family)); 165 } 166 167 /** 168 * Searches for an appropriate font based on the font family name and 169 * weight and posture style. This method is not guaranteed to return 170 * a specific font, but does its best to find one that fits the 171 * specified requirements. 172 * 173 * For SDK/runtime fonts, we will attempt to match properties to a 174 * SDK/runtime fonts. If a specific SDK font is not found in the runtime 175 * JAR, the font loading will revert to FontFactory default font, rather 176 * then finding closest matching available SDK font. This is how SDK font 177 * loading was handled in the past. 178 * 179 * @param family The family of the font 180 * @param weight The weight of the font 181 * @param posture The posture or posture of the font 182 * @param size The point size of the font. This can be a fractional value 183 * 184 * @profile desktop 185 */ 186 @Override public Font font(String family, FontWeight weight, 187 FontPosture posture, float size) { 188 189 FontFactory fontFactory = getFontFactoryFromPipeline(); 190 if (!embeddedFontsLoaded && !fontFactory.isPlatformFont(family)) { 191 loadEmbeddedFonts(); 192 } 193 194 // REMIND. Some day need to have better granularity. 195 196 boolean bold = weight != null && 197 weight.ordinal() >= FontWeight.BOLD.ordinal(); 198 boolean italic = posture == FontPosture.ITALIC; 199 PGFont prismFont = fontFactory.createFont(family, bold, italic, size); 200 201 // Create Font and set implementation 202 Font fxFont = FontHelper.nativeFont(prismFont, prismFont.getName(), 203 prismFont.getFamilyName(), 204 prismFont.getStyleName(), size); 205 return fxFont; 206 } 207 208 /** 209 * @param font 210 */ 211 @Override public void loadFont(Font font) { 212 FontFactory fontFactory = getFontFactoryFromPipeline(); 213 String fullName = font.getName(); 214 if (!embeddedFontsLoaded && !fontFactory.isPlatformFont(fullName)) { 215 loadEmbeddedFonts(); 216 } 217 218 // find the native Prism Font object based on this JavaFX font. At the 219 // conclusion of this method, be sure to set the name, family, and 220 // style on the Font object via the setNativeFont method. 221 222 // the Prism font we're trying to find 223 PGFont prismFont = fontFactory.createFont(fullName, (float)font.getSize()); 224 225 // update the name variable to match what was actually loaded 226 String name = prismFont.getName(); 227 String family = prismFont.getFamilyName(); 228 String style = prismFont.getStyleName(); 229 FontHelper.setNativeFont(font, prismFont, name, family, style); 230 } 231 232 @Override public FontMetrics getFontMetrics(Font font) { 233 if (font != null) { 234 PGFont prismFont = (PGFont) FontHelper.getNativeFont(font); 235 Metrics metrics = PrismFontUtils.getFontMetrics(prismFont); 236 // TODO: what's the difference between ascent and maxAscent? 237 float maxAscent = -metrics.getAscent();//metrics.getMaxAscent(); 238 float ascent = -metrics.getAscent(); 239 float xheight = metrics.getXHeight(); 240 float descent = metrics.getDescent(); 241 // TODO: what's the difference between descent and maxDescent? 242 float maxDescent = metrics.getDescent();//metrics.getMaxDescent(); 243 float leading = metrics.getLineGap(); 244 return FontMetrics.impl_createFontMetrics(maxAscent, ascent, xheight, descent, maxDescent, leading, font); 245 } else { 246 return null; // this should never happen 247 } 248 } 249 250 @Override public float getCharWidth(char ch, Font font) { 251 PGFont prismFont = (PGFont) FontHelper.getNativeFont(font); 252 return (float)PrismFontUtils.getCharWidth(prismFont, ch); 253 } 254 255 @Override public float getSystemFontSize() { 256 // PrismFontFactory is what loads the DLL, so we may as 257 // well place the required native method there. 258 return PrismFontFactory.getSystemFontSize(); 259 } 260 261 FontFactory installedFontFactory = null; 262 private FontFactory getFontFactoryFromPipeline() { 263 if (installedFontFactory != null) { 264 return installedFontFactory; 265 } 266 try { 267 Class plc = Class.forName("com.sun.prism.GraphicsPipeline"); 268 Method gpm = plc.getMethod("getPipeline", (Class[])null); 269 Object plo = gpm.invoke(null); 270 Method gfm = plc.getMethod("getFontFactory", (Class[])null); 271 Object ffo = gfm.invoke(plo); 272 installedFontFactory = (FontFactory)ffo; 273 } catch (Exception e) { 274 } 275 return installedFontFactory; 276 } 277 }