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 }