1 /*
   2  * Copyright (c) 2011, 2012, 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 sun.font;
  27 
  28 import java.awt.Font;
  29 import java.awt.font.FontRenderContext;
  30 import java.awt.geom.AffineTransform;
  31 import java.awt.geom.GeneralPath;;
  32 import java.awt.geom.Point2D;
  33 import java.awt.geom.Rectangle2D;
  34 import java.util.ArrayList;
  35 
  36 // Right now this class is final to avoid a problem with native code.
  37 // For some reason the JNI IsInstanceOf was not working correctly
  38 // so we are checking the class specifically. If we subclass this
  39 // we need to modify the native code in CFontWrapper.m
  40 public final class CFont extends PhysicalFont implements FontSubstitution {
  41 
  42     /* CFontStrike doesn't call these methods so they are unimplemented.
  43      * They are here to meet the requirements of PhysicalFont, needed
  44      * because a CFont can sometimes be returned where a PhysicalFont
  45      * is expected.
  46      */
  47     StrikeMetrics getFontMetrics(long pScalerContext) {
  48        throw new InternalError("Not implemented");
  49     }
  50 
  51     float getGlyphAdvance(long pScalerContext, int glyphCode) {
  52        throw new InternalError("Not implemented");
  53     }
  54 
  55     void getGlyphMetrics(long pScalerContext, int glyphCode,
  56                                   Point2D.Float metrics) {
  57        throw new InternalError("Not implemented");
  58     }
  59 
  60     long getGlyphImage(long pScalerContext, int glyphCode) {
  61        throw new InternalError("Not implemented");
  62     }
  63 
  64     Rectangle2D.Float getGlyphOutlineBounds(long pScalerContext,
  65                                                      int glyphCode) {
  66        throw new InternalError("Not implemented");
  67     }
  68 
  69     GeneralPath getGlyphOutline(long pScalerContext, int glyphCode,
  70                                          float x, float y) {
  71        throw new InternalError("Not implemented");
  72     }
  73 
  74     GeneralPath getGlyphVectorOutline(long pScalerContext,
  75                                                int[] glyphs, int numGlyphs,
  76                                                float x, float y) {
  77        throw new InternalError("Not implemented");
  78     }
  79 
  80     @Override
  81     protected long getLayoutTableCache() {
  82         return getLayoutTableCacheNative(getNativeFontPtr());
  83     }
  84 
  85     @Override
  86     protected byte[] getTableBytes(int tag) {
  87         return getTableBytesNative(getNativeFontPtr(), tag);
  88     }
  89 
  90     private native synchronized long getLayoutTableCacheNative(long nativeFontPtr);
  91 
  92     private native byte[] getTableBytesNative(long nativeFontPtr, int tag);
  93 
  94     private static native long createNativeFont(final String nativeFontName,
  95                                                 final int style);
  96     private static native void disposeNativeFont(final long nativeFontPtr);
  97 
  98     private boolean isFakeItalic;
  99     private String nativeFontName;
 100     private long nativeFontPtr;
 101 
 102     private native float getWidthNative(final long nativeFontPtr);
 103     private native float getWeightNative(final long nativeFontPtr);
 104 
 105     private int fontWidth = -1;
 106     private int fontWeight = -1;
 107 
 108     @Override
 109     public int getWidth() {
 110         if (fontWidth == -1) {
 111             // Apple use a range of -1 -> +1, where 0.0 is normal
 112             // OpenType uses a % range from 50% -> 200% where 100% is normal
 113             // and maps these onto the integer values 1->9.
 114             // Since that is what Font2D.getWidth() expects, remap to that.
 115             float fw = getWidthNative(getNativeFontPtr());
 116             if (fw == 0.0) { // short cut the common case
 117                 fontWidth = Font2D.FWIDTH_NORMAL;
 118                 return fontWidth;
 119             }
 120             fw += 1.0; fw *= 100.0;
 121             if (fw <= 50.0) {
 122                 fontWidth = 1;
 123             } else if (fw <= 62.5) {
 124                 fontWidth = 2;
 125             } else if (fw <= 75.0) {
 126                 fontWidth = 3;
 127             } else if (fw <= 87.5) {
 128                 fontWidth = 4;
 129             } else if (fw <= 100.0) {
 130                 fontWidth = 5;
 131             } else if (fw <= 112.5) {
 132                 fontWidth = 6;
 133             } else if (fw <= 125.0) {
 134                 fontWidth = 7;
 135             } else if (fw <= 150.0) {
 136                 fontWidth = 8;
 137             } else {
 138                 fontWidth = 9;
 139             }
 140         }
 141         return fontWidth;
 142    }
 143 
 144     @Override
 145     public int getWeight() {
 146         if (fontWeight == -1) {
 147             // Apple use a range of -1 -> +1, where 0 is medium/regular
 148             // Map this on to the OpenType range of 100->900 where
 149             // 500 is medium/regular.
 150             // We'll actually map to 0->1000 but that's close enough.
 151             float fw = getWeightNative(getNativeFontPtr());
 152             if (fw == 0) {
 153                return Font2D.FWEIGHT_NORMAL;
 154             }
 155             fw += 1.0; fw *= 500;
 156             fontWeight = (int)fw;
 157           }
 158           return fontWeight;
 159     }
 160 
 161     // this constructor is called from CFontWrapper.m
 162     public CFont(String name) {
 163         this(name, name);
 164     }
 165 
 166     public CFont(String name, String inFamilyName) {
 167         handle = new Font2DHandle(this);
 168         fullName = name;
 169         familyName = inFamilyName;
 170         nativeFontName = fullName;
 171         setStyle();
 172     }
 173 
 174     /* Called from CFontManager too */
 175     public CFont(CFont other, String logicalFamilyName) {
 176         handle = new Font2DHandle(this);
 177         fullName = logicalFamilyName;
 178         familyName = logicalFamilyName;
 179         nativeFontName = other.nativeFontName;
 180         style = other.style;
 181         isFakeItalic = other.isFakeItalic;
 182     }
 183 
 184     public CFont createItalicVariant() {
 185         CFont font = new CFont(this, familyName);
 186         font.nativeFontName = fullName;
 187         font.fullName =
 188             fullName + (style == Font.BOLD ? "" : "-") + "Italic-Derived";
 189         font.style |= Font.ITALIC;
 190         font.isFakeItalic = true;
 191         return font;
 192     }
 193 
 194     protected synchronized long getNativeFontPtr() {
 195         if (nativeFontPtr == 0L) {
 196             nativeFontPtr = createNativeFont(nativeFontName, style);
 197         }
 198         return nativeFontPtr;
 199     }
 200 
 201     private native long getCGFontPtrNative(long ptr);
 202 
 203     // This digs the CGFont out of the AWTFont.
 204     protected synchronized long getPlatformNativeFontPtr() {
 205         return getCGFontPtrNative(getNativeFontPtr());
 206     }
 207 
 208     static native void getCascadeList(long nativeFontPtr, ArrayList<String> listOfString);
 209 
 210     private CompositeFont createCompositeFont() {
 211         ArrayList<String> listOfString = new ArrayList<String>();
 212         getCascadeList(nativeFontPtr, listOfString);
 213 
 214         FontManager fm = FontManagerFactory.getInstance();
 215         int numFonts = 1 + listOfString.size();
 216         PhysicalFont[] fonts = new PhysicalFont[numFonts];
 217         fonts[0] = this;
 218         int idx = 1;
 219         for (String s : listOfString) {
 220             if (s.equals(".AppleSymbolsFB"))  {
 221                 // Don't know why we get the weird name above .. replace.
 222                 s = "AppleSymbols";
 223             }
 224             Font2D f2d = fm.findFont2D(s, Font.PLAIN, FontManager.NO_FALLBACK);
 225             if (f2d == null || f2d == this) {
 226                 continue;
 227             }
 228             fonts[idx++] = (PhysicalFont)f2d;
 229         }
 230         if (idx < fonts.length) {
 231             PhysicalFont[] orig = fonts;
 232             fonts = new PhysicalFont[idx];
 233             System.arraycopy(orig, 0, fonts, 0, idx);
 234         }
 235         CompositeFont compFont = new CompositeFont(fonts);
 236         compFont.mapper = new CCompositeGlyphMapper(compFont);
 237         return compFont;
 238     }
 239 
 240     private CompositeFont compFont;
 241 
 242     public CompositeFont getCompositeFont2D() {
 243         if (compFont == null) {
 244            compFont = createCompositeFont();
 245         }
 246         return compFont;
 247     }
 248 
 249     protected synchronized void finalize() {
 250         if (nativeFontPtr != 0) {
 251             disposeNativeFont(nativeFontPtr);
 252         }
 253         nativeFontPtr = 0;
 254     }
 255 
 256     protected CharToGlyphMapper getMapper() {
 257         if (mapper == null) {
 258             mapper = new CCharToGlyphMapper(this);
 259         }
 260         return mapper;
 261     }
 262 
 263     protected FontStrike createStrike(FontStrikeDesc desc) {
 264         if (isFakeItalic) {
 265             desc = new FontStrikeDesc(desc);
 266             desc.glyphTx.concatenate(AffineTransform.getShearInstance(-0.2, 0));
 267         }
 268         return new CStrike(this, desc);
 269     }
 270 
 271     // <rdar://problem/5321707> sun.font.Font2D caches the last used strike,
 272     // but does not check if the properties of the strike match the properties
 273     // of the incoming java.awt.Font object (size, style, etc).
 274     // Simple answer: don't cache.
 275     private static FontRenderContext DEFAULT_FRC =
 276         new FontRenderContext(null, false, false);
 277     public FontStrike getStrike(final Font font) {
 278         return getStrike(font, DEFAULT_FRC);
 279     }
 280 
 281     public String toString() {
 282         return "CFont { fullName: " + fullName +
 283             ",  familyName: " + familyName + ", style: " + style +
 284             " } aka: " + super.toString();
 285     }
 286 }