1 /*
2  * Copyright (c) 2003, 2020, 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.geom.AffineTransform;
29 import java.awt.geom.GeneralPath;
30 import java.awt.geom.Point2D;
31 import java.awt.Rectangle;
32 import java.awt.geom.Rectangle2D;
33 import java.awt.geom.NoninvertibleTransformException;
34 
35 class NativeStrike extends PhysicalStrike {
36 
37      NativeFont nativeFont;
38      int numGlyphs;
39      AffineTransform invertDevTx;
40      AffineTransform fontTx;
41 
42      /* The following method prepares data used in obtaining FontMetrics.
43       * This is the one case in which we allow anything other than a
44       * simple scale to be used with a native font. We do this because in
45       * order to ensure that clients get the overall metrics they expect
46       * for a font whatever coordinate system (combination of font and
47       * device transform) they use.
48       * X11 fonts can only have a scale applied (remind : non-uniform?)
49       * We strip out everything else and if necessary obtain an inverse
50       * tx which we use to return metrics for the font in the transformed
51       * coordinate system of the font. ie we pass X11 a simple scale, and
52       * then apply the non-scale part of the font TX to that result.
53       */
getNativePointSize()54      private int getNativePointSize() {
55          /* Make a copy of the glyphTX in which we will store the
56           * font transform, inverting the devTx if necessary
57           */
58          double[] mat = new double[4];
59          desc.glyphTx.getMatrix(mat);
60          fontTx = new AffineTransform(mat);
61 
62          /* Now work backwards to get the font transform */
63          if (!desc.devTx.isIdentity() &&
64              desc.devTx.getType() != AffineTransform.TYPE_TRANSLATION) {
65              try {
66                  invertDevTx = desc.devTx.createInverse();
67                  fontTx.concatenate(invertDevTx);
68              } catch (NoninvertibleTransformException e) {
69                  e.printStackTrace();
70              }
71          }
72 
73          /* At this point the fontTx may be a simple +ve scale, or it
74           * may be something more complex.
75           */
76          Point2D.Float pt = new Point2D.Float(1f,1f);
77          fontTx.deltaTransform(pt, pt);
78          double ptSize = Math.abs(pt.y);
79          int ttype = fontTx.getType();
80          if ((ttype & ~AffineTransform.TYPE_UNIFORM_SCALE) != 0 ||
81              fontTx.getScaleY() <= 0) {
82              /* We need to create an inverse transform that doesn't
83               * include the point size (strictly the uniform scale)
84               */
85              fontTx.scale(1/ptSize, 1/ptSize);
86          } else {
87              fontTx = null; // no need
88          }
89          return (int)ptSize;
90      }
91 
NativeStrike(NativeFont nativeFont, FontStrikeDesc desc)92      NativeStrike(NativeFont nativeFont, FontStrikeDesc desc) {
93          super(nativeFont, desc);
94          this.nativeFont = nativeFont;
95 
96 
97          /* If this is a delegate for bitmaps, we expect to have
98           * been invoked only for a simple scale. If that's not
99           * true, just bail
100           */
101          if (nativeFont.isBitmapDelegate) {
102              int ttype = desc.glyphTx.getType();
103              if ((ttype & ~AffineTransform.TYPE_UNIFORM_SCALE) != 0 ||
104                  desc.glyphTx.getScaleX() <= 0) {
105              numGlyphs = 0;
106              return;
107              }
108          }
109 
110          int ptSize = getNativePointSize();
111          byte [] nameBytes = nativeFont.getPlatformNameBytes(ptSize);
112          double scale = Math.abs(desc.devTx.getScaleX());
113          pScalerContext = createScalerContext(nameBytes, ptSize, scale);
114          if (pScalerContext == 0L) {
115              SunFontManager.getInstance().deRegisterBadFont(nativeFont);
116              pScalerContext = createNullScalerContext();
117              numGlyphs = 0;
118              if (FontUtilities.isLogging()) {
119                  FontUtilities.logSevere("Could not create native strike " +
120                                          new String(nameBytes));
121              }
122              return;
123          }
124          numGlyphs = nativeFont.getMapper().getNumGlyphs();
125          this.disposer = new NativeStrikeDisposer(nativeFont, desc,
126                                                   pScalerContext);
127      }
128 
129      /* The asymmetry of the following methods is to help preserve
130       * performance with minimal textual changes to the calling code
131       * when moving initialisation of these arrays out of the constructor.
132       * This may be restructured later when there's more room for changes
133       */
usingIntGlyphImages()134      private boolean usingIntGlyphImages() {
135          if (intGlyphImages != null) {
136             return true;
137         } else if (longAddresses) {
138             return false;
139         } else {
140             /* We could obtain minGlyphIndex and index relative to that
141              * if we need to save space.
142              */
143             int glyphLenArray = getMaxGlyph(pScalerContext);
144 
145             /* This shouldn't be necessary - its a precaution */
146             if (glyphLenArray < numGlyphs) {
147                 glyphLenArray = numGlyphs;
148             }
149             intGlyphImages = new int[glyphLenArray];
150             this.disposer.intGlyphImages = intGlyphImages;
151             return true;
152         }
153      }
154 
getLongGlyphImages()155      private long[] getLongGlyphImages() {
156         if (longGlyphImages == null && longAddresses) {
157 
158             /* We could obtain minGlyphIndex and index relative to that
159              * if we need to save space.
160              */
161             int glyphLenArray = getMaxGlyph(pScalerContext);
162 
163             /* This shouldn't be necessary - its a precaution */
164             if (glyphLenArray < numGlyphs) {
165                 glyphLenArray = numGlyphs;
166             }
167             longGlyphImages = new long[glyphLenArray];
168             this.disposer.longGlyphImages = longGlyphImages;
169         }
170         return longGlyphImages;
171      }
172 
NativeStrike(NativeFont nativeFont, FontStrikeDesc desc, boolean nocache)173      NativeStrike(NativeFont nativeFont, FontStrikeDesc desc,
174                   boolean nocache) {
175          super(nativeFont, desc);
176          this.nativeFont = nativeFont;
177 
178          int ptSize = (int)desc.glyphTx.getScaleY();
179          double scale = desc.devTx.getScaleX(); // uniform scale
180          byte [] nameBytes = nativeFont.getPlatformNameBytes(ptSize);
181          pScalerContext = createScalerContext(nameBytes, ptSize, scale);
182 
183          int numGlyphs = nativeFont.getMapper().getNumGlyphs();
184      }
185 
186      /* We want the native font to be responsible for reporting the
187       * font metrics, even if it often delegates to another font.
188       * The code here isn't yet implementing exactly that. If the glyph
189       * transform was something native couldn't handle, there's no native
190       * context from which to obtain metrics. Need to revise this to obtain
191       * the metrics and transform them. But currently in such a case it
192       * gets the metrics from a different font - its glyph delegate font.
193       */
getFontMetrics()194      StrikeMetrics getFontMetrics() {
195          if (strikeMetrics == null) {
196              if (pScalerContext != 0) {
197                  strikeMetrics = nativeFont.getFontMetrics(pScalerContext);
198              }
199              if (strikeMetrics != null && fontTx != null) {
200                  strikeMetrics.convertToUserSpace(fontTx);
201              }
202          }
203          return strikeMetrics;
204      }
205 
createScalerContext(byte[] nameBytes, int ptSize, double scale)206      private native long createScalerContext(byte[] nameBytes,
207                                              int ptSize, double scale);
208 
getMaxGlyph(long pScalerContext)209      private native int getMaxGlyph(long pScalerContext);
210 
createNullScalerContext()211      private native long createNullScalerContext();
212 
getGlyphImagePtrs(int[] glyphCodes, long[] images,int len)213      void getGlyphImagePtrs(int[] glyphCodes, long[] images,int  len) {
214          for (int i=0; i<len; i++) {
215              images[i] = getGlyphImagePtr(glyphCodes[i]);
216          }
217      }
218 
getGlyphImagePtr(int glyphCode)219      long getGlyphImagePtr(int glyphCode) {
220          long glyphPtr;
221 
222          if (usingIntGlyphImages()) {
223              if ((glyphPtr = intGlyphImages[glyphCode] & INTMASK) != 0L) {
224                  return glyphPtr;
225              } else {
226                  glyphPtr = nativeFont.getGlyphImage(pScalerContext,glyphCode);
227                  /* Synchronize in case some other thread has updated this
228                   * cache entry already - unlikely but possible.
229                   */
230                  synchronized (this) {
231                      if (intGlyphImages[glyphCode] == 0) {
232                          intGlyphImages[glyphCode] = (int)glyphPtr;
233                          return glyphPtr;
234                      } else {
235                          StrikeCache.freeIntPointer((int)glyphPtr);
236                          return intGlyphImages[glyphCode] & INTMASK;
237                      }
238                  }
239              }
240          }
241          /* must be using long (8 byte) addresses */
242          else if ((glyphPtr = getLongGlyphImages()[glyphCode]) != 0L) {
243              return glyphPtr;
244          } else {
245              glyphPtr = nativeFont.getGlyphImage(pScalerContext, glyphCode);
246 
247              synchronized (this) {
248                  if (longGlyphImages[glyphCode] == 0L) {
249                      longGlyphImages[glyphCode] = glyphPtr;
250                      return glyphPtr;
251                  } else {
252                      StrikeCache.freeLongPointer(glyphPtr);
253                      return longGlyphImages[glyphCode];
254                  }
255              }
256          }
257      }
258 
259      /* This is used when a FileFont uses the native names to create a
260       * delegate NativeFont/Strike to get images from native. This is used
261       * because Solaris TrueType fonts have external PCF bitmaps rather than
262       * embedded bitmaps. This is really only important for CJK fonts as
263       * for most scripts the external X11 bitmaps aren't much better - if
264       * at all - than the results from hinting the outlines.
265       */
getGlyphImagePtrNoCache(int glyphCode)266      long getGlyphImagePtrNoCache(int glyphCode) {
267          return nativeFont.getGlyphImageNoDefault(pScalerContext, glyphCode);
268      }
269 
getGlyphImageBounds(int glyphcode, Point2D.Float pt, Rectangle result)270      void getGlyphImageBounds(int glyphcode, Point2D.Float pt,
271                               Rectangle result) {
272      }
273 
getGlyphMetrics(int glyphCode)274      Point2D.Float getGlyphMetrics(int glyphCode) {
275          Point2D.Float pt = new Point2D.Float(getGlyphAdvance(glyphCode), 0f);
276          return pt;
277      }
278 
getGlyphAdvance(int glyphCode)279      float getGlyphAdvance(int glyphCode) {
280          return nativeFont.getGlyphAdvance(pScalerContext, glyphCode);
281      }
282 
getGlyphOutlineBounds(int glyphCode)283      Rectangle2D.Float getGlyphOutlineBounds(int glyphCode) {
284          return nativeFont.getGlyphOutlineBounds(pScalerContext, glyphCode);
285      }
286 
getGlyphOutline(int glyphCode, float x, float y)287      GeneralPath getGlyphOutline(int glyphCode, float x, float y) {
288          return new GeneralPath();
289      }
290 
getGlyphVectorOutline(int[] glyphs, float x, float y)291      GeneralPath getGlyphVectorOutline(int[] glyphs, float x, float y) {
292          return new GeneralPath();
293      }
294 
295 }
296