Author: tilman
Date: Tue Jul 15 15:11:37 2025
New Revision: 1927245

URL: http://svn.apache.org/viewvc?rev=1927245&view=rev
Log:
PDFBOX-6032: add a fallback functional interface, as suggested by Ilgoo Kim

Added:
    
pdfbox/branches/3.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/CustomFactory.java
   (with props)
Modified:
    
pdfbox/branches/3.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/PDImageXObject.java
    
pdfbox/branches/3.0/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/graphics/image/PDImageXObjectTest.java

Added: 
pdfbox/branches/3.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/CustomFactory.java
URL: 
http://svn.apache.org/viewvc/pdfbox/branches/3.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/CustomFactory.java?rev=1927245&view=auto
==============================================================================
--- 
pdfbox/branches/3.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/CustomFactory.java
 (added)
+++ 
pdfbox/branches/3.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/CustomFactory.java
 Tue Jul 15 15:11:37 2025
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pdfbox.pdmodel.graphics.image;
+
+import java.io.IOException;
+
+import org.apache.pdfbox.pdmodel.PDDocument;
+
+/**
+ * A functional interface that allows users to define a custom strategy for 
converting image data as
+ * a byte array into a {@link PDImageXObject}.
+ */
+@FunctionalInterface
+public interface CustomFactory
+{
+
+    /**
+     * Creates a {@link PDImageXObject} from the given image byte array and 
document context.
+     *
+     * @param document the document that shall use this PDImageXObject.
+     * @param byteArray the image data as a byte array
+     * @return a PDImageXObject.
+     * @throws IOException if there is an error when creating the 
PDImageXObject.
+     */
+    PDImageXObject createFromByteArray(PDDocument document, byte[] byteArray) 
throws IOException;
+}

Propchange: 
pdfbox/branches/3.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/CustomFactory.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: 
pdfbox/branches/3.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/PDImageXObject.java
URL: 
http://svn.apache.org/viewvc/pdfbox/branches/3.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/PDImageXObject.java?rev=1927245&r1=1927244&r2=1927245&view=diff
==============================================================================
--- 
pdfbox/branches/3.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/PDImageXObject.java
 (original)
+++ 
pdfbox/branches/3.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/PDImageXObject.java
 Tue Jul 15 15:11:37 2025
@@ -344,6 +344,27 @@ public final class PDImageXObject extend
      */
     public static PDImageXObject createFromByteArray(PDDocument document, 
byte[] byteArray, String name) throws IOException
     {
+        return createFromByteArray(document, byteArray, name, null);
+    }
+
+    /**
+     * Create a PDImageXObject from an image byte array. This overloaded 
version allows providing 
+     * a custom factory to handle specific image formats, such as BMP and GIF, 
or to act as a 
+     * fallback strategy when the default converters (e.g., for PNG or TIFF) 
fail.
+     *
+     * @param document the document that shall use this PDImageXObject.
+     * @param byteArray bytes from an image file.
+     * @param name name of image file for exception messages, can be null.
+     * @param customFactory optional factory used to handle BMP, GIF, or 
fallback cases 
+     *                       (e.g., for PNG or TIFF). If {@code null}, this 
method delegates to
+     *                       {@link #createFromByteArray(PDDocument, byte[], 
String)}.
+     * @return a PDImageXObject.
+     * @throws IOException if there is an error when reading the file or 
creating the
+     * PDImageXObject.
+     * @throws IllegalArgumentException if the image type is not supported.
+     */
+    public static PDImageXObject createFromByteArray(PDDocument document, 
byte[] byteArray, String name, CustomFactory customFactory) throws IOException
+    {
         FileType fileType = FileTypeDetector.detectFileType(byteArray);
         if (fileType == null)
         {
@@ -380,6 +401,11 @@ public final class PDImageXObject extend
         }
         if (fileType == FileType.BMP || fileType == FileType.GIF || fileType 
== FileType.PNG)
         {
+            if (customFactory != null)
+            {
+                return customFactory.createFromByteArray(document, byteArray);
+            }
+            
             ByteArrayInputStream bais = new ByteArrayInputStream(byteArray);
             BufferedImage bim = ImageIO.read(bais);
             return LosslessFactory.createFromImage(document, bim);

Modified: 
pdfbox/branches/3.0/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/graphics/image/PDImageXObjectTest.java
URL: 
http://svn.apache.org/viewvc/pdfbox/branches/3.0/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/graphics/image/PDImageXObjectTest.java?rev=1927245&r1=1927244&r2=1927245&view=diff
==============================================================================
--- 
pdfbox/branches/3.0/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/graphics/image/PDImageXObjectTest.java
 (original)
+++ 
pdfbox/branches/3.0/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/graphics/image/PDImageXObjectTest.java
 Tue Jul 15 15:11:37 2025
@@ -17,12 +17,20 @@
 package org.apache.pdfbox.pdmodel.graphics.image;
 
 
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.WritableRaster;
+
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
-import java.awt.image.BufferedImage;
+import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.net.URISyntaxException;
 import javax.imageio.ImageIO;
 import org.apache.pdfbox.io.IOUtils;
@@ -124,6 +132,19 @@ class PDImageXObjectTest
         testCompareCreatedFromByteArrayWithCreatedByLosslessFactory("lzw.tif");
     }
 
+    /**
+     * Test of createFromByteArray method with CustomFactory parameter, of 
class PDImageXObject.
+     * @throws java.io.IOException
+     * @throws java.net.URISyntaxException
+     */
+    @Test
+    void testCreateFromByteArrayWithCustomFactory() throws IOException, 
URISyntaxException
+    {
+        testCompareCreatedFromByteArrayWithCreatedByCustomFactory("gif.gif");
+        
testCompareCreatedFromByteArrayWithCreatedByCustomFactory("gif-1bit-transparent.gif");
+        testCompareCreatedFromByteArrayWithCreatedByCustomFactory("lzw.tif");
+    }
+
     private void 
testCompareCreatedFileByExtensionWithCreatedByLosslessFactory(String filename)
             throws IOException, URISyntaxException
     {
@@ -314,6 +335,29 @@ class PDImageXObjectTest
         }
     }
 
+    private void 
testCompareCreatedFromByteArrayWithCreatedByCustomFactory(String filename)
+            throws IOException, URISyntaxException
+    {
+        try (PDDocument doc = new PDDocument())
+        {
+            File file = new 
File(PDImageXObjectTest.class.getResource(filename).toURI());
+            byte[] byteArray;
+            try (InputStream in = new FileInputStream(file))
+            {
+                byteArray = IOUtils.toByteArray(in);
+            }
+
+            CustomFactory customFactory = this::alphaFlattenedJPEGFactory;
+
+            PDImageXObject image = PDImageXObject.createFromByteArray(doc, 
byteArray, filename, customFactory);
+
+            PDImageXObject expectedImage = alphaFlattenedJPEGFactory(doc, 
byteArray);
+
+            assertEquals(expectedImage.getSuffix(), image.getSuffix());
+            checkIdentARGB(image.getImage(), expectedImage.getImage());
+        }
+    }
+
     private void checkIdentARGB(BufferedImage expectedImage, BufferedImage 
actualImage)
     {
         String errMsg = "";
@@ -333,5 +377,32 @@ class PDImageXObjectTest
                 assertEquals(expectedImage.getRGB(x, y), actualImage.getRGB(x, 
y), errMsg);
             }
         }
-    }    
+    }
+
+    private PDImageXObject alphaFlattenedJPEGFactory(PDDocument document, 
byte[] byteArray) throws IOException
+    {
+        ByteArrayInputStream bais = new ByteArrayInputStream(byteArray);
+        BufferedImage bim = ImageIO.read(bais);
+
+        if (bim.isAlphaPremultiplied()) {
+            ColorModel colorModel = bim.getColorModel();
+            WritableRaster raster = bim.copyData(null);
+            bim = new BufferedImage(colorModel, raster, false, null);
+        }
+
+        BufferedImage flattened = new BufferedImage(
+            bim.getWidth(),
+            bim.getHeight(),
+            BufferedImage.TYPE_INT_RGB
+        );
+
+        Graphics2D g = flattened.createGraphics();
+        g.setComposite(AlphaComposite.SrcOver);
+        g.setColor(Color.WHITE);
+        g.fillRect(0, 0, flattened.getWidth(), flattened.getHeight());
+        g.drawImage(bim, 0, 0, null);
+        g.dispose();
+
+        return JPEGFactory.createFromImage(document, flattened);
+    }
 }


Reply via email to