1 /*
   2  * Copyright (c) 2013, 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.directwrite;
  27 
  28 import com.sun.javafx.font.Disposer;
  29 import com.sun.javafx.font.FontStrikeDesc;
  30 import com.sun.javafx.font.PrismFontFactory;
  31 import com.sun.javafx.font.PrismFontFile;
  32 import com.sun.javafx.font.PrismFontStrike;
  33 import com.sun.javafx.geom.Path2D;
  34 import com.sun.javafx.geom.RectBounds;
  35 import com.sun.javafx.geom.transform.BaseTransform;
  36 
  37 class DWFontFile extends PrismFontFile {
  38     private IDWriteFontFace fontFace;
  39     private DWDisposer disposer;
  40 
  41     DWFontFile(String name, String filename, int fIndex, boolean register,
  42                boolean embedded, boolean copy, boolean tracked) throws Exception {
  43         super(name, filename, fIndex, register, embedded, copy, tracked);
  44         fontFace = createFontFace();
  45 
  46         if (PrismFontFactory.debugFonts) {
  47             if (fontFace == null) {
  48                 System.err.println("Failed to create IDWriteFontFace for " + this);
  49             }
  50         }
  51 
  52         if (copy) {
  53             disposer = new DWDisposer(fontFace);
  54             Disposer.addRecord(this, disposer);
  55         }
  56     }
  57 
  58     private IDWriteFontFace createEmbeddedFontFace() {
  59         IDWriteFactory factory = DWFactory.getDWriteFactory();
  60         IDWriteFontFile fontFile = factory.CreateFontFileReference(getFileName());
  61         if (fontFile == null) return null;
  62         boolean[] isSupportedFontType = new boolean[1];
  63         int[] fontFileType = new int[1];
  64         int[] fontFaceType = new int[1];
  65         int[] numberOfFaces = new int[1];
  66         int hr = fontFile.Analyze(isSupportedFontType, fontFileType, fontFaceType, numberOfFaces);
  67         IDWriteFontFace face = null;
  68         if (hr == OS.S_OK && isSupportedFontType[0]) {
  69             int faceIndex = getFontIndex();
  70             int simulation = OS.DWRITE_FONT_SIMULATIONS_NONE;
  71             face = factory.CreateFontFace(fontFaceType[0], fontFile, faceIndex, simulation);
  72         }
  73         fontFile.Release();
  74         return face;
  75     }
  76 
  77     private IDWriteFontFace createFontFace() {
  78         if (isEmbeddedFont()) {
  79             return createEmbeddedFontFace();
  80         }
  81 
  82         IDWriteFontCollection collection = DWFactory.getFontCollection();
  83         int index = collection.FindFamilyName(getFamilyName());
  84         if (index == -1) {
  85             /* This can happen when the family name reported by GDI does not
  86              * match family name in DirectWrite. For example, GDI reports
  87              * 'Arial Black' as family name while DirectWrite represents the
  88              * same font using family equals to 'Arial' and style equals to
  89              * DWRITE_FONT_WEIGHT_BLACK. The fix to try to create the font
  90              * using the font file.
  91              */
  92             return createEmbeddedFontFace();
  93         }
  94 
  95         IDWriteFontFamily family = collection.GetFontFamily(index);
  96         if (family == null) return null;
  97         int weight = isBold() ? OS.DWRITE_FONT_WEIGHT_BOLD :
  98                                 OS.DWRITE_FONT_WEIGHT_NORMAL;
  99         int stretch = OS.DWRITE_FONT_STRETCH_NORMAL;
 100         int style = isItalic() ? OS.DWRITE_FONT_STYLE_ITALIC :
 101                                  OS.DWRITE_FONT_STYLE_NORMAL;
 102         IDWriteFont font = family.GetFirstMatchingFont(weight, stretch, style);
 103         family.Release();
 104         if (font == null) return null;
 105         IDWriteFontFace face = font.CreateFontFace();
 106         font.Release();
 107         return face;
 108     }
 109 
 110     IDWriteFontFace getFontFace() {
 111         return fontFace;
 112     }
 113 
 114     Path2D getGlyphOutline(int gc, float size) {
 115         if (fontFace == null) return null;
 116         if (size == 0) return new Path2D();
 117         return fontFace.GetGlyphRunOutline(size, (short)gc, false);
 118     }
 119 
 120     RectBounds getBBox(int glyphCode, float size) {
 121         /* In coretext this is the bounds for the path of the glyph */
 122         float[] bb = new float[4];
 123         getGlyphBoundingBox(glyphCode, size, bb);
 124         return new RectBounds(bb[0], bb[1], bb[2], bb[3]);
 125     }
 126 
 127     @Override protected int[] createGlyphBoundingBox(int gc) {
 128         if (fontFace == null) return null;
 129         DWRITE_GLYPH_METRICS metrics = fontFace.GetDesignGlyphMetrics((short)gc, false);
 130         if (metrics == null) return null;
 131         int[] bb = new int[4];
 132         bb[0] = metrics.leftSideBearing;
 133         bb[1] = metrics.verticalOriginY - metrics.advanceHeight + metrics.bottomSideBearing;
 134         bb[2] = metrics.advanceWidth - metrics.rightSideBearing;
 135         bb[3] = metrics.verticalOriginY - metrics.topSideBearing;
 136         return bb;
 137     }
 138 
 139     @Override
 140     protected PrismFontStrike<DWFontFile> createStrike(float size, BaseTransform transform,
 141                                                        int aaMode, FontStrikeDesc desc) {
 142         return new DWFontStrike(this, size, transform, aaMode, desc);
 143     }
 144 
 145     /* This is called only for fonts where a temp file was created
 146      */
 147     @Override
 148     protected synchronized void disposeOnShutdown() {
 149         if (fontFace != null) {
 150             //If this isn't done, Windows won't delete the file, since
 151             // DW will have it open.
 152             if (disposer != null) {
 153                 disposer.dispose();
 154             } else {
 155                 fontFace.Release();
 156                 if (PrismFontFactory.debugFonts) {
 157                     System.err.println("null disposer for " + fontFace);
 158                 }
 159             }
 160             if (PrismFontFactory.debugFonts) {
 161                 System.err.println("fontFace freed: " + fontFace);
 162             }
 163             fontFace = null;
 164         }
 165         super.disposeOnShutdown(); // deletes the file.
 166     }
 167 }