1 /*
2  * Copyright (c) 2011, 2017, 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      */
getFontMetrics(long pScalerContext)47     StrikeMetrics getFontMetrics(long pScalerContext) {
48        throw new InternalError("Not implemented");
49     }
50 
getGlyphAdvance(long pScalerContext, int glyphCode)51     float getGlyphAdvance(long pScalerContext, int glyphCode) {
52        throw new InternalError("Not implemented");
53     }
54 
getGlyphMetrics(long pScalerContext, int glyphCode, Point2D.Float metrics)55     void getGlyphMetrics(long pScalerContext, int glyphCode,
56                                   Point2D.Float metrics) {
57        throw new InternalError("Not implemented");
58     }
59 
getGlyphImage(long pScalerContext, int glyphCode)60     long getGlyphImage(long pScalerContext, int glyphCode) {
61        throw new InternalError("Not implemented");
62     }
63 
getGlyphOutlineBounds(long pScalerContext, int glyphCode)64     Rectangle2D.Float getGlyphOutlineBounds(long pScalerContext,
65                                                      int glyphCode) {
66        throw new InternalError("Not implemented");
67     }
68 
getGlyphOutline(long pScalerContext, int glyphCode, float x, float y)69     GeneralPath getGlyphOutline(long pScalerContext, int glyphCode,
70                                          float x, float y) {
71        throw new InternalError("Not implemented");
72     }
73 
getGlyphVectorOutline(long pScalerContext, int[] glyphs, int numGlyphs, float x, float y)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
getLayoutTableCache()81     protected long getLayoutTableCache() {
82         return getLayoutTableCacheNative(getNativeFontPtr());
83     }
84 
85     @Override
getTableBytes(int tag)86     protected byte[] getTableBytes(int tag) {
87         return getTableBytesNative(getNativeFontPtr(), tag);
88     }
89 
getLayoutTableCacheNative(long nativeFontPtr)90     private native synchronized long getLayoutTableCacheNative(long nativeFontPtr);
91 
getTableBytesNative(long nativeFontPtr, int tag)92     private native byte[] getTableBytesNative(long nativeFontPtr, int tag);
93 
createNativeFont(final String nativeFontName, final int style)94     private static native long createNativeFont(final String nativeFontName,
95                                                 final int style);
disposeNativeFont(final long nativeFontPtr)96     private static native void disposeNativeFont(final long nativeFontPtr);
97 
98     private boolean isFakeItalic;
99     private String nativeFontName;
100     private long nativeFontPtr;
101 
getWidthNative(final long nativeFontPtr)102     private native float getWidthNative(final long nativeFontPtr);
getWeightNative(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
getWidth()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
getWeight()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
CFont(String name)162     public CFont(String name) {
163         this(name, name);
164     }
165 
CFont(String name, String inFamilyName)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 */
CFont(CFont other, String logicalFamilyName)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 
createItalicVariant()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 
getNativeFontPtr()194     protected synchronized long getNativeFontPtr() {
195         if (nativeFontPtr == 0L) {
196             nativeFontPtr = createNativeFont(nativeFontName, style);
197         }
198         return nativeFontPtr;
199     }
200 
getCascadeList(long nativeFontPtr, ArrayList<String> listOfString)201     static native void getCascadeList(long nativeFontPtr, ArrayList<String> listOfString);
202 
createCompositeFont()203     private CompositeFont createCompositeFont() {
204         ArrayList<String> listOfString = new ArrayList<String>();
205         getCascadeList(nativeFontPtr, listOfString);
206 
207         // add JRE "Lucida Sans Regular" to the cascade list to enable fallback
208         // to happen to this JRE font in case the intended glyph is missing in
209         // fonts provided in the CoreText provided cascaded list
210         listOfString.add("Lucida Sans Regular");
211         FontManager fm = FontManagerFactory.getInstance();
212         int numFonts = 1 + listOfString.size();
213         PhysicalFont[] fonts = new PhysicalFont[numFonts];
214         fonts[0] = this;
215         int idx = 1;
216         for (String s : listOfString) {
217             if (s.equals(".AppleSymbolsFB"))  {
218                 // Don't know why we get the weird name above .. replace.
219                 s = "AppleSymbols";
220             }
221             Font2D f2d = fm.findFont2D(s, Font.PLAIN, FontManager.NO_FALLBACK);
222             if (f2d == null || f2d == this) {
223                 continue;
224             }
225             fonts[idx++] = (PhysicalFont)f2d;
226         }
227         if (idx < fonts.length) {
228             PhysicalFont[] orig = fonts;
229             fonts = new PhysicalFont[idx];
230             System.arraycopy(orig, 0, fonts, 0, idx);
231         }
232         CompositeFont compFont = new CompositeFont(fonts);
233         compFont.mapper = new CCompositeGlyphMapper(compFont);
234         return compFont;
235     }
236 
237     private CompositeFont compFont;
238 
getCompositeFont2D()239     public CompositeFont getCompositeFont2D() {
240         if (compFont == null) {
241            compFont = createCompositeFont();
242         }
243         return compFont;
244     }
245 
finalize()246     protected synchronized void finalize() {
247         if (nativeFontPtr != 0) {
248             disposeNativeFont(nativeFontPtr);
249         }
250         nativeFontPtr = 0;
251     }
252 
getMapper()253     protected CharToGlyphMapper getMapper() {
254         if (mapper == null) {
255             mapper = new CCharToGlyphMapper(this);
256         }
257         return mapper;
258     }
259 
createStrike(FontStrikeDesc desc)260     protected FontStrike createStrike(FontStrikeDesc desc) {
261         if (isFakeItalic) {
262             desc = new FontStrikeDesc(desc);
263             desc.glyphTx.concatenate(AffineTransform.getShearInstance(-0.2, 0));
264         }
265         return new CStrike(this, desc);
266     }
267 
268     // <rdar://problem/5321707> sun.font.Font2D caches the last used strike,
269     // but does not check if the properties of the strike match the properties
270     // of the incoming java.awt.Font object (size, style, etc).
271     // Simple answer: don't cache.
272     private static FontRenderContext DEFAULT_FRC =
273         new FontRenderContext(null, false, false);
getStrike(final Font font)274     public FontStrike getStrike(final Font font) {
275         return getStrike(font, DEFAULT_FRC);
276     }
277 
toString()278     public String toString() {
279         return "CFont { fullName: " + fullName +
280             ",  familyName: " + familyName + ", style: " + style +
281             " } aka: " + super.toString();
282     }
283 }
284