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); 83 } catch (Exception e) { 84 } 85 } 86 } 87 embeddedFontsLoaded = true; 88 } 89 } 90 91 @Override public Font loadFont(InputStream in, double size) { 92 FontFactory factory = getFontFactoryFromPipeline(); 93 PGFont font = factory.loadEmbeddedFont(null, in, (float)size, true); 94 if (font != null) return createFont(font); 95 return null; 96 } 97 98 @Override public Font loadFont(String path, double size) { 99 FontFactory factory = getFontFactoryFromPipeline(); 100 PGFont font = factory.loadEmbeddedFont(null, path, (float)size, true); 101 if (font != null) return createFont(font); 102 return null; 103 } 104 105 @SuppressWarnings("deprecation") 106 private Font createFont(PGFont font) { 107 return FontHelper.nativeFont(font, 108 font.getName(), 109 font.getFamilyName(), 110 font.getStyleName(), 111 font.getSize()); 112 } 113 114 /** 115 * Gets all the font families installed on the user's system, including any 116 * embedded fonts or SDK fonts. 117 * 118 * @profile common 119 */ 120 @Override public List<String> getFamilies() { 121 loadEmbeddedFonts(); 122 return Arrays.asList(getFontFactoryFromPipeline(). 123 getFontFamilyNames()); 124 } 125 126 /** 127 * Gets the names of all fonts that are installed on the users system, 128 * including any embedded fonts and SDK fonts. 129 * 130 * @profile common 131 */ 132 @Override public List<String> getFontNames() { 133 loadEmbeddedFonts(); 134 return Arrays.asList(getFontFactoryFromPipeline().getFontFullNames()); 135 } 136 137 /** 138 * Gets the names of all fonts in the specified font family that are 139 * installed on the users system, including any embedded fonts and 140 * SDK fonts. 141 * 142 * @profile common 143 */ 144 @Override public List<String> getFontNames(String family) { 145 loadEmbeddedFonts(); 146 return Arrays.asList(getFontFactoryFromPipeline(). 147 getFontFullNames(family)); 148 } 149 150 /** 151 * Searches for an appropriate font based on the font family name and 152 * weight and posture style. This method is not guaranteed to return 153 * a specific font, but does its best to find one that fits the 154 * specified requirements. 155 * 156 * For SDK/runtime fonts, we will attempt to match properties to a 157 * SDK/runtime fonts. If a specific SDK font is not found in the runtime 158 * JAR, the font loading will revert to FontFactory default font, rather 159 * then finding closest matching available SDK font. This is how SDK font 160 * loading was handled in the past. 161 * 162 * @param family The family of the font 163 * @param weight The weight of the font 164 * @param posture The posture or posture of the font 165 * @param size The point size of the font. This can be a fractional value 166 * 167 * @profile desktop 168 */ 169 @Override public Font font(String family, FontWeight weight, 170 FontPosture posture, float size) { 171 172 FontFactory fontFactory = getFontFactoryFromPipeline(); 173 if (!embeddedFontsLoaded && !fontFactory.isPlatformFont(family)) { 174 loadEmbeddedFonts(); 175 } 176 177 // REMIND. Some day need to have better granularity. 178 179 boolean bold = weight != null && 180 weight.ordinal() >= FontWeight.BOLD.ordinal(); 181 boolean italic = posture == FontPosture.ITALIC; 182 PGFont prismFont = fontFactory.createFont(family, bold, italic, size); 183 184 // Create Font and set implementation 185 Font fxFont = FontHelper.nativeFont(prismFont, prismFont.getName(), 186 prismFont.getFamilyName(), 187 prismFont.getStyleName(), size); 188 return fxFont; 189 } 190 191 /** 192 * @param font 193 */ 194 @Override public void loadFont(Font font) { 195 FontFactory fontFactory = getFontFactoryFromPipeline(); 196 String fullName = font.getName(); 197 if (!embeddedFontsLoaded && !fontFactory.isPlatformFont(fullName)) { 198 loadEmbeddedFonts(); 199 } 200 201 // find the native Prism Font object based on this JavaFX font. At the 202 // conclusion of this method, be sure to set the name, family, and 203 // style on the Font object via the setNativeFont method. 204 205 // the Prism font we're trying to find 206 PGFont prismFont = fontFactory.createFont(fullName, (float)font.getSize()); 207 208 // update the name variable to match what was actually loaded 209 String name = prismFont.getName(); 210 String family = prismFont.getFamilyName(); 211 String style = prismFont.getStyleName(); 212 FontHelper.setNativeFont(font, prismFont, name, family, style); 213 } 214 215 @Override public FontMetrics getFontMetrics(Font font) { 216 if (font != null) { 217 PGFont prismFont = (PGFont) FontHelper.getNativeFont(font); 218 Metrics metrics = PrismFontUtils.getFontMetrics(prismFont); 219 // TODO: what's the difference between ascent and maxAscent? 220 float maxAscent = -metrics.getAscent();//metrics.getMaxAscent(); 221 float ascent = -metrics.getAscent(); 222 float xheight = metrics.getXHeight(); 223 float descent = metrics.getDescent(); 224 // TODO: what's the difference between descent and maxDescent? 225 float maxDescent = metrics.getDescent();//metrics.getMaxDescent(); 226 float leading = metrics.getLineGap(); 227 return FontMetrics.impl_createFontMetrics(maxAscent, ascent, xheight, descent, maxDescent, leading, font); 228 } else { 229 return null; // this should never happen 230 } 231 } 232 233 @Override public float getCharWidth(char ch, Font font) { 234 PGFont prismFont = (PGFont) FontHelper.getNativeFont(font); 235 return (float)PrismFontUtils.getCharWidth(prismFont, ch); 236 } 237 238 @Override public float getSystemFontSize() { 239 // PrismFontFactory is what loads the DLL, so we may as 240 // well place the required native method there. 241 return PrismFontFactory.getSystemFontSize(); 242 } 243 244 FontFactory installedFontFactory = null; 245 private FontFactory getFontFactoryFromPipeline() { 246 if (installedFontFactory != null) { 247 return installedFontFactory; 248 } 249 try { 250 Class plc = Class.forName("com.sun.prism.GraphicsPipeline"); 251 Method gpm = plc.getMethod("getPipeline", (Class[])null); 252 Object plo = gpm.invoke(null); 253 Method gfm = plc.getMethod("getFontFactory", (Class[])null); 254 Object ffo = gfm.invoke(plo); 255 installedFontFactory = (FontFactory)ffo; 256 } catch (Exception e) { 257 } 258 return installedFontFactory; 259 } 260 }