Author: tilman
Date: Sat Aug  9 17:35:31 2025
New Revision: 1927718

Log:
PDFBOX-6049: introduce new adjustRotation option

Modified:
   pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/multipdf/Overlay.java
   pdfbox/trunk/tools/src/main/java/org/apache/pdfbox/tools/OverlayPDF.java

Modified: 
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/multipdf/Overlay.java
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/multipdf/Overlay.java   
Sat Aug  9 16:40:21 2025        (r1927717)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/multipdf/Overlay.java   
Sat Aug  9 17:35:31 2025        (r1927718)
@@ -67,6 +67,7 @@ public class Overlay implements Closeabl
     }
 
     private LayoutPage defaultOverlayPage;
+    private final Map<Integer,LayoutPage> rotatedDefaultOverlayPagesMap = new 
HashMap<>();
     private LayoutPage firstPageOverlayPage;
     private LayoutPage lastPageOverlayPage;
     private LayoutPage oddPageOverlayPage;
@@ -100,6 +101,7 @@ public class Overlay implements Closeabl
 
     private int numberOfOverlayPages = 0;
     private boolean useAllOverlayPages = false;
+    private boolean adjustRotation = false;
 
     /**
      * This will add overlays to a document.
@@ -204,6 +206,7 @@ public class Overlay implements Closeabl
         }
         openDocumentsSet.clear();
         specificPageOverlayLayoutPageMap.clear();
+        rotatedDefaultOverlayPagesMap.clear();
     }
 
     private void loadPDFs() throws IOException
@@ -288,9 +291,9 @@ public class Overlay implements Closeabl
         private final PDRectangle overlayMediaBox;
         private final COSStream overlayCOSStream;
         private final COSDictionary overlayResources;
-        private final short overlayRotation;
+        private int overlayRotation;
 
-        private LayoutPage(PDRectangle mediaBox, COSStream contentStream, 
COSDictionary resources, short rotation)
+        private LayoutPage(PDRectangle mediaBox, COSStream contentStream, 
COSDictionary resources, int rotation)
         {
             overlayMediaBox = mediaBox;
             overlayCOSStream = contentStream;
@@ -326,7 +329,7 @@ public class Overlay implements Closeabl
             resources = new PDResources();
         }
         return new LayoutPage(page.getMediaBox(), 
createCombinedContentStream(contents),
-                resources.getCOSObject(), (short) page.getRotation());
+                resources.getCOSObject(), page.getRotation());
     }
     
     private Map<Integer,LayoutPage> createPageOverlayLayoutPageMap(PDDocument 
doc) throws IOException
@@ -468,7 +471,7 @@ public class Overlay implements Closeabl
         array.add(createOverlayStream(page, layoutPage, formXObjectId));
     }
 
-    private LayoutPage getLayoutPage(int pageNumber, int numberOfPages)
+    private LayoutPage getLayoutPage(int pageNumber, int numberOfPages) throws 
IOException
     {
         LayoutPage layoutPage = null;
         if (!useAllOverlayPages && 
specificPageOverlayLayoutPageMap.containsKey(pageNumber))
@@ -494,6 +497,27 @@ public class Overlay implements Closeabl
         else if (defaultOverlayPage != null)
         {
             layoutPage = defaultOverlayPage;
+
+            if (adjustRotation)
+            {
+                // PDFBOX-6049: consider the rotation of the document page
+                // Note that this segment is only the second best solution to 
the problem. The best
+                // would be to make appropriate transforms in 
calculateAffineTransform()                
+                PDPage page = inputPDFDocument.getPage(pageNumber - 1);
+                if (page.getRotation() != 0)
+                {
+                    LayoutPage rotatedLayoutPage = 
rotatedDefaultOverlayPagesMap.get(page.getRotation());
+                    if (rotatedLayoutPage == null)
+                    {
+                        // createLayoutPage must be called because we can't 
reuse the COSStream
+                        rotatedLayoutPage = 
createLayoutPage(defaultOverlayDocument.getPage(0));
+                        int newRotation = (rotatedLayoutPage.overlayRotation - 
page.getRotation() + 360) % 360;
+                        rotatedLayoutPage.overlayRotation = newRotation;
+                        rotatedDefaultOverlayPagesMap.put(page.getRotation(), 
rotatedLayoutPage);
+                    }
+                    return rotatedLayoutPage;
+                }
+            }
         }
         else if (useAllOverlayPages)
         {
@@ -516,15 +540,15 @@ public class Overlay implements Closeabl
         {
             case 90:
                 at.translate(0, layoutPage.overlayMediaBox.getWidth());
-                at.rotate(Math.toRadians(-90));
+                at.quadrantRotate(3); // 270
                 break;
             case 180:
                 at.translate(layoutPage.overlayMediaBox.getWidth(), 
layoutPage.overlayMediaBox.getHeight());
-                at.rotate(Math.toRadians(-180));
+                at.quadrantRotate(2); // 180
                 break;
             case 270:
                 at.translate(layoutPage.overlayMediaBox.getHeight(), 0);
-                at.rotate(Math.toRadians(-270));
+                at.quadrantRotate(1); // 90
                 break;
             default:
                 break;
@@ -795,4 +819,23 @@ public class Overlay implements Closeabl
     {
         evenPageOverlayDocument = evenPageOverlayPDF;
     }
+
+    /**
+     * This sets whether the overlay is to be rotated according to the 
rotation of the pages of the
+     * source document. This may look weird if the content of the document is 
also rotated. So it's
+     * really a users decision to activate this option if the overlay appears 
rotated in some pages
+     * of the result document and this isn't wanted.
+     * <p>
+     * This setting will only apply to usage of the default overlay, because 
it is assumed that when
+     * using specific overlays for specific pages, it is known in advance what 
kind of input there
+     * is.
+     *
+     * @param adjustRotation if true, the overlay will always look the same 
when the result file is
+     * displayed on the screen. If false (default) then it will be rotated if 
the page is rotated.
+     */
+    public void setAdjustRotation(boolean adjustRotation)
+    {
+        this.adjustRotation = adjustRotation;
+
+    }
 }

Modified: 
pdfbox/trunk/tools/src/main/java/org/apache/pdfbox/tools/OverlayPDF.java
==============================================================================
--- pdfbox/trunk/tools/src/main/java/org/apache/pdfbox/tools/OverlayPDF.java    
Sat Aug  9 16:40:21 2025        (r1927717)
+++ pdfbox/trunk/tools/src/main/java/org/apache/pdfbox/tools/OverlayPDF.java    
Sat Aug  9 17:35:31 2025        (r1927718)
@@ -60,6 +60,9 @@ public final class OverlayPDF implements
     @Option(names = "-useAllPages", description = "overlay file used for 
overlay, all pages are used by simply repeating them")
     private File useAllPages;
 
+    @Option(names = "-adjustRotation", description = "adjust rotation for 
rotated source pages (applies only if default overlay file is used)")
+    private boolean adjustRotation = false;
+
     @Option(names = "-page", description = "overlay file used for the given 
page number, may occur more than once")    
     Map<Integer, String> specificPageOverlayFile = new HashMap<>();
 
@@ -140,6 +143,7 @@ public final class OverlayPDF implements
         {
             overlayer.setInputFile(infile.getAbsolutePath());
         }
+        overlayer.setAdjustRotation(adjustRotation);
 
 
         try (PDDocument result = overlayer.overlay(specificPageOverlayFile))

Reply via email to