/**
   * Gets an instance of a CircleAnnotation that has valid Object Reference.
   *
   * @param library document library
   * @param rect bounding rectangle in user space
   * @return new CircleAnnotation Instance.
   */
  public static CircleAnnotation getInstance(Library library, Rectangle rect) {
    // state manager
    StateManager stateManager = library.getStateManager();

    // create a new entries to hold the annotation properties
    HashMap<Name, Object> entries = new HashMap<Name, Object>();
    // set default link annotation values.
    entries.put(Dictionary.TYPE_KEY, Annotation.TYPE_VALUE);
    entries.put(Dictionary.SUBTYPE_KEY, Annotation.SUBTYPE_CIRCLE);
    // coordinates
    if (rect != null) {
      entries.put(Annotation.RECTANGLE_KEY, PRectangle.getPRectangleVector(rect));
    } else {
      entries.put(Annotation.RECTANGLE_KEY, new Rectangle(10, 10, 50, 100));
    }

    // create the new instance
    CircleAnnotation circleAnnotation = new CircleAnnotation(library, entries);
    circleAnnotation.init();
    circleAnnotation.setPObjectReference(stateManager.getNewReferencNumber());
    circleAnnotation.setNew(true);

    // set default flags.
    circleAnnotation.setFlag(Annotation.FLAG_READ_ONLY, false);
    circleAnnotation.setFlag(Annotation.FLAG_NO_ROTATE, false);
    circleAnnotation.setFlag(Annotation.FLAG_NO_ZOOM, false);
    circleAnnotation.setFlag(Annotation.FLAG_PRINT, true);

    return circleAnnotation;
  }
  /** Resets the annotations appearance stream. */
  public void resetAppearanceStream(double dx, double dy, AffineTransform pageTransform) {

    matrix = new AffineTransform();
    shapes = new Shapes();

    // setup the AP stream.
    setModifiedDate(PDate.formatDateTime(new Date()));
    // refresh rectangle
    rectangle = getUserSpaceRectangle().getBounds();
    entries.put(Annotation.RECTANGLE_KEY, PRectangle.getPRectangleVector(rectangle));
    userSpaceRectangle =
        new Rectangle2D.Float(
            (float) rectangle.getX(), (float) rectangle.getY(),
            (float) rectangle.getWidth(), (float) rectangle.getHeight());

    int strokeWidth = (int) borderStyle.getStrokeWidth();
    Rectangle rectangleToDraw =
        new Rectangle(
            (int) rectangle.getX() + strokeWidth,
            (int) rectangle.getY() + strokeWidth,
            (int) rectangle.getWidth() - strokeWidth * 2,
            (int) rectangle.getHeight() - strokeWidth * 2);

    // setup the space for the AP content stream.
    AffineTransform af = new AffineTransform();
    af.scale(1, -1);
    af.translate(-this.bbox.getMinX(), -this.bbox.getMaxY());

    BasicStroke stroke;
    if (borderStyle.isStyleDashed()) {
      stroke =
          new BasicStroke(
              borderStyle.getStrokeWidth(),
              BasicStroke.CAP_BUTT,
              BasicStroke.JOIN_MITER,
              borderStyle.getStrokeWidth() * 2.0f,
              borderStyle.getDashArray(),
              0.0f);
    } else {
      stroke = new BasicStroke(borderStyle.getStrokeWidth());
    }

    Ellipse2D.Double circle =
        new Ellipse2D.Double(
            rectangleToDraw.getMinX(),
            rectangleToDraw.getMinY(),
            rectangleToDraw.getWidth(),
            rectangleToDraw.getHeight());

    shapes.add(new TransformDrawCmd(af));
    shapes.add(new StrokeDrawCmd(stroke));
    shapes.add(new ShapeDrawCmd(circle));
    if (isFillColor) {
      shapes.add(new ColorDrawCmd(fillColor));
      shapes.add(new FillDrawCmd());
    }
    if (borderStyle.getStrokeWidth() > 0) {
      shapes.add(new ColorDrawCmd(color));
      shapes.add(new DrawDrawCmd());
    }

    // update the appearance stream
    // create/update the appearance stream of the xObject.
    StateManager stateManager = library.getStateManager();
    Form form;
    if (hasAppearanceStream()) {
      form = (Form) getAppearanceStream();
      // else a stream, we won't support this for annotations.
    } else {
      // create a new xobject/form object
      HashMap formEntries = new HashMap();
      formEntries.put(Form.TYPE_KEY, Form.TYPE_VALUE);
      formEntries.put(Form.SUBTYPE_KEY, Form.SUB_TYPE_VALUE);
      form = new Form(library, formEntries, null);
      form.setPObjectReference(stateManager.getNewReferencNumber());
      library.addObject(form, form.getPObjectReference());
    }

    if (form != null) {
      Rectangle2D formBbox =
          new Rectangle2D.Float(0, 0, (float) bbox.getWidth(), (float) bbox.getHeight());
      form.setAppearance(shapes, matrix, formBbox);
      stateManager.addChange(new PObject(form, form.getPObjectReference()));
      // update the AP's stream bytes so contents can be written out
      form.setRawBytes(PostScriptEncoder.generatePostScript(shapes.getShapes()));
      HashMap appearanceRefs = new HashMap();
      appearanceRefs.put(APPEARANCE_STREAM_NORMAL_KEY, form.getPObjectReference());
      entries.put(APPEARANCE_STREAM_KEY, appearanceRefs);

      // compress the form object stream.
      if (compressAppearanceStream) {
        form.getEntries().put(Stream.FILTER_KEY, new Name("FlateDecode"));
      } else {
        form.getEntries().remove(Stream.FILTER_KEY);
      }
    }
  }