/* * Copyright (c) 1998, 2008, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.print; import java.awt.Color;import java.awt.Font;import java.awt.Graphics;import java.awt.Graphics2D;import java.awt.Image;import java.awt.Shape;import java.awt.Transparency; import java.awt.font.FontRenderContext;import java.awt.font.TextLayout; import java.awt.geom.AffineTransform;import java.awt.geom.Area;import java.awt.geom.PathIterator;import java.awt.geom.Point2D;import java.awt.geom.Rectangle2D;import java.awt.geom.Line2D; import java.awt.image.BufferedImage;import sun.awt.image.ByteComponentRaster; import java.awt.print.PageFormat;import java.awt.print.Printable;import java.awt.print.PrinterException;import java.awt.print.PrinterJob; /** * This class converts paths into PostScript * by breaking all graphics into fills and * clips of paths. */ class PSPathGraphics extends PathGraphics { /** * For a drawing application the initial user space * resolution is 72dpi. */ private static final int DEFAULT_USER_RES = 72; PSPathGraphics(Graphics2D graphics, PrinterJob printerJob, Printable painter, PageFormat pageFormat, int pageIndex, boolean canRedraw) { super(graphics, printerJob, painter, pageFormat, pageIndex, canRedraw); } /** * Creates a new <code>Graphics</code> object that is * a copy of this <code>Graphics</code> object. * @return a new graphics context that is a copy of * this graphics context. * @since JDK1.0 */ public Graphics create() { return new PSPathGraphics((Graphics2D) getDelegate().create(), getPrinterJob(), getPrintable(), getPageFormat(), getPageIndex(), canDoRedraws()); } /** * Override the inherited implementation of fill * so that we can generate PostScript in user space * rather than device space. */ public void fill(Shape s, Color color) { deviceFill(s.getPathIterator(new AffineTransform()), color); } /** * Draws the text given by the specified string, using this * graphics context's current font and color. The baseline of the * first character is at position (<i>x</i>, <i>y</i>) in this * graphics context's coordinate system. * @param str the string to be drawn. * @param x the <i>x</i> coordinate. * @param y the <i>y</i> coordinate. * @see java.awt.Graphics#drawBytes * @see java.awt.Graphics#drawChars * @since JDK1.0 */ public void drawString(String str, int x, int y) { drawString(str, (float) x, (float) y); } /** * Renders the text specified by the specified <code>String</code>, * using the current <code>Font</code> and <code>Paint</code> attributes * in the <code>Graphics2D</code> context. * The baseline of the first character is at position * (<i>x</i>, <i>y</i>) in the User Space. * The rendering attributes applied include the <code>Clip</code>, * <code>Transform</code>, <code>Paint</code>, <code>Font</code> and * <code>Composite</code> attributes. For characters in script systems * such as Hebrew and Arabic, the glyphs can be rendered from right to * left, in which case the coordinate supplied is the location of the * leftmost character on the baseline. * @param s the <code>String</code> to be rendered * @param x, y the coordinates where the <code>String</code> * should be rendered * @see #setPaint * @see java.awt.Graphics#setColor * @see java.awt.Graphics#setFont * @see #setTransform * @see #setComposite * @see #setClip */ public void drawString(String str, float x, float y) { drawString(str, x, y, getFont(), getFontRenderContext(), 0f); } protected boolean canDrawStringToWidth() { return true; } protected int platformFontCount(Font font, String str) { PSPrinterJob psPrinterJob = (PSPrinterJob) getPrinterJob(); return psPrinterJob.platformFontCount(font, str); } protected void drawString(String str, float x, float y, Font font, FontRenderContext frc, float w) { if (str.length() == 0) { return; } /* If the Font has layout attributes we need to delegate to TextLayout. * TextLayout renders text as GlyphVectors. We try to print those * using printer fonts - ie using Postscript text operators so * we may be reinvoked. In that case the "!printingGlyphVector" test * prevents us recursing and instead sends us into the body of the * method where we can safely ignore layout attributes as those * are already handled by TextLayout. */ if (font.hasLayoutAttributes() && !printingGlyphVector) { TextLayout layout = new TextLayout(str, font, frc); layout.draw(this, x, y); return; } Font oldFont = getFont(); if (!oldFont.equals(font)) { setFont(font); } else { oldFont = null; } boolean drawnWithPS = false; float translateX = 0f, translateY = 0f; boolean fontisTransformed = getFont().isTransformed(); if (fontisTransformed) { AffineTransform fontTx = getFont().getTransform(); int transformType = fontTx.getType(); /* TYPE_TRANSLATION is a flag bit but we can do "==" here * because we want to detect when its just that bit set and * */ if (transformType == AffineTransform.TYPE_TRANSLATION) { translateX = (float)(fontTx.getTranslateX()); translateY = (float)(fontTx.getTranslateY()); if (Math.abs(translateX) < 0.00001) translateX = 0f; if (Math.abs(translateY) < 0.00001) translateY = 0f; fontisTransformed = false; } } boolean directToPS = !fontisTransformed; if (!PSPrinterJob.shapeTextProp && directToPS) { PSPrinterJob psPrinterJob = (PSPrinterJob) getPrinterJob(); if (psPrinterJob.setFont(getFont())) { /* Set the text color. * We should not be in this shape printing path * if the application is drawing with non-solid * colors. We should be in the raster path. Because * we are here in the shape path, the cast of the * paint to a Color should be fine. */ try { psPrinterJob.setColor((Color)getPaint()); } catch (ClassCastException e) { if (oldFont != null) { setFont(oldFont); } throw new IllegalArgumentException( "Expected a Color instance"); } psPrinterJob.setTransform(getTransform()); psPrinterJob.setClip(getClip()); drawnWithPS = psPrinterJob.textOut(this, str, x+translateX, y+translateY, font, frc, w); } } /* The text could not be converted directly to PS text * calls so decompose the text into a shape. */ if (drawnWithPS == false) { if (oldFont != null) { setFont(oldFont); oldFont = null; } super.drawString(str, x, y, font, frc, w); } if (oldFont != null) { setFont(oldFont); } } /** * The various <code>drawImage()</code> methods for * <code>WPathGraphics</code> are all decomposed * into an invocation of <code>drawImageToPlatform</code>. * The portion of the passed in image defined by * <code>srcX, srcY, srcWidth, and srcHeight</code> * is transformed by the supplied AffineTransform and * drawn using PS to the printer context. * * @param img The image to be drawn. * This method does nothing if <code>img</code> is null. * @param xform Used to tranform the image before drawing. * This can be null. * @param bgcolor This color is drawn where the image has transparent * pixels. If this parameter is null then the * pixels already in the destination should show * through. * @param srcX With srcY this defines the upper-left corner * of the portion of the image to be drawn. * * @param srcY With srcX this defines the upper-left corner * of the portion of the image to be drawn. * @param srcWidth The width of the portion of the image to * be drawn. * @param srcHeight The height of the portion of the image to * be drawn. * @param handlingTransparency if being recursively called to * print opaque region of transparent image */ protected boolean drawImageToPlatform(Image image, AffineTransform xform, Color bgcolor, int srcX, int srcY, int srcWidth, int srcHeight, boolean handlingTransparency) { BufferedImage img = getBufferedImage(image); if (img == null) { return true; } PSPrinterJob psPrinterJob = (PSPrinterJob) getPrinterJob(); /* The full transform to be applied to the image is the * caller's transform concatenated on to the transform * from user space to device space. If the caller didn't * supply a transform then we just act as if they passed * in the identify transform. */ AffineTransform fullTransform = getTransform(); if (xform == null) { xform = new AffineTransform(); } fullTransform.concatenate(xform); /* Split the full transform into a pair of * transforms. The first transform holds effects * such as rotation and shearing. The second transform * is setup to hold only the scaling effects. * These transforms are created such that a point, * p, in user space, when transformed by 'fullTransform' * lands in the same place as when it is transformed * by 'rotTransform' and then 'scaleTransform'. * * The entire image transformation is not in Java in order * to minimize the amount of memory needed in the VM. By * dividing the transform in two, we rotate and shear * the source image in its own space and only go to * the, usually, larger, device space when we ask * PostScript to perform the final scaling. */ double[] fullMatrix = new double[6]; fullTransform.getMatrix(fullMatrix); /* Calculate the amount of scaling in the x * and y directions. This scaling is computed by * transforming a unit vector along each axis * and computing the resulting magnitude. * The computed values 'scaleX' and 'scaleY' * represent the amount of scaling PS will be asked * to perform. * Clamp this to the device scale for better quality printing. */ Point2D.Float unitVectorX = new Point2D.Float(1, 0); Point2D.Float unitVectorY = new Point2D.Float(0, 1); fullTransform.deltaTransform(unitVectorX, unitVectorX); fullTransform.deltaTransform(unitVectorY, unitVectorY); Point2D.Float origin = new Point2D.Float(0, 0); double scaleX = unitVectorX.distance(origin); double scaleY = unitVectorY.distance(origin); double devResX = psPrinterJob.getXRes(); double devResY = psPrinterJob.getYRes(); double devScaleX = devResX / DEFAULT_USER_RES; double devScaleY = devResY / DEFAULT_USER_RES; /* check if rotated or sheared */ int transformType = fullTransform.getType(); boolean clampScale = ((transformType & (AffineTransform.TYPE_GENERAL_ROTATION | AffineTransform.TYPE_GENERAL_TRANSFORM)) != 0); if (clampScale) { if (scaleX > devScaleX) scaleX = devScaleX; if (scaleY > devScaleY) scaleY = devScaleY; } /* We do not need to draw anything if either scaling * factor is zero. */ if (scaleX != 0 && scaleY != 0) { /* Here's the transformation we will do with Java2D, */ AffineTransform rotTransform = new AffineTransform( fullMatrix[0] / scaleX, //m00 fullMatrix[1] / scaleY, //m10 fullMatrix[2] / scaleX, //m01 fullMatrix[3] / scaleY, //m11 fullMatrix[4] / scaleX, //m02 fullMatrix[5] / scaleY); //m12 /* The scale transform is not used directly: we instead * directly multiply by scaleX and scaleY. * * Conceptually here is what the scaleTransform is: * * AffineTransform scaleTransform = new AffineTransform( * scaleX, //m00 * 0, //m10 * 0, //m01 * scaleY, //m11 * 0, //m02 * 0); //m12 */ /* Convert the image source's rectangle into the rotated * and sheared space. Once there, we calculate a rectangle * that encloses the resulting shape. It is this rectangle * which defines the size of the BufferedImage we need to * create to hold the transformed image. */ Rectangle2D.Float srcRect = new Rectangle2D.Float(srcX, srcY, srcWidth, srcHeight); Shape rotShape = rotTransform.createTransformedShape(srcRect); Rectangle2D rotBounds = rotShape.getBounds2D(); /* add a fudge factor as some fp precision problems have * been observed which caused pixels to be rounded down and * out of the image. */ rotBounds.setRect(rotBounds.getX(), rotBounds.getY(), rotBounds.getWidth()+0.001, rotBounds.getHeight()+0.001); int boundsWidth = (int) rotBounds.getWidth(); int boundsHeight = (int) rotBounds.getHeight(); if (boundsWidth > 0 && boundsHeight > 0) { /* If the image has transparent or semi-transparent * pixels then we'll have the application re-render * the portion of the page covered by the image. * This will be done in a later call to print using the * saved graphics state. * However several special cases can be handled otherwise: * - bitmask transparency with a solid background colour * - images which have transparency color models but no * transparent pixels * - images with bitmask transparency and an IndexColorModel * (the common transparent GIF case) can be handled by * rendering just the opaque pixels. */ boolean drawOpaque = true; if (!handlingTransparency && hasTransparentPixels(img)) { drawOpaque = false; if (isBitmaskTransparency(img)) { if (bgcolor == null) { if (drawBitmaskImage(img, xform, bgcolor, srcX, srcY, srcWidth, srcHeight)) { // image drawn, just return. return true; } } else if (bgcolor.getTransparency() == Transparency.OPAQUE) { drawOpaque = true; } } if (!canDoRedraws()) { drawOpaque = true; } } else { // if there's no transparent pixels there's no need // for a background colour. This can avoid edge artifacts // in rotation cases. bgcolor = null; } // if src region extends beyond the image, the "opaque" path // may blit b/g colour (including white) where it shoudn't. if ((srcX+srcWidth > img.getWidth(null) || srcY+srcHeight > img.getHeight(null)) && canDoRedraws()) { drawOpaque = false; } if (drawOpaque == false) { fullTransform.getMatrix(fullMatrix); AffineTransform tx = new AffineTransform( fullMatrix[0] / devScaleX, //m00 fullMatrix[1] / devScaleY, //m10 fullMatrix[2] / devScaleX, //m01 fullMatrix[3] / devScaleY, //m11 fullMatrix[4] / devScaleX, //m02 fullMatrix[5] / devScaleY); //m12 Rectangle2D.Float rect = new Rectangle2D.Float(srcX, srcY, srcWidth, srcHeight); Shape shape = fullTransform.createTransformedShape(rect); // Region isn't user space because its potentially // been rotated for landscape. Rectangle2D region = shape.getBounds2D(); region.setRect(region.getX(), region.getY(), region.getWidth()+0.001, region.getHeight()+0.001); // Try to limit the amount of memory used to 8Mb, so // if at device resolution this exceeds a certain // image size then scale down the region to fit in // that memory, but never to less than 72 dpi. int w = (int)region.getWidth(); int h = (int)region.getHeight(); int nbytes = w * h * 3; int maxBytes = 8 * 1024 * 1024; double origDpi = (devResX < devResY) ? devResX : devResY; int dpi = (int)origDpi; double scaleFactor = 1; double maxSFX = w/(double)boundsWidth; double maxSFY = h/(double)boundsHeight; double maxSF = (maxSFX > maxSFY) ? maxSFY : maxSFX; int minDpi = (int)(dpi/maxSF); if (minDpi < DEFAULT_USER_RES) minDpi = DEFAULT_USER_RES; while (nbytes > maxBytes && dpi > minDpi) {Lines 1–500 of 780. Load more
/* * Copyright (c) 1998, 2008, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.print; import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Shape; import java.awt.Transparency; import java.awt.font.FontRenderContext; import java.awt.font.TextLayout; import java.awt.geom.AffineTransform; import java.awt.geom.Area; import java.awt.geom.PathIterator; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.geom.Line2D; import java.awt.image.BufferedImage; import sun.awt.image.ByteComponentRaster; import java.awt.print.PageFormat; import java.awt.print.Printable; import java.awt.print.PrinterException; import java.awt.print.PrinterJob; /** * This class converts paths into PostScript * by breaking all graphics into fills and * clips of paths. */ class PSPathGraphics extends PathGraphics { /** * For a drawing application the initial user space * resolution is 72dpi. */ private static final int DEFAULT_USER_RES = 72; PSPathGraphics(Graphics2D graphics, PrinterJob printerJob, Printable painter, PageFormat pageFormat, int pageIndex, boolean canRedraw) { super(graphics, printerJob, painter, pageFormat, pageIndex, canRedraw); } /** * Creates a new <code>Graphics</code> object that is * a copy of this <code>Graphics</code> object. * @return a new graphics context that is a copy of * this graphics context. * @since JDK1.0 */ public Graphics create() { return new PSPathGraphics((Graphics2D) getDelegate().create(), getPrinterJob(), getPrintable(), getPageFormat(), getPageIndex(), canDoRedraws()); } /** * Override the inherited implementation of fill * so that we can generate PostScript in user space * rather than device space. */ public void fill(Shape s, Color color) { deviceFill(s.getPathIterator(new AffineTransform()), color); } /** * Draws the text given by the specified string, using this * graphics context's current font and color. The baseline of the * first character is at position (<i>x</i>, <i>y</i>) in this * graphics context's coordinate system. * @param str the string to be drawn. * @param x the <i>x</i> coordinate. * @param y the <i>y</i> coordinate. * @see java.awt.Graphics#drawBytes * @see java.awt.Graphics#drawChars * @since JDK1.0 */ public void drawString(String str, int x, int y) { drawString(str, (float) x, (float) y); } /** * Renders the text specified by the specified <code>String</code>, * using the current <code>Font</code> and <code>Paint</code> attributes * in the <code>Graphics2D</code> context. * The baseline of the first character is at position * (<i>x</i>, <i>y</i>) in the User Space. * The rendering attributes applied include the <code>Clip</code>, * <code>Transform</code>, <code>Paint</code>, <code>Font</code> and * <code>Composite</code> attributes. For characters in script systems * such as Hebrew and Arabic, the glyphs can be rendered from right to * left, in which case the coordinate supplied is the location of the * leftmost character on the baseline. * @param s the <code>String</code> to be rendered * @param x, y the coordinates where the <code>String</code> * should be rendered * @see #setPaint * @see java.awt.Graphics#setColor * @see java.awt.Graphics#setFont * @see #setTransform * @see #setComposite * @see #setClip */ public void drawString(String str, float x, float y) { drawString(str, x, y, getFont(), getFontRenderContext(), 0f); } protected boolean canDrawStringToWidth() { return true; } protected int platformFontCount(Font font, String str) { PSPrinterJob psPrinterJob = (PSPrinterJob) getPrinterJob(); return psPrinterJob.platformFontCount(font, str); } protected void drawString(String str, float x, float y, Font font, FontRenderContext frc, float w) { if (str.length() == 0) { return; } /* If the Font has layout attributes we need to delegate to TextLayout. * TextLayout renders text as GlyphVectors. We try to print those * using printer fonts - ie using Postscript text operators so * we may be reinvoked. In that case the "!printingGlyphVector" test * prevents us recursing and instead sends us into the body of the * method where we can safely ignore layout attributes as those * are already handled by TextLayout. */ if (font.hasLayoutAttributes() && !printingGlyphVector) { TextLayout layout = new TextLayout(str, font, frc); layout.draw(this, x, y); return; } Font oldFont = getFont(); if (!oldFont.equals(font)) { setFont(font); } else { oldFont = null; } boolean drawnWithPS = false; float translateX = 0f, translateY = 0f; boolean fontisTransformed = getFont().isTransformed(); if (fontisTransformed) { AffineTransform fontTx = getFont().getTransform(); int transformType = fontTx.getType(); /* TYPE_TRANSLATION is a flag bit but we can do "==" here * because we want to detect when its just that bit set and * */ if (transformType == AffineTransform.TYPE_TRANSLATION) { translateX = (float)(fontTx.getTranslateX()); translateY = (float)(fontTx.getTranslateY()); if (Math.abs(translateX) < 0.00001) translateX = 0f; if (Math.abs(translateY) < 0.00001) translateY = 0f; fontisTransformed = false; } } boolean directToPS = !fontisTransformed; if (!PSPrinterJob.shapeTextProp && directToPS) { PSPrinterJob psPrinterJob = (PSPrinterJob) getPrinterJob(); if (psPrinterJob.setFont(getFont())) { /* Set the text color. * We should not be in this shape printing path * if the application is drawing with non-solid * colors. We should be in the raster path. Because * we are here in the shape path, the cast of the * paint to a Color should be fine. */ try { psPrinterJob.setColor((Color)getPaint()); } catch (ClassCastException e) { if (oldFont != null) { setFont(oldFont); } throw new IllegalArgumentException( "Expected a Color instance"); } psPrinterJob.setTransform(getTransform()); psPrinterJob.setClip(getClip()); drawnWithPS = psPrinterJob.textOut(this, str, x+translateX, y+translateY, font, frc, w); } } /* The text could not be converted directly to PS text * calls so decompose the text into a shape. */ if (drawnWithPS == false) { if (oldFont != null) { setFont(oldFont); oldFont = null; } super.drawString(str, x, y, font, frc, w); } if (oldFont != null) { setFont(oldFont); } } /** * The various <code>drawImage()</code> methods for * <code>WPathGraphics</code> are all decomposed * into an invocation of <code>drawImageToPlatform</code>. * The portion of the passed in image defined by * <code>srcX, srcY, srcWidth, and srcHeight</code> * is transformed by the supplied AffineTransform and * drawn using PS to the printer context. * * @param img The image to be drawn. * This method does nothing if <code>img</code> is null. * @param xform Used to tranform the image before drawing. * This can be null. * @param bgcolor This color is drawn where the image has transparent * pixels. If this parameter is null then the * pixels already in the destination should show * through. * @param srcX With srcY this defines the upper-left corner * of the portion of the image to be drawn. * * @param srcY With srcX this defines the upper-left corner * of the portion of the image to be drawn. * @param srcWidth The width of the portion of the image to * be drawn. * @param srcHeight The height of the portion of the image to * be drawn. * @param handlingTransparency if being recursively called to * print opaque region of transparent image */ protected boolean drawImageToPlatform(Image image, AffineTransform xform, Color bgcolor, int srcX, int srcY, int srcWidth, int srcHeight, boolean handlingTransparency) { BufferedImage img = getBufferedImage(image); if (img == null) { return true; } PSPrinterJob psPrinterJob = (PSPrinterJob) getPrinterJob(); /* The full transform to be applied to the image is the * caller's transform concatenated on to the transform * from user space to device space. If the caller didn't * supply a transform then we just act as if they passed * in the identify transform. */ AffineTransform fullTransform = getTransform(); if (xform == null) { xform = new AffineTransform(); } fullTransform.concatenate(xform); /* Split the full transform into a pair of * transforms. The first transform holds effects * such as rotation and shearing. The second transform * is setup to hold only the scaling effects. * These transforms are created such that a point, * p, in user space, when transformed by 'fullTransform' * lands in the same place as when it is transformed * by 'rotTransform' and then 'scaleTransform'. * * The entire image transformation is not in Java in order * to minimize the amount of memory needed in the VM. By * dividing the transform in two, we rotate and shear * the source image in its own space and only go to * the, usually, larger, device space when we ask * PostScript to perform the final scaling. */ double[] fullMatrix = new double[6]; fullTransform.getMatrix(fullMatrix); /* Calculate the amount of scaling in the x * and y directions. This scaling is computed by * transforming a unit vector along each axis * and computing the resulting magnitude. * The computed values 'scaleX' and 'scaleY' * represent the amount of scaling PS will be asked * to perform. * Clamp this to the device scale for better quality printing. */ Point2D.Float unitVectorX = new Point2D.Float(1, 0); Point2D.Float unitVectorY = new Point2D.Float(0, 1); fullTransform.deltaTransform(unitVectorX, unitVectorX); fullTransform.deltaTransform(unitVectorY, unitVectorY); Point2D.Float origin = new Point2D.Float(0, 0); double scaleX = unitVectorX.distance(origin); double scaleY = unitVectorY.distance(origin); double devResX = psPrinterJob.getXRes(); double devResY = psPrinterJob.getYRes(); double devScaleX = devResX / DEFAULT_USER_RES; double devScaleY = devResY / DEFAULT_USER_RES; /* check if rotated or sheared */ int transformType = fullTransform.getType(); boolean clampScale = ((transformType & (AffineTransform.TYPE_GENERAL_ROTATION | AffineTransform.TYPE_GENERAL_TRANSFORM)) != 0); if (clampScale) { if (scaleX > devScaleX) scaleX = devScaleX; if (scaleY > devScaleY) scaleY = devScaleY; } /* We do not need to draw anything if either scaling * factor is zero. */ if (scaleX != 0 && scaleY != 0) { /* Here's the transformation we will do with Java2D, */ AffineTransform rotTransform = new AffineTransform( fullMatrix[0] / scaleX, //m00 fullMatrix[1] / scaleY, //m10 fullMatrix[2] / scaleX, //m01 fullMatrix[3] / scaleY, //m11 fullMatrix[4] / scaleX, //m02 fullMatrix[5] / scaleY); //m12 /* The scale transform is not used directly: we instead * directly multiply by scaleX and scaleY. * * Conceptually here is what the scaleTransform is: * * AffineTransform scaleTransform = new AffineTransform( * scaleX, //m00 * 0, //m10 * 0, //m01 * scaleY, //m11 * 0, //m02 * 0); //m12 */ /* Convert the image source's rectangle into the rotated * and sheared space. Once there, we calculate a rectangle * that encloses the resulting shape. It is this rectangle * which defines the size of the BufferedImage we need to * create to hold the transformed image. */ Rectangle2D.Float srcRect = new Rectangle2D.Float(srcX, srcY, srcWidth, srcHeight); Shape rotShape = rotTransform.createTransformedShape(srcRect); Rectangle2D rotBounds = rotShape.getBounds2D(); /* add a fudge factor as some fp precision problems have * been observed which caused pixels to be rounded down and * out of the image. */ rotBounds.setRect(rotBounds.getX(), rotBounds.getY(), rotBounds.getWidth()+0.001, rotBounds.getHeight()+0.001); int boundsWidth = (int) rotBounds.getWidth(); int boundsHeight = (int) rotBounds.getHeight(); if (boundsWidth > 0 && boundsHeight > 0) { /* If the image has transparent or semi-transparent * pixels then we'll have the application re-render * the portion of the page covered by the image. * This will be done in a later call to print using the * saved graphics state. * However several special cases can be handled otherwise: * - bitmask transparency with a solid background colour * - images which have transparency color models but no * transparent pixels * - images with bitmask transparency and an IndexColorModel * (the common transparent GIF case) can be handled by * rendering just the opaque pixels. */ boolean drawOpaque = true; if (!handlingTransparency && hasTransparentPixels(img)) { drawOpaque = false; if (isBitmaskTransparency(img)) { if (bgcolor == null) { if (drawBitmaskImage(img, xform, bgcolor, srcX, srcY, srcWidth, srcHeight)) { // image drawn, just return. return true; } } else if (bgcolor.getTransparency() == Transparency.OPAQUE) { drawOpaque = true; } } if (!canDoRedraws()) { drawOpaque = true; } } else { // if there's no transparent pixels there's no need // for a background colour. This can avoid edge artifacts // in rotation cases. bgcolor = null; } // if src region extends beyond the image, the "opaque" path // may blit b/g colour (including white) where it shoudn't. if ((srcX+srcWidth > img.getWidth(null) || srcY+srcHeight > img.getHeight(null)) && canDoRedraws()) { drawOpaque = false; } if (drawOpaque == false) { fullTransform.getMatrix(fullMatrix); AffineTransform tx = new AffineTransform( fullMatrix[0] / devScaleX, //m00 fullMatrix[1] / devScaleY, //m10 fullMatrix[2] / devScaleX, //m01 fullMatrix[3] / devScaleY, //m11 fullMatrix[4] / devScaleX, //m02 fullMatrix[5] / devScaleY); //m12 Rectangle2D.Float rect = new Rectangle2D.Float(srcX, srcY, srcWidth, srcHeight); Shape shape = fullTransform.createTransformedShape(rect); // Region isn't user space because its potentially // been rotated for landscape. Rectangle2D region = shape.getBounds2D(); region.setRect(region.getX(), region.getY(), region.getWidth()+0.001, region.getHeight()+0.001); // Try to limit the amount of memory used to 8Mb, so // if at device resolution this exceeds a certain // image size then scale down the region to fit in // that memory, but never to less than 72 dpi. int w = (int)region.getWidth(); int h = (int)region.getHeight(); int nbytes = w * h * 3; int maxBytes = 8 * 1024 * 1024; double origDpi = (devResX < devResY) ? devResX : devResY; int dpi = (int)origDpi; double scaleFactor = 1; double maxSFX = w/(double)boundsWidth; double maxSFY = h/(double)boundsHeight; double maxSF = (maxSFX > maxSFY) ? maxSFY : maxSFX; int minDpi = (int)(dpi/maxSF); if (minDpi < DEFAULT_USER_RES) minDpi = DEFAULT_USER_RES; while (nbytes > maxBytes && dpi > minDpi) {