gt:             at = new AffineTransform(0, -1, -1, 0, width, height);
context: /*
   * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
   * Santa Clara, California 95054, U.S.A. All rights reserved.
   *
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   *
   * This library 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
   * Lesser General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public
   * License along with this library; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
   */
  package com.sun.pdfview;
  import java.awt.BasicStroke;
  import java.awt.Color;
  import java.awt.Dimension;
  import java.awt.Image;
  import java.awt.Shape;
  import java.awt.geom.AffineTransform;
  import java.awt.geom.GeneralPath;
  import java.awt.geom.NoninvertibleTransformException;
  import java.awt.geom.Rectangle2D;
  import java.awt.image.BufferedImage;
  import java.awt.image.ImageObserver;
  import java.lang.ref.WeakReference;
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Map;
  
  import com.sun.pdfview.annotation.AnnotationType;
  import com.sun.pdfview.annotation.PDFAnnotation;
  
  /**
  * A PDFPage encapsulates the parsed commands required to render a
  * single page from a PDFFile. The PDFPage is not itself drawable;
  * instead, create a PDFImage to display something on the screen.
  * <p>
  * This file also contains all of the PDFCmd commands that might be a part of the command stream in
  * a PDFPage. They probably should be inner classes of PDFPage instead of separate non-public
  * classes.
  *
  * @author Mike Wessler
  */
  public class PDFPage {
      /**
      * the array of commands. The length of this array will always
      * be greater than or equal to the actual number of commands.
      */
      private final List<PDFCmd> commands;
      /**
      * whether this page has been finished. If true, there will be no
      * more commands added to the cmds list.
      */
      private boolean finished = false;
      /** the page number used to find this page */
      private final int pageNumber;
      /** the bounding box of the page, in page coordinates */
      private final Rectangle2D bbox;
      /** the rotation of this page, in degrees */
      private final int rotation;
      /**
      * a map from image info (width, height, clip) to a soft reference to the
      * rendered image
      */
      private final Cache cache;
      /** a map from image info to weak references to parsers that are active */
      public Map<ImageInfo, WeakReference<?>> renderers;
      /** List of annotations for this page */
      private List<PDFAnnotation> annots;
  
      /**
      * create a PDFPage with dimensions in bbox and rotation.
      */
      public PDFPage(Rectangle2D bbox, int rotation) {
          this(-1, bbox, rotation, null);
      }
  
      /**
      * create a PDFPage with dimensions in bbox and rotation.
      */
      public PDFPage(int pageNumber, Rectangle2D bbox, int rotation, Cache cache) {
          this.pageNumber = pageNumber;
          this.cache = cache;
          if (bbox == null) {
              bbox = new Rectangle2D.Float(0, 0, 1, 1);
          }
          rotation = rotation % 360; // less than a full turn
          if (rotation < 0) {
              rotation += 360;
          }
          rotation = rotation / 90; // for page rotation use only multiples of 90 degrees 
          rotation = rotation * 90; // 0, 90, 180, 270
          this.rotation = rotation;
          if (rotation == 90 || rotation == 270) {
              bbox = new Rectangle2D.Double(bbox.getX(), bbox.getY(), bbox.getHeight(), bbox.getWidth());
          }
          this.bbox = bbox;
          // initialize the cache of images and parsers
          this.renderers = Collections.synchronizedMap(new HashMap<ImageInfo, WeakReference<?>>());
          // initialize the list of commands
          this.commands = Collections.synchronizedList(new ArrayList<PDFCmd>(250));
          // corresponding pop in PDFParser -> setStatus
          this.addPush();
      }
  
      /**
      * Get the width and height of this image in the correct aspect ratio.
      * The image returned will have at least one of the width and
      * height values identical to those requested. The other
      * dimension may be smaller, so as to keep the aspect ratio
      * the same as in the original page.
      *
      * @param width
      * the maximum width of the image
      * @param height
      * the maximum height of the image
      * @param clip
      * the region in <b>page space</b> of the page to
      * display. It may be null, in which the page's defined crop box
      * will be used.
      */
      public Dimension getUnstretchedSize(int width, int height, Rectangle2D clip) {
          if (clip == null) {
              clip = this.bbox;
          } else {
              if (getRotation() == 90 || getRotation() == 270) {
                  clip = new Rectangle2D.Double(clip.getX(), clip.getY(), clip.getHeight(), clip.getWidth());
              }
          }
          double ratio = clip.getHeight() / clip.getWidth();
          double askratio = (double) height / (double) width;
          if (askratio > ratio) {
              // asked for something too high
              height = (int) (width * ratio + 0.5);
          } else {
              // asked for something too wide
              width = (int) (height / ratio + 0.5);
          }
          return new Dimension(width, height);
      }
  
      /**
      * Get an image producer which can be used to draw the image
      * represented by this PDFPage. The ImageProducer is guaranteed to
      * stay in sync with the PDFPage as commands are added to it.
      *
      * The image will contain the section of the page specified by the clip,
      * scaled to fit in the area given by width and height.
      *
      * @param width
      * the width of the image to be produced
      * @param height
      * the height of the image to be produced
      * @param clip
      * the region in <b>page space</b> of the entire page to
      * display
      * @param observer
      * an image observer who will be notified when the
      * image changes, or null
      * @return an Image that contains the PDF data
      */
      public Image getImage(int width, int height, Rectangle2D clip, ImageObserver observer) {
          return getImage(width, height, clip, observer, true, false);
      }
  
      /**
      * Get an image producer which can be used to draw the image
      * represented by this PDFPage. The ImageProducer is guaranteed to
      * stay in sync with the PDFPage as commands are added to it.
      *
      * The image will contain the section of the page specified by the clip,
      * scaled to fit in the area given by width and height.
      *
      * @param width
      * the width of the image to be produced
      * @param height
      * the height of the image to be produced
      * @param clip
      * the region in <b>page space</b> of the entire page to
      * display
      * @param observer
      * an image observer who will be notified when the
      * image changes, or null
      * @param drawbg
      * if true, put a white background on the image. If not,
      * draw no color (alpha 0) for the background.
      * @param wait
      * if true, do not return until this image is fully rendered.
      * @return an Image that contains the PDF data
      */
      public Image getImage(int width, int height, Rectangle2D clip, ImageObserver observer, boolean drawbg, boolean wait) {
          // see if we already have this image
          BufferedImage image = null;
          PDFRenderer renderer = null;
          ImageInfo info = new ImageInfo(width, height, clip, null);
          if (this.cache != null) {
              image = this.cache.getImage(this, info);
              renderer = this.cache.getImageRenderer(this, info);
          }
          // not in the cache, so create it
          if (image == null) {
              if (drawbg) {
                  info.bgColor = Color.WHITE;
              }
              image = new RefImage(info.width, info.height, BufferedImage.TYPE_INT_ARGB);
              renderer = new PDFRenderer(this, info, image);
              if (this.cache != null) {
                  this.cache.addImage(this, info, image, renderer);
              }
              this.renderers.put(info, new WeakReference<PDFRenderer>(renderer));
          }
          // the renderer may be null if we are getting this image from the
          // cache and rendering has completed.
          if (renderer != null) {
              if (observer != null) {
                  renderer.addObserver(observer);
              }
              
  			if (!renderer.isFinished()) {
  				renderer.go(wait);
  				if (renderer.getStatus() == Watchable.ERROR) {
  					PDFDebugger.debug("Error during reading image!");
  				}
  			}
          }
          // return the image
          return image;
      }
  
      /**
      * get the page number used to lookup this page
      *
      * @return the page number
      */
      public int getPageNumber() {
          return this.pageNumber;
      }
  
      /**
      * get the aspect ratio of the correctly oriented page.
      *
      * @return the width/height aspect ratio of the page
      */
      public float getAspectRatio() {
          return getWidth() / getHeight();
      }
  
      /**
      * get the bounding box of the page, before any rotation.
      */
      public Rectangle2D getBBox() {
          return this.bbox;
      }
  
      /**
      * get the width of this page, after rotation
      */
      public float getWidth() {
          return (float) this.bbox.getWidth();
      }
  
      /**
      * get the height of this page, after rotation
      */
      public float getHeight() {
          return (float) this.bbox.getHeight();
      }
  
      /**
      * get the rotation of this image
      */
      public int getRotation() {
          return this.rotation;
      }
  
      /**
      * Get the initial transform to map from a specified clip rectangle in
      * pdf coordinates to an image of the specfied width and
      * height in device coordinates
      *
      * @param width
      * the width of the image
      * @param height
      * the height of the image
      * @param clip
      * the desired clip rectangle (in PDF space) or null to use
      * the page's bounding box
      */
      public AffineTransform getInitialTransform(int width, int height, Rectangle2D clip) {
          AffineTransform at = new AffineTransform();
          switch (getRotation()) {
          case 0:
              at = new AffineTransform(1, 0, 0, -1, 0, height);
              break;
          case 90:
              at = new AffineTransform(0, 1, 1, 0, 0, 0);
              break;
          case 180:
              at = new AffineTransform(-1, 0, 0, 1, width, 0);
              break;
          case 270:
  