Author: lehmi
Date: Sat Dec  6 12:34:23 2025
New Revision: 1930286

Log:
PDFBOX-5169: reduce the memory footprint by reusing the internal byte array 
instead of copying it

Modified:
   
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfwriter/compress/COSWriterObjectStream.java

Modified: 
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfwriter/compress/COSWriterObjectStream.java
==============================================================================
--- 
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfwriter/compress/COSWriterObjectStream.java
   Sat Dec  6 12:32:10 2025        (r1930285)
+++ 
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfwriter/compress/COSWriterObjectStream.java
   Sat Dec  6 12:34:23 2025        (r1930286)
@@ -113,22 +113,22 @@ public class COSWriterObjectStream
         stream.setInt(COSName.N, objectCount);
         // Prepare the compressible objects for writing.
         List<Long> objectNumbers = new ArrayList<>(objectCount);
-        List<byte[]> objectsBuffer = new ArrayList<>(objectCount);
+        List<ByteArrayOutputStream> objectsBuffer = new 
ArrayList<>(objectCount);
         for (int i = 0; i < objectCount; i++)
         {
-            try (ByteArrayOutputStream partialOutput = new 
ByteArrayOutputStream())
+            try (ByteArrayOutputStream partialOutput = new 
DirectAccessByteArrayOutputStream())
             {
                 objectNumbers.add(preparedKeys.get(i).getNumber());
                 COSBase base = preparedObjects.get(i);
                 writeObject(partialOutput, base, true);
-                objectsBuffer.add(partialOutput.toByteArray());
+                objectsBuffer.add(partialOutput);
             }
         }
 
         // Deduce the object stream byte offset map.
         byte[] offsetsMapBuffer;
         long nextObjectOffset = 0;
-        try (ByteArrayOutputStream partialOutput = new ByteArrayOutputStream())
+        try (ByteArrayOutputStream partialOutput = new 
DirectAccessByteArrayOutputStream())
         {
             for (int i = 0; i < objectNumbers.size(); i++)
             {
@@ -138,7 +138,7 @@ public class COSWriterObjectStream
                 partialOutput.write(
                         
String.valueOf(nextObjectOffset).getBytes(StandardCharsets.ISO_8859_1));
                 partialOutput.write(COSWriter.SPACE);
-                nextObjectOffset += objectsBuffer.get(i).length;
+                nextObjectOffset += objectsBuffer.get(i).size();
             }
             offsetsMapBuffer = partialOutput.toByteArray();
         }
@@ -148,9 +148,9 @@ public class COSWriterObjectStream
         {
             output.write(offsetsMapBuffer);
             stream.setInt(COSName.FIRST, offsetsMapBuffer.length);
-            for (byte[] rawObject : objectsBuffer)
+            for (ByteArrayOutputStream rawObject : objectsBuffer)
             {
-                output.write(rawObject);
+                output.write(rawObject.toByteArray());
             }
         }
         return stream;
@@ -384,7 +384,24 @@ public class COSWriterObjectStream
      */
     private void writeCOSNull(OutputStream output) throws IOException
     {
-        output.write("null".getBytes(StandardCharsets.ISO_8859_1));
+        output.write(COSNull.NULL_BYTES);
         output.write(COSWriter.SPACE);
     }
+
+    /**
+     * Reuse the underlying byte array instead of copying it.
+     * 
+     * This is a private class as reusing the byte array may have some 
unwanted side effects.
+     * 
+     */
+    private class DirectAccessByteArrayOutputStream extends 
ByteArrayOutputStream
+    {
+
+        @Override
+        public byte[] toByteArray()
+        {
+            return buf;
+        }
+    }
+
 }

Reply via email to