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