You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by le...@apache.org on 2024/11/12 07:14:23 UTC

svn commit: r1921857 - in /pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel: common/PDRectangle.java graphics/state/PDGraphicsState.java

Author: lehmi
Date: Tue Nov 12 07:14:23 2024
New Revision: 1921857

URL: http://svn.apache.org/viewvc?rev=1921857&view=rev
Log:
PDFBOX-4718: optimize intersection of clipping paths

Modified:
    pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/common/PDRectangle.java
    pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/state/PDGraphicsState.java

Modified: pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/common/PDRectangle.java
URL: http://svn.apache.org/viewvc/pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/common/PDRectangle.java?rev=1921857&r1=1921856&r2=1921857&view=diff
==============================================================================
--- pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/common/PDRectangle.java (original)
+++ pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/common/PDRectangle.java Tue Nov 12 07:14:23 2024
@@ -133,6 +133,14 @@ public class PDRectangle implements COSO
     public PDRectangle( COSArray array )
     {
         float[] values = Arrays.copyOf(array.toFloatArray(), 4);
+        // replace huge values, most likely those are invalid due to a malformed pdf
+        for (int i = 0; i < values.length; i++)
+        {
+            if (Math.abs(values[i]) > Integer.MAX_VALUE)
+            {
+                values[i] = values[i] > 0 ? Integer.MAX_VALUE : -Integer.MAX_VALUE;
+            }
+        }
         rectArray = new COSArray();
         // we have to start with the lower left corner
         rectArray.add( new COSFloat( Math.min(values[0],values[2] )) );

Modified: pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/state/PDGraphicsState.java
URL: http://svn.apache.org/viewvc/pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/state/PDGraphicsState.java?rev=1921857&r1=1921856&r2=1921857&view=diff
==============================================================================
--- pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/state/PDGraphicsState.java (original)
+++ pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/state/PDGraphicsState.java Tue Nov 12 07:14:23 2024
@@ -21,13 +21,10 @@ import java.awt.Composite;
 import java.awt.geom.Area;
 import java.awt.geom.GeneralPath;
 import java.awt.geom.Path2D;
+import java.awt.geom.Rectangle2D;
 import java.util.ArrayList;
-import java.util.IdentityHashMap;
 import java.util.List;
-import java.util.Map;
-
 import org.apache.pdfbox.cos.COSBase;
-
 import org.apache.pdfbox.pdmodel.common.PDRectangle;
 import org.apache.pdfbox.pdmodel.graphics.PDLineDashPattern;
 import org.apache.pdfbox.pdmodel.graphics.blend.BlendComposite;
@@ -46,7 +43,7 @@ public class PDGraphicsState implements
 {
     private boolean isClippingPathDirty;
     private List<Path2D> clippingPaths = new ArrayList<Path2D>(1);
-    private Map<Path2D, Area> clippingCache = new IdentityHashMap<Path2D, Area>();
+    private Area clippingPathCache = null;
     private Matrix currentTransformationMatrix = new Matrix();
     private PDColor strokingColor = PDDeviceGray.INSTANCE.getInitialColor();
     private PDColor nonStrokingColor = PDDeviceGray.INSTANCE.getInitialColor();
@@ -507,7 +504,7 @@ public class PDGraphicsState implements
             clone.nonStrokingColor = nonStrokingColor; // immutable
             clone.lineDashPattern = lineDashPattern; // immutable
             clone.clippingPaths = clippingPaths; // not cloned, see intersectClippingPath
-            clone.clippingCache = clippingCache;
+            clone.clippingPathCache = clippingPathCache;
             clone.isClippingPathDirty = false;
             clone.textLineMatrix = textLineMatrix == null ? null : textLineMatrix.clone();
             clone.textMatrix = textMatrix == null ? null : textMatrix.clone();
@@ -616,12 +613,12 @@ public class PDGraphicsState implements
         {
             // shallow copy
             clippingPaths = new ArrayList<Path2D>(clippingPaths);
-
             isClippingPathDirty = true;
         }
-
         // add path to current clipping paths, combined later (see getCurrentClippingPath)
         clippingPaths.add(clonePath ? (Path2D) path.clone() : path);
+        // clear cache
+        clippingPathCache = null;
     }
 
     /**
@@ -641,30 +638,34 @@ public class PDGraphicsState implements
      */
     public Area getCurrentClippingPath()
     {
+        // If there is just a single clipping path, no intersections are needed.
         if (clippingPaths.size() == 1)
         {
-            // If there is just a single clipping path, no intersections are needed.
-            Path2D path = clippingPaths.get(0);
-            Area area = clippingCache.get(path);
-            if (area == null)
+            if (clippingPathCache == null)
             {
-                area = new Area(path);
-                clippingCache.put(path, area);
+                clippingPathCache = new Area(clippingPaths.get(0));
             }
-            return area;
+            return clippingPathCache;
         }
-        // If there are multiple clipping paths, combine them to a single area.
-        Area clippingArea = new Area();
-        clippingArea.add(new Area(clippingPaths.get(0)));
+        // calculate the intersected overall bounding box for all clipping paths
+        Rectangle2D boundingBox = clippingPaths.get(0).getBounds2D();
         for (int i = 1; i < clippingPaths.size(); i++)
         {
-            clippingArea.intersect(new Area(clippingPaths.get(i)));
+            Rectangle2D.intersect(boundingBox, clippingPaths.get(i).getBounds2D(), boundingBox);
+        }
+        // use the overall bounding box as starting area
+        Area clippingArea = new Area(boundingBox);
+        // combine all clipping paths to a single area
+        for (int i = 0; i < clippingPaths.size(); i++)
+        {
+            Area nextArea = new Area(clippingPaths.get(i));
+            clippingArea.intersect(nextArea);
+            nextArea.reset();
         }
-        // Replace the list of individual clipping paths with the intersection, and add it to the cache.
-        Path2D newPath = new Path2D.Double(clippingArea);
+        clippingPathCache = clippingArea;
+        // Replace the list of individual clipping paths with the intersection
         clippingPaths = new ArrayList<Path2D>(1);
-        clippingPaths.add(newPath);
-        clippingCache.put(newPath, clippingArea);
+        clippingPaths.add(new Path2D.Double(clippingArea));
         return clippingArea;
     }