src/windows/classes/sun/awt/windows/WPathGraphics.java

Print this page
rev 1379 : [mq]: fontmanager.patch


  47 import java.awt.geom.Line2D;
  48 
  49 import java.awt.image.BufferedImage;
  50 import java.awt.image.ColorModel;
  51 import java.awt.image.DataBuffer;
  52 import java.awt.image.IndexColorModel;
  53 import java.awt.image.WritableRaster;
  54 import sun.awt.image.ByteComponentRaster;
  55 import sun.awt.image.BytePackedRaster;
  56 
  57 import java.awt.print.PageFormat;
  58 import java.awt.print.Printable;
  59 import java.awt.print.PrinterException;
  60 import java.awt.print.PrinterJob;
  61 
  62 import java.util.Arrays;
  63 
  64 import sun.font.CharToGlyphMapper;
  65 import sun.font.CompositeFont;
  66 import sun.font.Font2D;
  67 import sun.font.FontManager;
  68 import sun.font.PhysicalFont;
  69 import sun.font.TrueTypeFont;
  70 
  71 import sun.print.PathGraphics;
  72 import sun.print.ProxyGraphics2D;
  73 
  74 class WPathGraphics extends PathGraphics {
  75 
  76     /**
  77      * For a drawing application the initial user space
  78      * resolution is 72dpi.
  79      */
  80     private static final int DEFAULT_USER_RES = 72;
  81 
  82     private static final float MIN_DEVICE_LINEWIDTH = 1.2f;
  83     private static final float MAX_THINLINE_INCHES = 0.014f;
  84 
  85     /* Note that preferGDITextLayout implies useGDITextLayout.
  86      * "prefer" is used to override cases where would otherwise
  87      * choose not to use it. Note that non-layout factors may


 102                     useGDITextLayout = true;
 103                     preferGDITextLayout = true;
 104                 }
 105             }
 106         }
 107     }
 108 
 109     WPathGraphics(Graphics2D graphics, PrinterJob printerJob,
 110                   Printable painter, PageFormat pageFormat, int pageIndex,
 111                   boolean canRedraw) {
 112         super(graphics, printerJob, painter, pageFormat, pageIndex, canRedraw);
 113     }
 114 
 115     /**
 116      * Creates a new <code>Graphics</code> object that is
 117      * a copy of this <code>Graphics</code> object.
 118      * @return     a new graphics context that is a copy of
 119      *                       this graphics context.
 120      * @since      JDK1.0
 121      */

 122     public Graphics create() {
 123 
 124         return new WPathGraphics((Graphics2D) getDelegate().create(),
 125                                  getPrinterJob(),
 126                                  getPrintable(),
 127                                  getPageFormat(),
 128                                  getPageIndex(),
 129                                  canDoRedraws());
 130     }
 131 
 132     /**
 133      * Strokes the outline of a Shape using the settings of the current
 134      * graphics state.  The rendering attributes applied include the
 135      * clip, transform, paint or color, composite and stroke attributes.
 136      * @param s The shape to be drawn.
 137      * @see #setStroke
 138      * @see #setPaint
 139      * @see java.awt.Graphics#setColor
 140      * @see #transform
 141      * @see #setTransform
 142      * @see #clip
 143      * @see #setClip
 144      * @see #setComposite
 145      */

 146     public void draw(Shape s) {
 147 
 148         Stroke stroke = getStroke();
 149 
 150         /* If the line being drawn is thinner than can be
 151          * rendered, then change the line width, stroke
 152          * the shape, and then set the line width back.
 153          * We can only do this for BasicStroke's.
 154          */
 155         if (stroke instanceof BasicStroke) {
 156             BasicStroke lineStroke;
 157             BasicStroke minLineStroke = null;
 158             float deviceLineWidth;
 159             float lineWidth;
 160             AffineTransform deviceTransform;
 161             Point2D.Float penSize;
 162 
 163             /* Get the requested line width in user space.
 164              */
 165             lineStroke = (BasicStroke) stroke;


 233         /* The stroke in effect was not a BasicStroke so we
 234          * will not try to enforce a minimum line width.
 235          */
 236         } else {
 237             super.draw(s);
 238         }
 239     }
 240 
 241     /**
 242      * Draws the text given by the specified string, using this
 243      * graphics context's current font and color. The baseline of the
 244      * first character is at position (<i>x</i>,&nbsp;<i>y</i>) in this
 245      * graphics context's coordinate system.
 246      * @param       str      the string to be drawn.
 247      * @param       x        the <i>x</i> coordinate.
 248      * @param       y        the <i>y</i> coordinate.
 249      * @see         java.awt.Graphics#drawBytes
 250      * @see         java.awt.Graphics#drawChars
 251      * @since       JDK1.0
 252      */

 253     public void drawString(String str, int x, int y) {
 254         drawString(str, (float) x, (float) y);
 255     }
 256 

 257      public void drawString(String str, float x, float y) {
 258          drawString(str, x, y, getFont(), getFontRenderContext(), 0f);
 259      }
 260 
 261     /* A return value of 0 would mean font not available to GDI, or the
 262      * it can't be used for this string.
 263      * A return of 1 means it is suitable, including for composites.
 264      * We check that the transform in effect is doable with GDI, and that
 265      * this is a composite font AWT can handle, or a physical font GDI
 266      * can handle directly. Its possible that some strings may ultimately
 267      * fail the more stringent tests in drawString but this is rare and
 268      * also that method will always succeed, as if the font isn't available
 269      * it will use outlines via a superclass call. Also it is only called for
 270      * the default render context (as canDrawStringToWidth() will return
 271      * false. That is why it ignores the frc and width arguments.
 272      */

 273     protected int platformFontCount(Font font, String str) {
 274 
 275         AffineTransform deviceTransform = getTransform();
 276         AffineTransform fontTransform = new AffineTransform(deviceTransform);
 277         fontTransform.concatenate(getFont().getTransform());
 278         int transformType = fontTransform.getType();
 279 
 280         /* Test if GDI can handle the transform */
 281         boolean directToGDI = ((transformType !=
 282                                AffineTransform.TYPE_GENERAL_TRANSFORM)
 283                                && ((transformType & AffineTransform.TYPE_FLIP)
 284                                    == 0));
 285 
 286         if (!directToGDI) {
 287             return 0;
 288         }
 289 
 290         /* Since all windows fonts are available, and the JRE fonts
 291          * are also registered. Only the Font.createFont() case is presently
 292          * unknown to GDI. Those can be registered too, although that
 293          * code does not exist yet, it can be added too, so we should not
 294          * fail that case. Just do a quick check whether its a TrueTypeFont
 295          * - ie not a Type1 font etc, and let drawString() resolve the rest.
 296          */
 297         Font2D font2D = FontManager.getFont2D(font);
 298         if (font2D instanceof CompositeFont ||
 299             font2D instanceof TrueTypeFont) {
 300             return 1;
 301         } else {
 302             return 0;
 303         }
 304     }
 305 
 306     private static boolean isXP() {
 307         String osVersion = System.getProperty("os.version");
 308         if (osVersion != null) {
 309             Float version = Float.valueOf(osVersion);
 310             return (version.floatValue() >= 5.1f);
 311         } else {
 312             return false;
 313         }
 314     }
 315 
 316     /* In case GDI doesn't handle shaping or BIDI consistently with
 317      * 2D's TextLayout, we can detect these cases and redelegate up to
 318      * be drawn via TextLayout, which in is rendered as runs of
 319      * GlyphVectors, to which we can assign positions for each glyph.
 320      */
 321     private boolean strNeedsTextLayout(String str, Font font) {
 322         char[] chars = str.toCharArray();
 323         boolean isComplex = FontManager.isComplexText(chars, 0, chars.length);
 324         if (!isComplex) {
 325             return false;
 326         } else if (!useGDITextLayout) {
 327             return true;
 328         } else {
 329             if (preferGDITextLayout ||
 330                 (isXP() && FontManager.textLayoutIsCompatible(font))) {
 331                 return false;
 332             } else {
 333                 return true;
 334             }
 335         }
 336     }
 337 
 338     private int getAngle(Point2D.Double pt) {
 339         /* Get the rotation in 1/10'ths degree (as needed by Windows)
 340          * so that GDI can draw the text rotated.
 341          * This calculation is only valid for a uniform scale, no shearing.
 342          */
 343         double angle = Math.toDegrees(Math.atan2(pt.y, pt.x));
 344         if (angle < 0.0) {
 345             angle+= 360.0;
 346         }
 347         /* Windows specifies the rotation anti-clockwise from the x-axis
 348          * of the device, 2D specifies +ve rotation towards the y-axis
 349          * Since the 2D y-axis runs from top-to-bottom, windows angle of
 350          * rotation here is opposite than 2D's, so the rotation needed


 371      * using the current <code>Font</code> and <code>Paint</code> attributes
 372      * in the <code>Graphics2D</code> context.
 373      * The baseline of the first character is at position
 374      * (<i>x</i>,&nbsp;<i>y</i>) in the User Space.
 375      * The rendering attributes applied include the <code>Clip</code>,
 376      * <code>Transform</code>, <code>Paint</code>, <code>Font</code> and
 377      * <code>Composite</code> attributes. For characters in script systems
 378      * such as Hebrew and Arabic, the glyphs can be rendered from right to
 379      * left, in which case the coordinate supplied is the location of the
 380      * leftmost character on the baseline.
 381      * @param s the <code>String</code> to be rendered
 382      * @param x,&nbsp;y the coordinates where the <code>String</code>
 383      * should be rendered
 384      * @see #setPaint
 385      * @see java.awt.Graphics#setColor
 386      * @see java.awt.Graphics#setFont
 387      * @see #setTransform
 388      * @see #setComposite
 389      * @see #setClip
 390      */

 391     public void drawString(String str, float x, float y,
 392                            Font font, FontRenderContext frc, float targetW) {
 393         if (str.length() == 0) {
 394             return;
 395         }
 396 
 397         if (WPrinterJob.shapeTextProp) {
 398             super.drawString(str, x, y, font, frc, targetW);
 399             return;
 400         }
 401 
 402         /* If the Font has layout attributes we need to delegate to TextLayout.
 403          * TextLayout renders text as GlyphVectors. We try to print those
 404          * using printer fonts - ie using Postscript text operators so
 405          * we may be reinvoked. In that case the "!printingGlyphVector" test
 406          * prevents us recursing and instead sends us into the body of the
 407          * method where we can safely ignore layout attributes as those
 408          * are already handled by TextLayout.
 409          * Similarly if layout is needed based on the text, then we
 410          * delegate to TextLayout if possible, or failing that we delegate


 481          * Although we have already tested that there is no shear,
 482          * there may be a non-uniform scale, so the width of the font
 483          * does not scale equally with the height. That is handled
 484          * by specifying an 'average width' scale to GDI.
 485          */
 486         float fontSize = font.getSize2D();
 487 
 488         Point2D.Double pty = new Point2D.Double(0.0, 1.0);
 489         fontTransform.deltaTransform(pty, pty);
 490         double scaleFactorY = Math.sqrt(pty.x*pty.x+pty.y*pty.y);
 491         float scaledFontSizeY = (float)(fontSize * scaleFactorY);
 492 
 493         Point2D.Double ptx = new Point2D.Double(1.0, 0.0);
 494         fontTransform.deltaTransform(ptx, ptx);
 495         double scaleFactorX = Math.sqrt(ptx.x*ptx.x+ptx.y*ptx.y);
 496         float scaledFontSizeX = (float)(fontSize * scaleFactorX);
 497 
 498         float awScale = getAwScale(scaleFactorX, scaleFactorY);
 499         int iangle = getAngle(ptx);
 500 
 501         Font2D font2D = FontManager.getFont2D(font);
 502         if (font2D instanceof TrueTypeFont) {
 503             textOut(str, font, (TrueTypeFont)font2D, frc,
 504                     scaledFontSizeY, iangle, awScale,
 505                     deviceTransform, scaleFactorX,
 506                     x, y, devpos.x, devpos.y, targetW);
 507         } else if (font2D instanceof CompositeFont) {
 508             /* Composite fonts are made up of multiple fonts and each
 509              * substring that uses a particular component font needs to
 510              * be separately sent to GDI.
 511              * This works for standard composite fonts, alternate ones,
 512              * Fonts that are a physical font backed by a standard composite,
 513              * and with fallback fonts.
 514              */
 515             CompositeFont compFont = (CompositeFont)font2D;
 516             float userx = x, usery = y;
 517             float devx = devpos.x, devy = devpos.y;
 518             char[] chars = str.toCharArray();
 519             int len = chars.length;
 520             int[] glyphs = new int[len];
 521             compFont.getMapper().charsToGlyphs(len, chars, glyphs);


 532                 String substr = new String(chars, startChar,endChar-startChar);
 533                 PhysicalFont slotFont = compFont.getSlotFont(slot);
 534                 textOut(substr, font, slotFont, frc,
 535                         scaledFontSizeY, iangle, awScale,
 536                         deviceTransform, scaleFactorX,
 537                         userx, usery, devx, devy, 0f);
 538                 Rectangle2D bds = font.getStringBounds(substr, frc);
 539                 float xAdvance = (float)bds.getWidth();
 540                 userx += xAdvance;
 541                 userpos.x += xAdvance;
 542                 deviceTransform.transform(userpos, devpos);
 543             }
 544         } else {
 545             super.drawString(str, x, y, font, frc, targetW);
 546         }
 547     }
 548 
 549     /** return true if the Graphics instance can directly print
 550      * this glyphvector
 551      */

 552     protected boolean printGlyphVector(GlyphVector gv, float x, float y) {
 553         /* We don't want to try to handle per-glyph transforms. GDI can't
 554          * handle per-glyph rotations, etc. There's no way to express it
 555          * in a single call, so just bail for this uncommon case.
 556          */
 557         if ((gv.getLayoutFlags() & GlyphVector.FLAG_HAS_TRANSFORMS) != 0) {
 558             return false;
 559         }
 560 
 561         AffineTransform deviceTransform = getTransform();
 562         AffineTransform fontTransform = new AffineTransform(deviceTransform);
 563         Font font = gv.getFont();
 564         fontTransform.concatenate(font.getTransform());
 565         int transformType = fontTransform.getType();
 566 
 567         /* Use GDI for the text if the graphics transform is something
 568          * for which we can obtain a suitable GDI font.
 569          * A flip or shearing transform on the graphics or a transform
 570          * on the font force us to decompose the text into a shape.
 571          */


 676          * When we specify the advances, they are in device space, so
 677          * we don't want any further interpretation applied by GDI, but
 678          * since as noted the advances are interpreted in the HFONT's
 679          * coordinate space, our advances would be rotated again.
 680          * We don't have any way to tell GDI to rotate only the glyphs and
 681          * not the advances, so we need to account for this in the advances
 682          * we supply, by supplying unrotated advances.
 683          * Note that "iangle" is in the opposite direction to 2D's normal
 684          * direction of rotation, so this rotation inverts the
 685          * rotation element of the deviceTransform.
 686          */
 687         AffineTransform advanceTransform =
 688             new AffineTransform(deviceTransform);
 689         advanceTransform.rotate(iangle*Math.PI/1800.0);
 690         float[] glyphAdvPos = new float[glyphPos.length];
 691 
 692         advanceTransform.transform(glyphPos, 0,         //source
 693                                    glyphAdvPos, 0,      //destination
 694                                    glyphPos.length/2);  //num points
 695 
 696         Font2D font2D = FontManager.getFont2D(font);
 697         if (font2D instanceof TrueTypeFont) {
 698             String family = font2D.getFamilyName(null);
 699             int style = font.getStyle() | font2D.getStyle();
 700             if (!wPrinterJob.setFont(family, scaledFontSizeY, style,
 701                                      iangle, awScale)) {
 702                 return false;
 703             }
 704             wPrinterJob.glyphsOut(glyphCodes, devpos.x, devpos.y, glyphAdvPos);
 705 
 706         } else if (font2D instanceof CompositeFont) {
 707             /* Composite fonts are made up of multiple fonts and each
 708              * substring that uses a particular component font needs to
 709              * be separately sent to GDI.
 710              * This works for standard composite fonts, alternate ones,
 711              * Fonts that are a physical font backed by a standard composite,
 712              * and with fallback fonts.
 713              */
 714             CompositeFont compFont = (CompositeFont)font2D;
 715             float userx = x, usery = y;
 716             float devx = devpos.x, devy = devpos.y;


 775          boolean setFont = wPrinterJob.setFont(family, deviceSize, style,
 776                                                rotation, awScale);
 777          if (!setFont) {
 778              super.drawString(str, userx, usery, font, frc, targetW);
 779              return;
 780          }
 781 
 782          float[] glyphPos = null;
 783          if (!okGDIMetrics(str, font, frc, scaleFactorX)) {
 784              /* If there is a 1:1 char->glyph mapping then char positions
 785               * are the same as glyph positions and we can tell GDI
 786               * where to place the glyphs.
 787               * On drawing we remove control chars so these need to be
 788               * removed now so the string and positions are the same length.
 789               * For other cases we need to pass glyph codes to GDI.
 790               */
 791              str = wPrinterJob.removeControlChars(str);
 792              char[] chars = str.toCharArray();
 793              int len = chars.length;
 794              GlyphVector gv = null;
 795              if (!FontManager.isComplexText(chars, 0, len)) {
 796                  gv = font.createGlyphVector(frc, str);
 797              }
 798              if (gv == null) {
 799                  super.drawString(str, userx, usery, font, frc, targetW);
 800                  return;
 801              }
 802              glyphPos = gv.getGlyphPositions(0, len, null);
 803              Point2D gvAdvPt = gv.getGlyphPosition(gv.getNumGlyphs());
 804 
 805              /* GDI advances must not include device space rotation.
 806               * See earlier comment in printGlyphVector() for details.
 807               */
 808              AffineTransform advanceTransform =
 809                new AffineTransform(deviceTransform);
 810              advanceTransform.rotate(rotation*Math.PI/1800.0);
 811              float[] glyphAdvPos = new float[glyphPos.length];
 812 
 813              advanceTransform.transform(glyphPos, 0,         //source
 814                                         glyphAdvPos, 0,      //destination
 815                                         glyphPos.length/2);  //num points




  47 import java.awt.geom.Line2D;
  48 
  49 import java.awt.image.BufferedImage;
  50 import java.awt.image.ColorModel;
  51 import java.awt.image.DataBuffer;
  52 import java.awt.image.IndexColorModel;
  53 import java.awt.image.WritableRaster;
  54 import sun.awt.image.ByteComponentRaster;
  55 import sun.awt.image.BytePackedRaster;
  56 
  57 import java.awt.print.PageFormat;
  58 import java.awt.print.Printable;
  59 import java.awt.print.PrinterException;
  60 import java.awt.print.PrinterJob;
  61 
  62 import java.util.Arrays;
  63 
  64 import sun.font.CharToGlyphMapper;
  65 import sun.font.CompositeFont;
  66 import sun.font.Font2D;
  67 import sun.font.FontUtilities;
  68 import sun.font.PhysicalFont;
  69 import sun.font.TrueTypeFont;
  70 
  71 import sun.print.PathGraphics;
  72 import sun.print.ProxyGraphics2D;
  73 
  74 class WPathGraphics extends PathGraphics {
  75 
  76     /**
  77      * For a drawing application the initial user space
  78      * resolution is 72dpi.
  79      */
  80     private static final int DEFAULT_USER_RES = 72;
  81 
  82     private static final float MIN_DEVICE_LINEWIDTH = 1.2f;
  83     private static final float MAX_THINLINE_INCHES = 0.014f;
  84 
  85     /* Note that preferGDITextLayout implies useGDITextLayout.
  86      * "prefer" is used to override cases where would otherwise
  87      * choose not to use it. Note that non-layout factors may


 102                     useGDITextLayout = true;
 103                     preferGDITextLayout = true;
 104                 }
 105             }
 106         }
 107     }
 108 
 109     WPathGraphics(Graphics2D graphics, PrinterJob printerJob,
 110                   Printable painter, PageFormat pageFormat, int pageIndex,
 111                   boolean canRedraw) {
 112         super(graphics, printerJob, painter, pageFormat, pageIndex, canRedraw);
 113     }
 114 
 115     /**
 116      * Creates a new <code>Graphics</code> object that is
 117      * a copy of this <code>Graphics</code> object.
 118      * @return     a new graphics context that is a copy of
 119      *                       this graphics context.
 120      * @since      JDK1.0
 121      */
 122     @Override
 123     public Graphics create() {
 124 
 125         return new WPathGraphics((Graphics2D) getDelegate().create(),
 126                                  getPrinterJob(),
 127                                  getPrintable(),
 128                                  getPageFormat(),
 129                                  getPageIndex(),
 130                                  canDoRedraws());
 131     }
 132 
 133     /**
 134      * Strokes the outline of a Shape using the settings of the current
 135      * graphics state.  The rendering attributes applied include the
 136      * clip, transform, paint or color, composite and stroke attributes.
 137      * @param s The shape to be drawn.
 138      * @see #setStroke
 139      * @see #setPaint
 140      * @see java.awt.Graphics#setColor
 141      * @see #transform
 142      * @see #setTransform
 143      * @see #clip
 144      * @see #setClip
 145      * @see #setComposite
 146      */
 147     @Override
 148     public void draw(Shape s) {
 149 
 150         Stroke stroke = getStroke();
 151 
 152         /* If the line being drawn is thinner than can be
 153          * rendered, then change the line width, stroke
 154          * the shape, and then set the line width back.
 155          * We can only do this for BasicStroke's.
 156          */
 157         if (stroke instanceof BasicStroke) {
 158             BasicStroke lineStroke;
 159             BasicStroke minLineStroke = null;
 160             float deviceLineWidth;
 161             float lineWidth;
 162             AffineTransform deviceTransform;
 163             Point2D.Float penSize;
 164 
 165             /* Get the requested line width in user space.
 166              */
 167             lineStroke = (BasicStroke) stroke;


 235         /* The stroke in effect was not a BasicStroke so we
 236          * will not try to enforce a minimum line width.
 237          */
 238         } else {
 239             super.draw(s);
 240         }
 241     }
 242 
 243     /**
 244      * Draws the text given by the specified string, using this
 245      * graphics context's current font and color. The baseline of the
 246      * first character is at position (<i>x</i>,&nbsp;<i>y</i>) in this
 247      * graphics context's coordinate system.
 248      * @param       str      the string to be drawn.
 249      * @param       x        the <i>x</i> coordinate.
 250      * @param       y        the <i>y</i> coordinate.
 251      * @see         java.awt.Graphics#drawBytes
 252      * @see         java.awt.Graphics#drawChars
 253      * @since       JDK1.0
 254      */
 255     @Override
 256     public void drawString(String str, int x, int y) {
 257         drawString(str, (float) x, (float) y);
 258     }
 259 
 260     @Override
 261      public void drawString(String str, float x, float y) {
 262          drawString(str, x, y, getFont(), getFontRenderContext(), 0f);
 263      }
 264 
 265     /* A return value of 0 would mean font not available to GDI, or the
 266      * it can't be used for this string.
 267      * A return of 1 means it is suitable, including for composites.
 268      * We check that the transform in effect is doable with GDI, and that
 269      * this is a composite font AWT can handle, or a physical font GDI
 270      * can handle directly. Its possible that some strings may ultimately
 271      * fail the more stringent tests in drawString but this is rare and
 272      * also that method will always succeed, as if the font isn't available
 273      * it will use outlines via a superclass call. Also it is only called for
 274      * the default render context (as canDrawStringToWidth() will return
 275      * false. That is why it ignores the frc and width arguments.
 276      */
 277     @Override
 278     protected int platformFontCount(Font font, String str) {
 279 
 280         AffineTransform deviceTransform = getTransform();
 281         AffineTransform fontTransform = new AffineTransform(deviceTransform);
 282         fontTransform.concatenate(getFont().getTransform());
 283         int transformType = fontTransform.getType();
 284 
 285         /* Test if GDI can handle the transform */
 286         boolean directToGDI = ((transformType !=
 287                                AffineTransform.TYPE_GENERAL_TRANSFORM)
 288                                && ((transformType & AffineTransform.TYPE_FLIP)
 289                                    == 0));
 290 
 291         if (!directToGDI) {
 292             return 0;
 293         }
 294 
 295         /* Since all windows fonts are available, and the JRE fonts
 296          * are also registered. Only the Font.createFont() case is presently
 297          * unknown to GDI. Those can be registered too, although that
 298          * code does not exist yet, it can be added too, so we should not
 299          * fail that case. Just do a quick check whether its a TrueTypeFont
 300          * - ie not a Type1 font etc, and let drawString() resolve the rest.
 301          */
 302         Font2D font2D = FontUtilities.getFont2D(font);
 303         if (font2D instanceof CompositeFont ||
 304             font2D instanceof TrueTypeFont) {
 305             return 1;
 306         } else {
 307             return 0;
 308         }
 309     }
 310 
 311     private static boolean isXP() {
 312         String osVersion = System.getProperty("os.version");
 313         if (osVersion != null) {
 314             Float version = Float.valueOf(osVersion);
 315             return (version.floatValue() >= 5.1f);
 316         } else {
 317             return false;
 318         }
 319     }
 320 
 321     /* In case GDI doesn't handle shaping or BIDI consistently with
 322      * 2D's TextLayout, we can detect these cases and redelegate up to
 323      * be drawn via TextLayout, which in is rendered as runs of
 324      * GlyphVectors, to which we can assign positions for each glyph.
 325      */
 326     private boolean strNeedsTextLayout(String str, Font font) {
 327         char[] chars = str.toCharArray();
 328         boolean isComplex = FontUtilities.isComplexText(chars, 0, chars.length);
 329         if (!isComplex) {
 330             return false;
 331         } else if (!useGDITextLayout) {
 332             return true;
 333         } else {
 334             if (preferGDITextLayout ||
 335                 (isXP() && FontUtilities.textLayoutIsCompatible(font))) {
 336                 return false;
 337             } else {
 338                 return true;
 339             }
 340         }
 341     }
 342 
 343     private int getAngle(Point2D.Double pt) {
 344         /* Get the rotation in 1/10'ths degree (as needed by Windows)
 345          * so that GDI can draw the text rotated.
 346          * This calculation is only valid for a uniform scale, no shearing.
 347          */
 348         double angle = Math.toDegrees(Math.atan2(pt.y, pt.x));
 349         if (angle < 0.0) {
 350             angle+= 360.0;
 351         }
 352         /* Windows specifies the rotation anti-clockwise from the x-axis
 353          * of the device, 2D specifies +ve rotation towards the y-axis
 354          * Since the 2D y-axis runs from top-to-bottom, windows angle of
 355          * rotation here is opposite than 2D's, so the rotation needed


 376      * using the current <code>Font</code> and <code>Paint</code> attributes
 377      * in the <code>Graphics2D</code> context.
 378      * The baseline of the first character is at position
 379      * (<i>x</i>,&nbsp;<i>y</i>) in the User Space.
 380      * The rendering attributes applied include the <code>Clip</code>,
 381      * <code>Transform</code>, <code>Paint</code>, <code>Font</code> and
 382      * <code>Composite</code> attributes. For characters in script systems
 383      * such as Hebrew and Arabic, the glyphs can be rendered from right to
 384      * left, in which case the coordinate supplied is the location of the
 385      * leftmost character on the baseline.
 386      * @param s the <code>String</code> to be rendered
 387      * @param x,&nbsp;y the coordinates where the <code>String</code>
 388      * should be rendered
 389      * @see #setPaint
 390      * @see java.awt.Graphics#setColor
 391      * @see java.awt.Graphics#setFont
 392      * @see #setTransform
 393      * @see #setComposite
 394      * @see #setClip
 395      */
 396     @Override
 397     public void drawString(String str, float x, float y,
 398                            Font font, FontRenderContext frc, float targetW) {
 399         if (str.length() == 0) {
 400             return;
 401         }
 402 
 403         if (WPrinterJob.shapeTextProp) {
 404             super.drawString(str, x, y, font, frc, targetW);
 405             return;
 406         }
 407 
 408         /* If the Font has layout attributes we need to delegate to TextLayout.
 409          * TextLayout renders text as GlyphVectors. We try to print those
 410          * using printer fonts - ie using Postscript text operators so
 411          * we may be reinvoked. In that case the "!printingGlyphVector" test
 412          * prevents us recursing and instead sends us into the body of the
 413          * method where we can safely ignore layout attributes as those
 414          * are already handled by TextLayout.
 415          * Similarly if layout is needed based on the text, then we
 416          * delegate to TextLayout if possible, or failing that we delegate


 487          * Although we have already tested that there is no shear,
 488          * there may be a non-uniform scale, so the width of the font
 489          * does not scale equally with the height. That is handled
 490          * by specifying an 'average width' scale to GDI.
 491          */
 492         float fontSize = font.getSize2D();
 493 
 494         Point2D.Double pty = new Point2D.Double(0.0, 1.0);
 495         fontTransform.deltaTransform(pty, pty);
 496         double scaleFactorY = Math.sqrt(pty.x*pty.x+pty.y*pty.y);
 497         float scaledFontSizeY = (float)(fontSize * scaleFactorY);
 498 
 499         Point2D.Double ptx = new Point2D.Double(1.0, 0.0);
 500         fontTransform.deltaTransform(ptx, ptx);
 501         double scaleFactorX = Math.sqrt(ptx.x*ptx.x+ptx.y*ptx.y);
 502         float scaledFontSizeX = (float)(fontSize * scaleFactorX);
 503 
 504         float awScale = getAwScale(scaleFactorX, scaleFactorY);
 505         int iangle = getAngle(ptx);
 506 
 507         Font2D font2D = FontUtilities.getFont2D(font);
 508         if (font2D instanceof TrueTypeFont) {
 509             textOut(str, font, (TrueTypeFont)font2D, frc,
 510                     scaledFontSizeY, iangle, awScale,
 511                     deviceTransform, scaleFactorX,
 512                     x, y, devpos.x, devpos.y, targetW);
 513         } else if (font2D instanceof CompositeFont) {
 514             /* Composite fonts are made up of multiple fonts and each
 515              * substring that uses a particular component font needs to
 516              * be separately sent to GDI.
 517              * This works for standard composite fonts, alternate ones,
 518              * Fonts that are a physical font backed by a standard composite,
 519              * and with fallback fonts.
 520              */
 521             CompositeFont compFont = (CompositeFont)font2D;
 522             float userx = x, usery = y;
 523             float devx = devpos.x, devy = devpos.y;
 524             char[] chars = str.toCharArray();
 525             int len = chars.length;
 526             int[] glyphs = new int[len];
 527             compFont.getMapper().charsToGlyphs(len, chars, glyphs);


 538                 String substr = new String(chars, startChar,endChar-startChar);
 539                 PhysicalFont slotFont = compFont.getSlotFont(slot);
 540                 textOut(substr, font, slotFont, frc,
 541                         scaledFontSizeY, iangle, awScale,
 542                         deviceTransform, scaleFactorX,
 543                         userx, usery, devx, devy, 0f);
 544                 Rectangle2D bds = font.getStringBounds(substr, frc);
 545                 float xAdvance = (float)bds.getWidth();
 546                 userx += xAdvance;
 547                 userpos.x += xAdvance;
 548                 deviceTransform.transform(userpos, devpos);
 549             }
 550         } else {
 551             super.drawString(str, x, y, font, frc, targetW);
 552         }
 553     }
 554 
 555     /** return true if the Graphics instance can directly print
 556      * this glyphvector
 557      */
 558     @Override
 559     protected boolean printGlyphVector(GlyphVector gv, float x, float y) {
 560         /* We don't want to try to handle per-glyph transforms. GDI can't
 561          * handle per-glyph rotations, etc. There's no way to express it
 562          * in a single call, so just bail for this uncommon case.
 563          */
 564         if ((gv.getLayoutFlags() & GlyphVector.FLAG_HAS_TRANSFORMS) != 0) {
 565             return false;
 566         }
 567 
 568         AffineTransform deviceTransform = getTransform();
 569         AffineTransform fontTransform = new AffineTransform(deviceTransform);
 570         Font font = gv.getFont();
 571         fontTransform.concatenate(font.getTransform());
 572         int transformType = fontTransform.getType();
 573 
 574         /* Use GDI for the text if the graphics transform is something
 575          * for which we can obtain a suitable GDI font.
 576          * A flip or shearing transform on the graphics or a transform
 577          * on the font force us to decompose the text into a shape.
 578          */


 683          * When we specify the advances, they are in device space, so
 684          * we don't want any further interpretation applied by GDI, but
 685          * since as noted the advances are interpreted in the HFONT's
 686          * coordinate space, our advances would be rotated again.
 687          * We don't have any way to tell GDI to rotate only the glyphs and
 688          * not the advances, so we need to account for this in the advances
 689          * we supply, by supplying unrotated advances.
 690          * Note that "iangle" is in the opposite direction to 2D's normal
 691          * direction of rotation, so this rotation inverts the
 692          * rotation element of the deviceTransform.
 693          */
 694         AffineTransform advanceTransform =
 695             new AffineTransform(deviceTransform);
 696         advanceTransform.rotate(iangle*Math.PI/1800.0);
 697         float[] glyphAdvPos = new float[glyphPos.length];
 698 
 699         advanceTransform.transform(glyphPos, 0,         //source
 700                                    glyphAdvPos, 0,      //destination
 701                                    glyphPos.length/2);  //num points
 702 
 703         Font2D font2D = FontUtilities.getFont2D(font);
 704         if (font2D instanceof TrueTypeFont) {
 705             String family = font2D.getFamilyName(null);
 706             int style = font.getStyle() | font2D.getStyle();
 707             if (!wPrinterJob.setFont(family, scaledFontSizeY, style,
 708                                      iangle, awScale)) {
 709                 return false;
 710             }
 711             wPrinterJob.glyphsOut(glyphCodes, devpos.x, devpos.y, glyphAdvPos);
 712 
 713         } else if (font2D instanceof CompositeFont) {
 714             /* Composite fonts are made up of multiple fonts and each
 715              * substring that uses a particular component font needs to
 716              * be separately sent to GDI.
 717              * This works for standard composite fonts, alternate ones,
 718              * Fonts that are a physical font backed by a standard composite,
 719              * and with fallback fonts.
 720              */
 721             CompositeFont compFont = (CompositeFont)font2D;
 722             float userx = x, usery = y;
 723             float devx = devpos.x, devy = devpos.y;


 782          boolean setFont = wPrinterJob.setFont(family, deviceSize, style,
 783                                                rotation, awScale);
 784          if (!setFont) {
 785              super.drawString(str, userx, usery, font, frc, targetW);
 786              return;
 787          }
 788 
 789          float[] glyphPos = null;
 790          if (!okGDIMetrics(str, font, frc, scaleFactorX)) {
 791              /* If there is a 1:1 char->glyph mapping then char positions
 792               * are the same as glyph positions and we can tell GDI
 793               * where to place the glyphs.
 794               * On drawing we remove control chars so these need to be
 795               * removed now so the string and positions are the same length.
 796               * For other cases we need to pass glyph codes to GDI.
 797               */
 798              str = wPrinterJob.removeControlChars(str);
 799              char[] chars = str.toCharArray();
 800              int len = chars.length;
 801              GlyphVector gv = null;
 802              if (!FontUtilities.isComplexText(chars, 0, len)) {
 803                  gv = font.createGlyphVector(frc, str);
 804              }
 805              if (gv == null) {
 806                  super.drawString(str, userx, usery, font, frc, targetW);
 807                  return;
 808              }
 809              glyphPos = gv.getGlyphPositions(0, len, null);
 810              Point2D gvAdvPt = gv.getGlyphPosition(gv.getNumGlyphs());
 811 
 812              /* GDI advances must not include device space rotation.
 813               * See earlier comment in printGlyphVector() for details.
 814               */
 815              AffineTransform advanceTransform =
 816                new AffineTransform(deviceTransform);
 817              advanceTransform.rotate(rotation*Math.PI/1800.0);
 818              float[] glyphAdvPos = new float[glyphPos.length];
 819 
 820              advanceTransform.transform(glyphPos, 0,         //source
 821                                         glyphAdvPos, 0,      //destination
 822                                         glyphPos.length/2);  //num points