1 /*
   2  * Copyright 2003-2004 Sun Microsystems, Inc.  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.  Sun designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  22  * CA 95054 USA or visit www.sun.com if you need additional information or
  23  * have any 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       */
  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 
  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.getLogger()
 120                                    .severe("Could not create native strike " +
 121                                            new String(nameBytes));
 122              }
 123              return;
 124          }
 125          numGlyphs = nativeFont.getMapper().getNumGlyphs();
 126          this.disposer = new NativeStrikeDisposer(nativeFont, desc,
 127                                                   pScalerContext);
 128      }
 129 
 130      /* The asymmetry of the following methods is to help preserve
 131       * performance with minimal textual changes to the calling code
 132       * when moving initialisation of these arrays out of the constructor.
 133       * This may be restructured later when there's more room for changes
 134       */
 135      private boolean usingIntGlyphImages() {
 136          if (intGlyphImages != null) {
 137             return true;
 138         } else if (longAddresses) {
 139             return false;
 140         } else {
 141             /* We could obtain minGlyphIndex and index relative to that
 142              * if we need to save space.
 143              */
 144             int glyphLenArray = getMaxGlyph(pScalerContext);
 145 
 146             /* This shouldn't be necessary - its a precaution */
 147             if (glyphLenArray < numGlyphs) {
 148                 glyphLenArray = numGlyphs;
 149             }
 150             intGlyphImages = new int[glyphLenArray];
 151             this.disposer.intGlyphImages = intGlyphImages;
 152             return true;
 153         }
 154      }
 155 
 156      private long[] getLongGlyphImages() {
 157         if (longGlyphImages == null && longAddresses) {
 158 
 159             /* We could obtain minGlyphIndex and index relative to that
 160              * if we need to save space.
 161              */
 162             int glyphLenArray = getMaxGlyph(pScalerContext);
 163 
 164             /* This shouldn't be necessary - its a precaution */
 165             if (glyphLenArray < numGlyphs) {
 166                 glyphLenArray = numGlyphs;
 167             }
 168             longGlyphImages = new long[glyphLenArray];
 169             this.disposer.longGlyphImages = longGlyphImages;
 170         }
 171         return longGlyphImages;
 172      }
 173 
 174      NativeStrike(NativeFont nativeFont, FontStrikeDesc desc,
 175                   boolean nocache) {
 176          super(nativeFont, desc);
 177          this.nativeFont = nativeFont;
 178 
 179          int ptSize = (int)desc.glyphTx.getScaleY();
 180          double scale = desc.devTx.getScaleX(); // uniform scale
 181          byte [] nameBytes = nativeFont.getPlatformNameBytes(ptSize);
 182          pScalerContext = createScalerContext(nameBytes, ptSize, scale);
 183 
 184          int numGlyphs = nativeFont.getMapper().getNumGlyphs();
 185      }
 186 
 187      /* We want the native font to be responsible for reporting the
 188       * font metrics, even if it often delegates to another font.
 189       * The code here isn't yet implementing exactly that. If the glyph
 190       * transform was something native couldn't handle, there's no native
 191       * context from which to obtain metrics. Need to revise this to obtain
 192       * the metrics and transform them. But currently in such a case it
 193       * gets the metrics from a different font - its glyph delegate font.
 194       */
 195      StrikeMetrics getFontMetrics() {
 196          if (strikeMetrics == null) {
 197              if (pScalerContext != 0) {
 198                  strikeMetrics = nativeFont.getFontMetrics(pScalerContext);
 199              }
 200              if (strikeMetrics != null && fontTx != null) {
 201                  strikeMetrics.convertToUserSpace(fontTx);
 202              }
 203          }
 204          return strikeMetrics;
 205      }
 206 
 207      private native long createScalerContext(byte[] nameBytes,
 208                                              int ptSize, double scale);
 209 
 210      private native int getMaxGlyph(long pScalerContext);
 211 
 212      private native long createNullScalerContext();
 213 
 214      void getGlyphImagePtrs(int[] glyphCodes, long[] images,int  len) {
 215          for (int i=0; i<len; i++) {
 216              images[i] = getGlyphImagePtr(glyphCodes[i]);
 217          }
 218      }
 219 
 220      long getGlyphImagePtr(int glyphCode) {
 221          long glyphPtr;
 222 
 223          if (usingIntGlyphImages()) {
 224              if ((glyphPtr = intGlyphImages[glyphCode] & INTMASK) != 0L) {
 225                  return glyphPtr;
 226              } else {
 227                  glyphPtr = nativeFont.getGlyphImage(pScalerContext,glyphCode);
 228                  /* Synchronize in case some other thread has updated this
 229                   * cache entry already - unlikely but possible.
 230                   */
 231                  synchronized (this) {
 232                      if (intGlyphImages[glyphCode] == 0) {
 233                          intGlyphImages[glyphCode] = (int)glyphPtr;
 234                          return glyphPtr;
 235                      } else {
 236                          StrikeCache.freeIntPointer((int)glyphPtr);
 237                          return intGlyphImages[glyphCode] & INTMASK;
 238                      }
 239                  }
 240              }
 241          }
 242          /* must be using long (8 byte) addresses */
 243          else if ((glyphPtr = getLongGlyphImages()[glyphCode]) != 0L) {
 244              return glyphPtr;
 245          } else {
 246              glyphPtr = nativeFont.getGlyphImage(pScalerContext, glyphCode);
 247 
 248              synchronized (this) {
 249                  if (longGlyphImages[glyphCode] == 0L) {
 250                      longGlyphImages[glyphCode] = glyphPtr;
 251                      return glyphPtr;
 252                  } else {
 253                      StrikeCache.freeLongPointer(glyphPtr);
 254                      return longGlyphImages[glyphCode];
 255                  }
 256              }
 257          }
 258      }
 259 
 260      /* This is used when a FileFont uses the native names to create a
 261       * delegate NativeFont/Strike to get images from native. This is used
 262       * because Solaris TrueType fonts have external PCF bitmaps rather than
 263       * embedded bitmaps. This is really only important for CJK fonts as
 264       * for most scripts the external X11 bitmaps aren't much better - if
 265       * at all - than the results from hinting the outlines.
 266       */
 267      long getGlyphImagePtrNoCache(int glyphCode) {
 268          return nativeFont.getGlyphImageNoDefault(pScalerContext, glyphCode);
 269      }
 270 
 271      void getGlyphImageBounds(int glyphcode, Point2D.Float pt,
 272                               Rectangle result) {
 273      }
 274 
 275      Point2D.Float getGlyphMetrics(int glyphCode) {
 276          Point2D.Float pt = new Point2D.Float(getGlyphAdvance(glyphCode), 0f);
 277          return pt;
 278      }
 279 
 280      float getGlyphAdvance(int glyphCode) {
 281          return nativeFont.getGlyphAdvance(pScalerContext, glyphCode);
 282      }
 283 
 284      Rectangle2D.Float getGlyphOutlineBounds(int glyphCode) {
 285          return nativeFont.getGlyphOutlineBounds(pScalerContext, glyphCode);
 286      }
 287 
 288      GeneralPath getGlyphOutline(int glyphCode, float x, float y) {
 289          return new GeneralPath();
 290      }
 291 
 292      GeneralPath getGlyphVectorOutline(int[] glyphs, float x, float y) {
 293          return new GeneralPath();
 294      }
 295 
 296 }
 297 
 298 /* Returned instead of a NativeStrike.
 299  * It can intercept any request it wants, but mostly
 300  * passes them on to its delegate strike. It is important that
 301  * it override all the inherited FontStrike methods to delegate them
 302  * appropriately.
 303  */
 304 
 305 class DelegateStrike extends NativeStrike {
 306 
 307     private FontStrike delegateStrike;
 308 
 309     DelegateStrike(NativeFont nativeFont, FontStrikeDesc desc,
 310                    FontStrike delegate) {
 311         super(nativeFont, desc);
 312         this.delegateStrike = delegate;
 313     }
 314 
 315     /* We want the native font to be responsible for reporting the
 316      * font metrics, even if it often delegates to another font.
 317      * The code here isn't yet implementing exactly that. If the glyph
 318      * transform was something native couldn't handle, there's no native
 319      * context from which to obtain metrics. Need to revise this to obtain
 320      * the metrics and transform them. But currently in such a case it
 321      * gets the metrics from a different font - its glyph delegate font.
 322      */
 323    StrikeMetrics getFontMetrics() {
 324        if (strikeMetrics == null) {
 325            if (pScalerContext != 0) {
 326                strikeMetrics = super.getFontMetrics();
 327            }
 328             if (strikeMetrics == null) {
 329                 strikeMetrics = delegateStrike.getFontMetrics();
 330             }
 331         }
 332         return strikeMetrics;
 333     }
 334 
 335     void getGlyphImagePtrs(int[] glyphCodes, long[] images,int  len) {
 336         delegateStrike.getGlyphImagePtrs(glyphCodes, images, len);
 337     }
 338 
 339     long getGlyphImagePtr(int glyphCode) {
 340         return delegateStrike.getGlyphImagePtr(glyphCode);
 341     }
 342 
 343     void getGlyphImageBounds(int glyphCode,
 344                              Point2D.Float pt, Rectangle result) {
 345         delegateStrike.getGlyphImageBounds(glyphCode, pt, result);
 346     }
 347 
 348     Point2D.Float getGlyphMetrics(int glyphCode) {
 349         return delegateStrike.getGlyphMetrics(glyphCode);
 350     }
 351 
 352     float getGlyphAdvance(int glyphCode) {
 353         return delegateStrike.getGlyphAdvance(glyphCode);
 354     }
 355 
 356      Point2D.Float getCharMetrics(char ch) {
 357         return delegateStrike.getCharMetrics(ch);
 358     }
 359 
 360     float getCodePointAdvance(int cp) {
 361         if (cp < 0 || cp >= 0x10000) {
 362             cp = 0xffff;
 363         }
 364         return delegateStrike.getGlyphAdvance(cp);
 365     }
 366 
 367     Rectangle2D.Float getGlyphOutlineBounds(int glyphCode) {
 368         return delegateStrike.getGlyphOutlineBounds(glyphCode);
 369     }
 370 
 371     GeneralPath getGlyphOutline(int glyphCode, float x, float y) {
 372         return delegateStrike.getGlyphOutline(glyphCode, x, y);
 373     }
 374 
 375     GeneralPath getGlyphVectorOutline(int[] glyphs, float x, float y) {
 376         return delegateStrike.getGlyphVectorOutline(glyphs, x, y);
 377     }
 378 
 379 }