jdk/src/share/classes/sun/print/PSPathGraphics.java
changeset 2 90ce3da70b43
child 553 38a9503f374d
equal deleted inserted replaced
0:fd16c54261b3 2:90ce3da70b43
       
     1 /*
       
     2  * Copyright 1998-2006 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.print;
       
    27 
       
    28 import java.awt.Color;
       
    29 import java.awt.Font;
       
    30 import java.awt.Graphics;
       
    31 import java.awt.Graphics2D;
       
    32 import java.awt.Image;
       
    33 import java.awt.Paint;
       
    34 import java.awt.Shape;
       
    35 import java.awt.Transparency;
       
    36 
       
    37 import java.awt.font.FontRenderContext;
       
    38 import java.awt.font.TextLayout;
       
    39 
       
    40 import java.awt.geom.AffineTransform;
       
    41 import java.awt.geom.Area;
       
    42 import java.awt.geom.PathIterator;
       
    43 import java.awt.geom.Point2D;
       
    44 import java.awt.geom.Rectangle2D;
       
    45 import java.awt.geom.Line2D;
       
    46 
       
    47 import java.awt.image.BufferedImage;
       
    48 import sun.awt.image.ByteComponentRaster;
       
    49 
       
    50 import java.awt.print.PageFormat;
       
    51 import java.awt.print.Printable;
       
    52 import java.awt.print.PrinterException;
       
    53 import java.awt.print.PrinterJob;
       
    54 
       
    55 /**
       
    56  * This class converts paths into PostScript
       
    57  * by breaking all graphics into fills and
       
    58  * clips of paths.
       
    59  */
       
    60 
       
    61 class PSPathGraphics extends PathGraphics {
       
    62 
       
    63     /**
       
    64      * For a drawing application the initial user space
       
    65      * resolution is 72dpi.
       
    66      */
       
    67     private static final int DEFAULT_USER_RES = 72;
       
    68 
       
    69     PSPathGraphics(Graphics2D graphics, PrinterJob printerJob,
       
    70                    Printable painter, PageFormat pageFormat, int pageIndex,
       
    71                    boolean canRedraw) {
       
    72         super(graphics, printerJob, painter, pageFormat, pageIndex, canRedraw);
       
    73     }
       
    74 
       
    75     /**
       
    76      * Creates a new <code>Graphics</code> object that is
       
    77      * a copy of this <code>Graphics</code> object.
       
    78      * @return     a new graphics context that is a copy of
       
    79      *                       this graphics context.
       
    80      * @since      JDK1.0
       
    81      */
       
    82     public Graphics create() {
       
    83 
       
    84         return new PSPathGraphics((Graphics2D) getDelegate().create(),
       
    85                                   getPrinterJob(),
       
    86                                   getPrintable(),
       
    87                                   getPageFormat(),
       
    88                                   getPageIndex(),
       
    89                                   canDoRedraws());
       
    90     }
       
    91 
       
    92 
       
    93     /**
       
    94      * Override the inherited implementation of fill
       
    95      * so that we can generate PostScript in user space
       
    96      * rather than device space.
       
    97      */
       
    98     public void fill(Shape s, Color color) {
       
    99         deviceFill(s.getPathIterator(new AffineTransform()), color);
       
   100     }
       
   101 
       
   102     /**
       
   103      * Draws the text given by the specified string, using this
       
   104      * graphics context's current font and color. The baseline of the
       
   105      * first character is at position (<i>x</i>,&nbsp;<i>y</i>) in this
       
   106      * graphics context's coordinate system.
       
   107      * @param       str      the string to be drawn.
       
   108      * @param       x        the <i>x</i> coordinate.
       
   109      * @param       y        the <i>y</i> coordinate.
       
   110      * @see         java.awt.Graphics#drawBytes
       
   111      * @see         java.awt.Graphics#drawChars
       
   112      * @since       JDK1.0
       
   113      */
       
   114     public void drawString(String str, int x, int y) {
       
   115         drawString(str, (float) x, (float) y);
       
   116     }
       
   117 
       
   118     /**
       
   119      * Renders the text specified by the specified <code>String</code>,
       
   120      * using the current <code>Font</code> and <code>Paint</code> attributes
       
   121      * in the <code>Graphics2D</code> context.
       
   122      * The baseline of the first character is at position
       
   123      * (<i>x</i>,&nbsp;<i>y</i>) in the User Space.
       
   124      * The rendering attributes applied include the <code>Clip</code>,
       
   125      * <code>Transform</code>, <code>Paint</code>, <code>Font</code> and
       
   126      * <code>Composite</code> attributes. For characters in script systems
       
   127      * such as Hebrew and Arabic, the glyphs can be rendered from right to
       
   128      * left, in which case the coordinate supplied is the location of the
       
   129      * leftmost character on the baseline.
       
   130      * @param s the <code>String</code> to be rendered
       
   131      * @param x,&nbsp;y the coordinates where the <code>String</code>
       
   132      * should be rendered
       
   133      * @see #setPaint
       
   134      * @see java.awt.Graphics#setColor
       
   135      * @see java.awt.Graphics#setFont
       
   136      * @see #setTransform
       
   137      * @see #setComposite
       
   138      * @see #setClip
       
   139      */
       
   140      public void drawString(String str, float x, float y) {
       
   141          drawString(str, x, y, getFont(), getFontRenderContext(), 0f);
       
   142      }
       
   143 
       
   144 
       
   145     protected boolean canDrawStringToWidth() {
       
   146         return true;
       
   147     }
       
   148 
       
   149     protected int platformFontCount(Font font, String str) {
       
   150         PSPrinterJob psPrinterJob = (PSPrinterJob) getPrinterJob();
       
   151         return psPrinterJob.platformFontCount(font,  str);
       
   152     }
       
   153 
       
   154     protected void drawString(String str, float x, float y,
       
   155                               Font font, FontRenderContext frc, float w) {
       
   156         if (str.length() == 0) {
       
   157             return;
       
   158         }
       
   159 
       
   160         /* If the Font has layout attributes we need to delegate to TextLayout.
       
   161          * TextLayout renders text as GlyphVectors. We try to print those
       
   162          * using printer fonts - ie using Postscript text operators so
       
   163          * we may be reinvoked. In that case the "!printingGlyphVector" test
       
   164          * prevents us recursing and instead sends us into the body of the
       
   165          * method where we can safely ignore layout attributes as those
       
   166          * are already handled by TextLayout.
       
   167          */
       
   168         if (font.hasLayoutAttributes() && !printingGlyphVector) {
       
   169             TextLayout layout = new TextLayout(str, font, frc);
       
   170             layout.draw(this, x, y);
       
   171             return;
       
   172         }
       
   173 
       
   174         Font oldFont = getFont();
       
   175         if (!oldFont.equals(font)) {
       
   176             setFont(font);
       
   177         } else {
       
   178             oldFont = null;
       
   179         }
       
   180 
       
   181         boolean drawnWithPS = false;
       
   182 
       
   183         float translateX = 0f, translateY = 0f;
       
   184         boolean fontisTransformed = getFont().isTransformed();
       
   185 
       
   186         if (fontisTransformed) {
       
   187             AffineTransform fontTx = getFont().getTransform();
       
   188             int transformType = fontTx.getType();
       
   189             /* TYPE_TRANSLATION is a flag bit but we can do "==" here
       
   190              * because we want to detect when its just that bit set and
       
   191              *
       
   192              */
       
   193             if (transformType == AffineTransform.TYPE_TRANSLATION) {
       
   194                 translateX = (float)(fontTx.getTranslateX());
       
   195                 translateY = (float)(fontTx.getTranslateY());
       
   196                 if (Math.abs(translateX) < 0.00001) translateX = 0f;
       
   197                 if (Math.abs(translateY) < 0.00001) translateY = 0f;
       
   198                 fontisTransformed = false;
       
   199             }
       
   200         }
       
   201 
       
   202         boolean directToPS = !fontisTransformed;
       
   203 
       
   204         if (!PSPrinterJob.shapeTextProp && directToPS) {
       
   205 
       
   206             PSPrinterJob psPrinterJob = (PSPrinterJob) getPrinterJob();
       
   207             if (psPrinterJob.setFont(getFont())) {
       
   208 
       
   209                 /* Set the text color.
       
   210                  * We should not be in this shape printing path
       
   211                  * if the application is drawing with non-solid
       
   212                  * colors. We should be in the raster path. Because
       
   213                  * we are here in the shape path, the cast of the
       
   214                  * paint to a Color should be fine.
       
   215                  */
       
   216                 try {
       
   217                     psPrinterJob.setColor((Color)getPaint());
       
   218                 } catch (ClassCastException e) {
       
   219                     if (oldFont != null) {
       
   220                         setFont(oldFont);
       
   221                     }
       
   222                     throw new IllegalArgumentException(
       
   223                                                 "Expected a Color instance");
       
   224                 }
       
   225 
       
   226                 psPrinterJob.setTransform(getTransform());
       
   227                 psPrinterJob.setClip(getClip());
       
   228 
       
   229                 drawnWithPS = psPrinterJob.textOut(this, str,
       
   230                                                    x+translateX, y+translateY,
       
   231                                                    font, frc, w);
       
   232             }
       
   233         }
       
   234 
       
   235         /* The text could not be converted directly to PS text
       
   236          * calls so decompose the text into a shape.
       
   237          */
       
   238         if (drawnWithPS == false) {
       
   239             if (oldFont != null) {
       
   240                 setFont(oldFont);
       
   241                 oldFont = null;
       
   242             }
       
   243             super.drawString(str, x, y, font, frc, w);
       
   244         }
       
   245 
       
   246         if (oldFont != null) {
       
   247             setFont(oldFont);
       
   248         }
       
   249     }
       
   250 
       
   251     /**
       
   252      * The various <code>drawImage()</code> methods for
       
   253      * <code>WPathGraphics</code> are all decomposed
       
   254      * into an invocation of <code>drawImageToPlatform</code>.
       
   255      * The portion of the passed in image defined by
       
   256      * <code>srcX, srcY, srcWidth, and srcHeight</code>
       
   257      * is transformed by the supplied AffineTransform and
       
   258      * drawn using PS to the printer context.
       
   259      *
       
   260      * @param   img     The image to be drawn.
       
   261      *                  This method does nothing if <code>img</code> is null.
       
   262      * @param   xform   Used to tranform the image before drawing.
       
   263      *                  This can be null.
       
   264      * @param   bgcolor This color is drawn where the image has transparent
       
   265      *                  pixels. If this parameter is null then the
       
   266      *                  pixels already in the destination should show
       
   267      *                  through.
       
   268      * @param   srcX    With srcY this defines the upper-left corner
       
   269      *                  of the portion of the image to be drawn.
       
   270      *
       
   271      * @param   srcY    With srcX this defines the upper-left corner
       
   272      *                  of the portion of the image to be drawn.
       
   273      * @param   srcWidth    The width of the portion of the image to
       
   274      *                      be drawn.
       
   275      * @param   srcHeight   The height of the portion of the image to
       
   276      *                      be drawn.
       
   277      * @param   handlingTransparency if being recursively called to
       
   278      *                    print opaque region of transparent image
       
   279      */
       
   280     protected boolean drawImageToPlatform(Image image, AffineTransform xform,
       
   281                                           Color bgcolor,
       
   282                                           int srcX, int srcY,
       
   283                                           int srcWidth, int srcHeight,
       
   284                                           boolean handlingTransparency) {
       
   285 
       
   286         BufferedImage img = getBufferedImage(image);
       
   287         if (img == null) {
       
   288             return true;
       
   289         }
       
   290 
       
   291         PSPrinterJob psPrinterJob = (PSPrinterJob) getPrinterJob();
       
   292 
       
   293         /* The full transform to be applied to the image is the
       
   294          * caller's transform concatenated on to the transform
       
   295          * from user space to device space. If the caller didn't
       
   296          * supply a transform then we just act as if they passed
       
   297          * in the identify transform.
       
   298          */
       
   299         AffineTransform fullTransform = getTransform();
       
   300         if (xform == null) {
       
   301             xform = new AffineTransform();
       
   302         }
       
   303         fullTransform.concatenate(xform);
       
   304 
       
   305         /* Split the full transform into a pair of
       
   306          * transforms. The first transform holds effects
       
   307          * such as rotation and shearing. The second transform
       
   308          * is setup to hold only the scaling effects.
       
   309          * These transforms are created such that a point,
       
   310          * p, in user space, when transformed by 'fullTransform'
       
   311          * lands in the same place as when it is transformed
       
   312          * by 'rotTransform' and then 'scaleTransform'.
       
   313          *
       
   314          * The entire image transformation is not in Java in order
       
   315          * to minimize the amount of memory needed in the VM. By
       
   316          * dividing the transform in two, we rotate and shear
       
   317          * the source image in its own space and only go to
       
   318          * the, usually, larger, device space when we ask
       
   319          * PostScript to perform the final scaling.
       
   320          */
       
   321         double[] fullMatrix = new double[6];
       
   322         fullTransform.getMatrix(fullMatrix);
       
   323 
       
   324         /* Calculate the amount of scaling in the x
       
   325          * and y directions. This scaling is computed by
       
   326          * transforming a unit vector along each axis
       
   327          * and computing the resulting magnitude.
       
   328          * The computed values 'scaleX' and 'scaleY'
       
   329          * represent the amount of scaling PS will be asked
       
   330          * to perform.
       
   331          * Clamp this to the device scale for better quality printing.
       
   332          */
       
   333         Point2D.Float unitVectorX = new Point2D.Float(1, 0);
       
   334         Point2D.Float unitVectorY = new Point2D.Float(0, 1);
       
   335         fullTransform.deltaTransform(unitVectorX, unitVectorX);
       
   336         fullTransform.deltaTransform(unitVectorY, unitVectorY);
       
   337 
       
   338         Point2D.Float origin = new Point2D.Float(0, 0);
       
   339         double scaleX = unitVectorX.distance(origin);
       
   340         double scaleY = unitVectorY.distance(origin);
       
   341 
       
   342         double devResX = psPrinterJob.getXRes();
       
   343         double devResY = psPrinterJob.getYRes();
       
   344         double devScaleX = devResX / DEFAULT_USER_RES;
       
   345         double devScaleY = devResY / DEFAULT_USER_RES;
       
   346 
       
   347         if (scaleX > devScaleX) scaleX = devScaleX;
       
   348         if (scaleY > devScaleY) scaleY = devScaleY;
       
   349 
       
   350         /* We do not need to draw anything if either scaling
       
   351          * factor is zero.
       
   352          */
       
   353         if (scaleX != 0 && scaleY != 0) {
       
   354 
       
   355             /* Here's the transformation we will do with Java2D,
       
   356             */
       
   357             AffineTransform rotTransform = new AffineTransform(
       
   358                                         fullMatrix[0] / scaleX,  //m00
       
   359                                         fullMatrix[1] / scaleY,  //m10
       
   360                                         fullMatrix[2] / scaleX,  //m01
       
   361                                         fullMatrix[3] / scaleY,  //m11
       
   362                                         fullMatrix[4] / scaleX,  //m02
       
   363                                         fullMatrix[5] / scaleY); //m12
       
   364 
       
   365             /* The scale transform is not used directly: we instead
       
   366              * directly multiply by scaleX and scaleY.
       
   367              *
       
   368              * Conceptually here is what the scaleTransform is:
       
   369              *
       
   370              * AffineTransform scaleTransform = new AffineTransform(
       
   371              *                      scaleX,                     //m00
       
   372              *                      0,                          //m10
       
   373              *                      0,                          //m01
       
   374              *                      scaleY,                     //m11
       
   375              *                      0,                          //m02
       
   376              *                      0);                         //m12
       
   377              */
       
   378 
       
   379             /* Convert the image source's rectangle into the rotated
       
   380              * and sheared space. Once there, we calculate a rectangle
       
   381              * that encloses the resulting shape. It is this rectangle
       
   382              * which defines the size of the BufferedImage we need to
       
   383              * create to hold the transformed image.
       
   384              */
       
   385             Rectangle2D.Float srcRect = new Rectangle2D.Float(srcX, srcY,
       
   386                                                               srcWidth,
       
   387                                                               srcHeight);
       
   388 
       
   389             Shape rotShape = rotTransform.createTransformedShape(srcRect);
       
   390             Rectangle2D rotBounds = rotShape.getBounds2D();
       
   391 
       
   392             /* add a fudge factor as some fp precision problems have
       
   393              * been observed which caused pixels to be rounded down and
       
   394              * out of the image.
       
   395              */
       
   396             rotBounds.setRect(rotBounds.getX(), rotBounds.getY(),
       
   397                               rotBounds.getWidth()+0.001,
       
   398                               rotBounds.getHeight()+0.001);
       
   399 
       
   400             int boundsWidth = (int) rotBounds.getWidth();
       
   401             int boundsHeight = (int) rotBounds.getHeight();
       
   402 
       
   403             if (boundsWidth > 0 && boundsHeight > 0) {
       
   404 
       
   405 
       
   406                 /* If the image has transparent or semi-transparent
       
   407                  * pixels then we'll have the application re-render
       
   408                  * the portion of the page covered by the image.
       
   409                  * This will be done in a later call to print using the
       
   410                  * saved graphics state.
       
   411                  * However several special cases can be handled otherwise:
       
   412                  * - bitmask transparency with a solid background colour
       
   413                  * - images which have transparency color models but no
       
   414                  * transparent pixels
       
   415                  * - images with bitmask transparency and an IndexColorModel
       
   416                  * (the common transparent GIF case) can be handled by
       
   417                  * rendering just the opaque pixels.
       
   418                  */
       
   419                 boolean drawOpaque = true;
       
   420                 if (!handlingTransparency && hasTransparentPixels(img)) {
       
   421                     drawOpaque = false;
       
   422                     if (isBitmaskTransparency(img)) {
       
   423                         if (bgcolor == null) {
       
   424                             if (drawBitmaskImage(img, xform, bgcolor,
       
   425                                                 srcX, srcY,
       
   426                                                  srcWidth, srcHeight)) {
       
   427                                 // image drawn, just return.
       
   428                                 return true;
       
   429                             }
       
   430                         } else if (bgcolor.getTransparency()
       
   431                                    == Transparency.OPAQUE) {
       
   432                             drawOpaque = true;
       
   433                         }
       
   434                     }
       
   435                     if (!canDoRedraws()) {
       
   436                         drawOpaque = true;
       
   437                     }
       
   438                 } else {
       
   439                     // if there's no transparent pixels there's no need
       
   440                     // for a background colour. This can avoid edge artifacts
       
   441                     // in rotation cases.
       
   442                     bgcolor = null;
       
   443                 }
       
   444                 // if src region extends beyond the image, the "opaque" path
       
   445                 // may blit b/g colour (including white) where it shoudn't.
       
   446                 if ((srcX+srcWidth > img.getWidth(null) ||
       
   447                      srcY+srcHeight > img.getHeight(null))
       
   448                     && canDoRedraws()) {
       
   449                     drawOpaque = false;
       
   450                 }
       
   451                 if (drawOpaque == false) {
       
   452 
       
   453                     fullTransform.getMatrix(fullMatrix);
       
   454                     AffineTransform tx =
       
   455                         new AffineTransform(
       
   456                                             fullMatrix[0] / devScaleX,  //m00
       
   457                                             fullMatrix[1] / devScaleY,  //m10
       
   458                                             fullMatrix[2] / devScaleX,  //m01
       
   459                                             fullMatrix[3] / devScaleY,  //m11
       
   460                                             fullMatrix[4] / devScaleX,  //m02
       
   461                                             fullMatrix[5] / devScaleY); //m12
       
   462 
       
   463                     Rectangle2D.Float rect =
       
   464                         new Rectangle2D.Float(srcX, srcY, srcWidth, srcHeight);
       
   465 
       
   466                     Shape shape = fullTransform.createTransformedShape(rect);
       
   467                     // Region isn't user space because its potentially
       
   468                     // been rotated for landscape.
       
   469                     Rectangle2D region = shape.getBounds2D();
       
   470 
       
   471                     region.setRect(region.getX(), region.getY(),
       
   472                                    region.getWidth()+0.001,
       
   473                                    region.getHeight()+0.001);
       
   474 
       
   475                     // Try to limit the amount of memory used to 8Mb, so
       
   476                     // if at device resolution this exceeds a certain
       
   477                     // image size then scale down the region to fit in
       
   478                     // that memory, but never to less than 72 dpi.
       
   479 
       
   480                     int w = (int)region.getWidth();
       
   481                     int h = (int)region.getHeight();
       
   482                     int nbytes = w * h * 3;
       
   483                     int maxBytes = 8 * 1024 * 1024;
       
   484                     double origDpi = (devResX < devResY) ? devResX : devResY;
       
   485                     int dpi = (int)origDpi;
       
   486                     double scaleFactor = 1;
       
   487 
       
   488                     double maxSFX = w/(double)boundsWidth;
       
   489                     double maxSFY = h/(double)boundsHeight;
       
   490                     double maxSF = (maxSFX > maxSFY) ? maxSFY : maxSFX;
       
   491                     int minDpi = (int)(dpi/maxSF);
       
   492                     if (minDpi < DEFAULT_USER_RES) minDpi = DEFAULT_USER_RES;
       
   493 
       
   494                     while (nbytes > maxBytes && dpi > minDpi) {
       
   495                         scaleFactor *= 2;
       
   496                         dpi /= 2;
       
   497                         nbytes /= 4;
       
   498                     }
       
   499                     if (dpi < minDpi) {
       
   500                         scaleFactor = (origDpi / minDpi);
       
   501                     }
       
   502 
       
   503                     region.setRect(region.getX()/scaleFactor,
       
   504                                    region.getY()/scaleFactor,
       
   505                                    region.getWidth()/scaleFactor,
       
   506                                    region.getHeight()/scaleFactor);
       
   507 
       
   508                     /*
       
   509                      * We need to have the clip as part of the saved state,
       
   510                      * either directly, or all the components that are
       
   511                      * needed to reconstitute it (image source area,
       
   512                      * image transform and current graphics transform).
       
   513                      * The clip is described in user space, so we need to
       
   514                      * save the current graphics transform anyway so just
       
   515                      * save these two.
       
   516                      */
       
   517                     psPrinterJob.saveState(getTransform(), getClip(),
       
   518                                            region, scaleFactor, scaleFactor);
       
   519                     return true;
       
   520 
       
   521                 /* The image can be rendered directly by PS so we
       
   522                  * copy it into a BufferedImage (this takes care of
       
   523                  * ColorSpace and BufferedImageOp issues) and then
       
   524                  * send that to PS.
       
   525                  */
       
   526                 } else {
       
   527 
       
   528                     /* Create a buffered image big enough to hold the portion
       
   529                      * of the source image being printed.
       
   530                      */
       
   531                     BufferedImage deepImage = new BufferedImage(
       
   532                                                     (int) rotBounds.getWidth(),
       
   533                                                     (int) rotBounds.getHeight(),
       
   534                                                     BufferedImage.TYPE_3BYTE_BGR);
       
   535 
       
   536                     /* Setup a Graphics2D on to the BufferedImage so that the
       
   537                      * source image when copied, lands within the image buffer.
       
   538                      */
       
   539                     Graphics2D imageGraphics = deepImage.createGraphics();
       
   540                     imageGraphics.clipRect(0, 0,
       
   541                                            deepImage.getWidth(),
       
   542                                            deepImage.getHeight());
       
   543 
       
   544                     imageGraphics.translate(-rotBounds.getX(),
       
   545                                             -rotBounds.getY());
       
   546                     imageGraphics.transform(rotTransform);
       
   547 
       
   548                     /* Fill the BufferedImage either with the caller supplied
       
   549                      * color, 'bgColor' or, if null, with white.
       
   550                      */
       
   551                     if (bgcolor == null) {
       
   552                         bgcolor = Color.white;
       
   553                     }
       
   554 
       
   555                     /* REMIND: no need to use scaling here. */
       
   556                     imageGraphics.drawImage(img,
       
   557                                             srcX, srcY,
       
   558                                             srcX + srcWidth, srcY + srcHeight,
       
   559                                             srcX, srcY,
       
   560                                             srcX + srcWidth, srcY + srcHeight,
       
   561                                             bgcolor, null);
       
   562 
       
   563                     /* In PSPrinterJob images are printed in device space
       
   564                      * and therefore we need to set a device space clip.
       
   565                      * FIX: this is an overly tight coupling of these
       
   566                      * two classes.
       
   567                      * The temporary clip set needs to be an intersection
       
   568                      * with the previous user clip.
       
   569                      * REMIND: two xfms may lose accuracy in clip path.
       
   570                      */
       
   571                     Shape holdClip = getClip();
       
   572                     Shape oldClip =
       
   573                         getTransform().createTransformedShape(holdClip);
       
   574                     AffineTransform sat = AffineTransform.getScaleInstance(
       
   575                                                              scaleX, scaleY);
       
   576                     Shape imgClip = sat.createTransformedShape(rotShape);
       
   577                     Area imgArea = new Area(imgClip);
       
   578                     Area oldArea = new Area(oldClip);
       
   579                     imgArea.intersect(oldArea);
       
   580                     psPrinterJob.setClip(imgArea);
       
   581 
       
   582                     /* Scale the bounding rectangle by the scale transform.
       
   583                      * Because the scaling transform has only x and y
       
   584                      * scaling components it is equivalent to multiply
       
   585                      * the x components of the bounding rectangle by
       
   586                      * the x scaling factor and to multiply the y components
       
   587                      * by the y scaling factor.
       
   588                      */
       
   589                     Rectangle2D.Float scaledBounds
       
   590                             = new Rectangle2D.Float(
       
   591                                     (float) (rotBounds.getX() * scaleX),
       
   592                                     (float) (rotBounds.getY() * scaleY),
       
   593                                     (float) (rotBounds.getWidth() * scaleX),
       
   594                                     (float) (rotBounds.getHeight() * scaleY));
       
   595 
       
   596 
       
   597                     /* Pull the raster data from the buffered image
       
   598                      * and pass it along to PS.
       
   599                      */
       
   600                     ByteComponentRaster tile =
       
   601                                    (ByteComponentRaster)deepImage.getRaster();
       
   602 
       
   603                     psPrinterJob.drawImageBGR(tile.getDataStorage(),
       
   604                                 scaledBounds.x, scaledBounds.y,
       
   605                                 (float)Math.rint(scaledBounds.width+0.5),
       
   606                                 (float)Math.rint(scaledBounds.height+0.5),
       
   607                                 0f, 0f,
       
   608                                 deepImage.getWidth(), deepImage.getHeight(),
       
   609                                 deepImage.getWidth(), deepImage.getHeight());
       
   610 
       
   611                     /* Reset the device clip to match user clip */
       
   612                     psPrinterJob.setClip(
       
   613                                getTransform().createTransformedShape(holdClip));
       
   614 
       
   615 
       
   616                     imageGraphics.dispose();
       
   617                 }
       
   618 
       
   619             }
       
   620         }
       
   621 
       
   622         return true;
       
   623     }
       
   624 
       
   625     /** Redraw a rectanglular area using a proxy graphics
       
   626       * To do this we need to know the rectangular area to redraw and
       
   627       * the transform & clip in effect at the time of the original drawImage
       
   628       *
       
   629       */
       
   630 
       
   631     public void redrawRegion(Rectangle2D region, double scaleX, double scaleY,
       
   632                              Shape savedClip, AffineTransform savedTransform)
       
   633 
       
   634             throws PrinterException {
       
   635 
       
   636         PSPrinterJob psPrinterJob = (PSPrinterJob)getPrinterJob();
       
   637         Printable painter = getPrintable();
       
   638         PageFormat pageFormat = getPageFormat();
       
   639         int pageIndex = getPageIndex();
       
   640 
       
   641         /* Create a buffered image big enough to hold the portion
       
   642          * of the source image being printed.
       
   643          */
       
   644         BufferedImage deepImage = new BufferedImage(
       
   645                                         (int) region.getWidth(),
       
   646                                         (int) region.getHeight(),
       
   647                                         BufferedImage.TYPE_3BYTE_BGR);
       
   648 
       
   649         /* Get a graphics for the application to render into.
       
   650          * We initialize the buffer to white in order to
       
   651          * match the paper and then we shift the BufferedImage
       
   652          * so that it covers the area on the page where the
       
   653          * caller's Image will be drawn.
       
   654          */
       
   655         Graphics2D g = deepImage.createGraphics();
       
   656         ProxyGraphics2D proxy = new ProxyGraphics2D(g, psPrinterJob);
       
   657         proxy.setColor(Color.white);
       
   658         proxy.fillRect(0, 0, deepImage.getWidth(), deepImage.getHeight());
       
   659         proxy.clipRect(0, 0, deepImage.getWidth(), deepImage.getHeight());
       
   660 
       
   661         proxy.translate(-region.getX(), -region.getY());
       
   662 
       
   663         /* Calculate the resolution of the source image.
       
   664          */
       
   665         float sourceResX = (float)(psPrinterJob.getXRes() / scaleX);
       
   666         float sourceResY = (float)(psPrinterJob.getYRes() / scaleY);
       
   667 
       
   668         /* The application expects to see user space at 72 dpi.
       
   669          * so change user space from image source resolution to
       
   670          *  72 dpi.
       
   671          */
       
   672         proxy.scale(sourceResX / DEFAULT_USER_RES,
       
   673                     sourceResY / DEFAULT_USER_RES);
       
   674        proxy.translate(
       
   675             -psPrinterJob.getPhysicalPrintableX(pageFormat.getPaper())
       
   676                / psPrinterJob.getXRes() * DEFAULT_USER_RES,
       
   677             -psPrinterJob.getPhysicalPrintableY(pageFormat.getPaper())
       
   678                / psPrinterJob.getYRes() * DEFAULT_USER_RES);
       
   679        /* NB User space now has to be at 72 dpi for this calc to be correct */
       
   680         proxy.transform(new AffineTransform(getPageFormat().getMatrix()));
       
   681 
       
   682         proxy.setPaint(Color.black);
       
   683 
       
   684         painter.print(proxy, pageFormat, pageIndex);
       
   685 
       
   686         g.dispose();
       
   687 
       
   688         /* In PSPrinterJob images are printed in device space
       
   689          * and therefore we need to set a device space clip.
       
   690          */
       
   691         psPrinterJob.setClip(savedTransform.createTransformedShape(savedClip));
       
   692 
       
   693 
       
   694         /* Scale the bounding rectangle by the scale transform.
       
   695          * Because the scaling transform has only x and y
       
   696          * scaling components it is equivalent to multiply
       
   697          * the x components of the bounding rectangle by
       
   698          * the x scaling factor and to multiply the y components
       
   699          * by the y scaling factor.
       
   700          */
       
   701         Rectangle2D.Float scaledBounds
       
   702                 = new Rectangle2D.Float(
       
   703                         (float) (region.getX() * scaleX),
       
   704                         (float) (region.getY() * scaleY),
       
   705                         (float) (region.getWidth() * scaleX),
       
   706                         (float) (region.getHeight() * scaleY));
       
   707 
       
   708 
       
   709         /* Pull the raster data from the buffered image
       
   710          * and pass it along to PS.
       
   711          */
       
   712         ByteComponentRaster tile = (ByteComponentRaster)deepImage.getRaster();
       
   713 
       
   714         psPrinterJob.drawImageBGR(tile.getDataStorage(),
       
   715                             scaledBounds.x, scaledBounds.y,
       
   716                             scaledBounds.width,
       
   717                             scaledBounds.height,
       
   718                             0f, 0f,
       
   719                             deepImage.getWidth(), deepImage.getHeight(),
       
   720                             deepImage.getWidth(), deepImage.getHeight());
       
   721 
       
   722 
       
   723     }
       
   724 
       
   725 
       
   726     /*
       
   727      * Fill the path defined by <code>pathIter</code>
       
   728      * with the specified color.
       
   729      * The path is provided in current user space.
       
   730      */
       
   731     protected void deviceFill(PathIterator pathIter, Color color) {
       
   732 
       
   733         PSPrinterJob psPrinterJob = (PSPrinterJob) getPrinterJob();
       
   734         psPrinterJob.deviceFill(pathIter, color, getTransform(), getClip());
       
   735     }
       
   736 
       
   737     /*
       
   738      * Draw the bounding rectangle using path by calling draw()
       
   739      * function and passing a rectangle shape.
       
   740      */
       
   741     protected void deviceFrameRect(int x, int y, int width, int height,
       
   742                                    Color color) {
       
   743 
       
   744         draw(new Rectangle2D.Float(x, y, width, height));
       
   745     }
       
   746 
       
   747     /*
       
   748      * Draw a line using path by calling draw() function and passing
       
   749      * a line shape.
       
   750      */
       
   751     protected void deviceDrawLine(int xBegin, int yBegin,
       
   752                                   int xEnd, int yEnd, Color color) {
       
   753 
       
   754         draw(new Line2D.Float(xBegin, yBegin, xEnd, yEnd));
       
   755     }
       
   756 
       
   757     /*
       
   758      * Fill the rectangle with the specified color by calling fill().
       
   759      */
       
   760     protected void deviceFillRect(int x, int y, int width, int height,
       
   761                                   Color color) {
       
   762         fill(new Rectangle2D.Float(x, y, width, height));
       
   763     }
       
   764 
       
   765 
       
   766     /*
       
   767      * This method should not be invoked by PSPathGraphics.
       
   768      * FIX: Rework PathGraphics so that this method is
       
   769      * not an abstract method there.
       
   770      */
       
   771     protected void deviceClip(PathIterator pathIter) {
       
   772     }
       
   773 
       
   774 }