Author: tilman
Date: Tue Jul 15 14:47:19 2025
New Revision: 1927244
URL: http://svn.apache.org/viewvc?rev=1927244&view=rev
Log:
PDFBOX-6032: add a fallback functional interface, as suggested by Ilgoo Kim
Added:
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/CustomFactory.java
(with props)
Modified:
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/PDImageXObject.java
pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/graphics/image/PDImageXObjectTest.java
Added:
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/CustomFactory.java
URL:
http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/CustomFactory.java?rev=1927244&view=auto
==============================================================================
---
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/CustomFactory.java
(added)
+++
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/CustomFactory.java
Tue Jul 15 14:47:19 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/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/CustomFactory.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified:
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/PDImageXObject.java
URL:
http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/PDImageXObject.java?rev=1927244&r1=1927243&r2=1927244&view=diff
==============================================================================
---
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/PDImageXObject.java
(original)
+++
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/PDImageXObject.java
Tue Jul 15 14:47:19 2025
@@ -340,6 +340,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)
{
@@ -376,6 +397,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/trunk/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/graphics/image/PDImageXObjectTest.java
URL:
http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/graphics/image/PDImageXObjectTest.java?rev=1927244&r1=1927243&r2=1927244&view=diff
==============================================================================
---
pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/graphics/image/PDImageXObjectTest.java
(original)
+++
pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/graphics/image/PDImageXObjectTest.java
Tue Jul 15 14:47:19 2025
@@ -16,10 +16,16 @@
*/
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;
@@ -125,6 +131,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
{
@@ -318,6 +337,26 @@ class PDImageXObjectTest
}
}
+ private void
testCompareCreatedFromByteArrayWithCreatedByCustomFactory(String filename)
+ throws IOException, URISyntaxException
+ {
+ try (PDDocument doc = new PDDocument())
+ {
+ File file = new
File(PDImageXObjectTest.class.getResource(filename).toURI());
+ InputStream in = new FileInputStream(file);
+ byte[] byteArray = in.readAllBytes();
+
+ 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 = "";
@@ -337,5 +376,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);
+ }
}