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 }