FOP-3112: Rotate annotations

git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop-pdf-images/trunk@1906240 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/java/org/apache/fop/render/pdf/pdfbox/PDFBoxAdapter.java b/src/java/org/apache/fop/render/pdf/pdfbox/PDFBoxAdapter.java
index d380267..6cc06d2 100644
--- a/src/java/org/apache/fop/render/pdf/pdfbox/PDFBoxAdapter.java
+++ b/src/java/org/apache/fop/render/pdf/pdfbox/PDFBoxAdapter.java
@@ -182,7 +182,7 @@
         throws IOException {
         COSDictionary sourcePageResources = getResources(page);
         uniqueName = new UniqueName(key, sourcePageResources, pdfDoc.isFormXObjectEnabled());
-        handleAnnotations(sourceDoc, page, atdoc);
+        handleAnnotations(sourceDoc, page, atdoc, pos);
         if (pageNumbers.containsKey(targetPage.getPageIndex())) {
             pageNumbers.get(targetPage.getPageIndex()).set(0, targetPage.makeReference());
         }
@@ -458,7 +458,8 @@
          */
     }
 
-    private void handleAnnotations(PDDocument sourceDoc, PDPage page, AffineTransform at) throws IOException {
+    private void handleAnnotations(PDDocument sourceDoc, PDPage page, AffineTransform at, Rectangle pos)
+        throws IOException {
         PDDocumentCatalog srcCatalog = sourceDoc.getDocumentCatalog();
         PDAcroForm srcAcroForm = srcCatalog.getAcroForm();
         List pageAnnotations = page.getAnnotations();
@@ -466,7 +467,7 @@
             return;
         }
 
-        PDFBoxAdapterUtil.moveAnnotations(page, pageAnnotations, at);
+        PDFBoxAdapterUtil.moveAnnotations(page, pageAnnotations, at, pos);
 
         //Pseudo-cache the target page in place of the original source page.
         //This essentially replaces the original page reference with the target page.
diff --git a/src/java/org/apache/fop/render/pdf/pdfbox/PDFBoxAdapterUtil.java b/src/java/org/apache/fop/render/pdf/pdfbox/PDFBoxAdapterUtil.java
index fdeffc6..b397a7c 100644
--- a/src/java/org/apache/fop/render/pdf/pdfbox/PDFBoxAdapterUtil.java
+++ b/src/java/org/apache/fop/render/pdf/pdfbox/PDFBoxAdapterUtil.java
@@ -18,7 +18,9 @@
 /* $Id$ */
 package org.apache.fop.render.pdf.pdfbox;
 
+import java.awt.Rectangle;
 import java.awt.geom.AffineTransform;
+import java.awt.geom.Rectangle2D;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
@@ -36,6 +38,8 @@
 import org.apache.pdfbox.pdmodel.PDPage;
 import org.apache.pdfbox.pdmodel.common.PDRectangle;
 import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream;
 
 import org.apache.fop.pdf.PDFArray;
 import org.apache.fop.pdf.PDFDictionary;
@@ -164,12 +168,12 @@
         }
     }
 
-    protected static void moveAnnotations(PDPage page, List pageAnnotations, AffineTransform at) {
+    protected static void moveAnnotations(PDPage page, List pageAnnotations, AffineTransform at, Rectangle pos) {
         PDRectangle mediaBox = page.getMediaBox();
         PDRectangle cropBox = page.getCropBox();
         PDRectangle viewBox = cropBox != null ? cropBox : mediaBox;
         for (Object obj : pageAnnotations) {
-            PDAnnotation annot = (PDAnnotation)obj;
+            PDAnnotation annot = (PDAnnotation) obj;
             PDRectangle rect = annot.getRectangle();
             float translateX = (float) (at.getTranslateX() - viewBox.getLowerLeftX());
             float translateY = (float) (at.getTranslateY() - viewBox.getLowerLeftY());
@@ -178,18 +182,43 @@
                 rect.setLowerLeftX(rect.getLowerLeftX() + translateX);
                 rect.setUpperRightY(rect.getUpperRightY() + translateY);
                 rect.setLowerLeftY(rect.getLowerLeftY() + translateY);
+                int rotation = PDFUtil.getNormalizedRotation(page);
+                if (rotation > 0) {
+                    AffineTransform transform = AffineTransform.getTranslateInstance(translateX, translateY);
+                    float height = (float)pos.getHeight() / 1000f;
+                    rotateStream(transform, rotation, height, annot);
+                    transform.translate(-translateX, -translateY);
+                    rect = applyTransform(rect, transform);
+                }
                 annot.setRectangle(rect);
             }
-//            COSArray vertices = (COSArray) annot.getCOSObject().getDictionaryObject("Vertices");
-//            if (vertices != null) {
-//                Iterator iter = vertices.iterator();
-//                while (iter.hasNext()) {
-//                    COSFloat x = (COSFloat) iter.next();
-//                    COSFloat y = (COSFloat) iter.next();
-//                    x.setValue(x.floatValue() + translateX);
-//                    y.setValue(y.floatValue() + translateY);
-//                }
-//            }
         }
     }
+
+    private static void rotateStream(AffineTransform transform, int rotation, float height, PDAnnotation annot) {
+        transform.rotate(Math.toRadians(-rotation));
+        transform.translate(-height, 0);
+        COSDictionary mkDict = (COSDictionary) annot.getCOSObject().getDictionaryObject(COSName.MK);
+        if (mkDict != null) {
+            mkDict.removeItem(COSName.R);
+        }
+        PDAppearanceDictionary appearance = annot.getAppearance();
+        if (appearance != null) {
+            for (PDAppearanceStream stream : appearance.getNormalAppearance().getSubDictionary().values()) {
+                stream.setMatrix(new AffineTransform());
+            }
+            for (PDAppearanceStream stream : appearance.getDownAppearance().getSubDictionary().values()) {
+                stream.setMatrix(new AffineTransform());
+            }
+        }
+    }
+
+    private static PDRectangle applyTransform(PDRectangle rect, AffineTransform apAt) {
+        Rectangle2D.Float rectangle = new Rectangle2D.Float();
+        rectangle.setRect(rect.getLowerLeftX(), rect.getLowerLeftY(), rect.getWidth(), rect.getHeight());
+        Rectangle2D rectangleT = apAt.createTransformedShape(rectangle).getBounds2D();
+        rect = new PDRectangle((float)rectangleT.getX(), (float)rectangleT.getY(),
+                (float)rectangleT.getWidth(), (float)rectangleT.getHeight());
+        return rect;
+    }
 }
diff --git a/test/java/org/apache/fop/render/pdf/PDFBoxAdapterTestCase.java b/test/java/org/apache/fop/render/pdf/PDFBoxAdapterTestCase.java
index 1c2157b..1c6f5a4 100644
--- a/test/java/org/apache/fop/render/pdf/PDFBoxAdapterTestCase.java
+++ b/test/java/org/apache/fop/render/pdf/PDFBoxAdapterTestCase.java
@@ -131,6 +131,7 @@
     protected static final String TYPE0CFF = "type0cff.pdf";
     protected static final String ACCESSIBLERADIOBUTTONS = "accessibleradiobuttons.pdf";
     protected static final String PATTERN = "pattern.pdf";
+    protected static final String FORMROTATED = "formrotated.pdf";
 
     private static PDFPage getPDFPage(PDFDocument doc) {
         final Rectangle2D r = new Rectangle2D.Double();
@@ -762,4 +763,13 @@
         }
         Assert.assertEquals(compositeList, Arrays.asList(18, 19, 39, 42, 62, 63, 29));
     }
+
+    @Test
+    public void testFormRotated() throws IOException {
+        PDFDocument pdfdoc = new PDFDocument("");
+        loadPage(pdfdoc, FORMROTATED);
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        pdfdoc.output(bos);
+        Assert.assertFalse(bos.toString("UTF-8").contains("/R 90"));
+    }
 }
diff --git a/test/resources/org/apache/fop/render/pdf/formrotated.pdf b/test/resources/org/apache/fop/render/pdf/formrotated.pdf
new file mode 100755
index 0000000..5ef173b
--- /dev/null
+++ b/test/resources/org/apache/fop/render/pdf/formrotated.pdf
Binary files differ