Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions openpdf-core/src/main/java/org/openpdf/text/Image.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,15 @@
import org.openpdf.text.pdf.PdfObject;
import org.openpdf.text.pdf.PdfReader;
import org.openpdf.text.pdf.PdfStream;
import org.openpdf.text.pdf.PdfString;
import org.openpdf.text.pdf.PdfTemplate;
import org.openpdf.text.pdf.PdfWriter;
import org.openpdf.text.pdf.codec.CCITTG4Encoder;
import java.awt.Graphics2D;
import java.awt.color.ICC_Profile;
import java.awt.image.BufferedImage;
import java.awt.image.IndexColorModel;
import java.awt.image.WritableRaster;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
Expand Down Expand Up @@ -824,6 +827,71 @@ public static Image getInstance(java.awt.Image image, java.awt.Color color,
if (bi.getType() == BufferedImage.TYPE_BYTE_BINARY && bi.getColorModel().getNumColorComponents() <= 2) {
forceBW = true;
}

// Handle indexed color images
if (bi.getColorModel() instanceof IndexColorModel && !forceBW) {
IndexColorModel icm = (IndexColorModel) bi.getColorModel();
int mapSize = icm.getMapSize();
int bitsPerPixel = icm.getPixelSize();

// Ensure bits per pixel is valid (1, 2, 4, or 8)
// For PDF indexed images, bpc should be the bits needed to index the palette
if (bitsPerPixel > 8 || bitsPerPixel == 0) {
bitsPerPixel = 8;
} else if (bitsPerPixel > 4) {
bitsPerPixel = 8;
} else if (bitsPerPixel > 2) {
bitsPerPixel = 4;
} else if (bitsPerPixel > 1) {
bitsPerPixel = 2;
} else {
bitsPerPixel = 1;
}

// Extract palette data
byte[] reds = new byte[mapSize];
byte[] greens = new byte[mapSize];
byte[] blues = new byte[mapSize];
icm.getReds(reds);
icm.getGreens(greens);
icm.getBlues(blues);

// Build palette as RGB byte array
byte[] palette = new byte[mapSize * 3];
for (int i = 0; i < mapSize; i++) {
palette[i * 3] = reds[i];
palette[i * 3 + 1] = greens[i];
palette[i * 3 + 2] = blues[i];
}

// Extract pixel indices
int width = bi.getWidth();
int height = bi.getHeight();
byte[] pixelData = new byte[width * height];

WritableRaster raster = bi.getRaster();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
pixelData[y * width + x] = (byte) raster.getSample(x, y, 0);
}
}

// Create indexed image with palette
Image img = Image.getInstance(width, height, 1, bitsPerPixel, pixelData);

// Set up indexed colorspace: [/Indexed /DeviceRGB maxIndex palette]
PdfArray indexed = new PdfArray();
indexed.add(PdfName.INDEXED);
indexed.add(PdfName.DEVICERGB);
indexed.add(new PdfNumber(mapSize - 1));
indexed.add(new PdfString(palette));

PdfDictionary additional = new PdfDictionary();
additional.put(PdfName.COLORSPACE, indexed);
img.setAdditional(additional);

return img;
}
}

java.awt.image.PixelGrabber pg = new java.awt.image.PixelGrabber(image,
Expand Down
55 changes: 55 additions & 0 deletions openpdf-core/src/test/java/org/openpdf/text/ImageTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,59 @@ private byte[] readFileBytes() throws IOException {
return bytes;
}

@Test
void shouldDetectIndexedColorGif() throws Exception {
// Load H.gif which is an indexed color GIF
String fileName = "src/test/resources/H.gif";
Image image = Image.getInstance(fileName);

assertNotNull(image);
// colorspace should be 1 for indexed images (not 3 for RGB)
assertThat(image.getColorspace()).isEqualTo(1);

// Verify that additional colorspace info is set for indexed images
assertThat(image.getAdditional()).isNotNull();
assertThat(image.getAdditional().get(org.openpdf.text.pdf.PdfName.COLORSPACE)).isNotNull();
}

@Test
void shouldDetectIndexedColorFromBufferedImage() throws Exception {
// Create an indexed color BufferedImage programmatically
int width = 10;
int height = 10;

// Create a simple 4-color palette (red, green, blue, black)
byte[] reds = {(byte)255, 0, 0, 0};
byte[] greens = {0, (byte)255, 0, 0};
byte[] blues = {0, 0, (byte)255, 0};

java.awt.image.IndexColorModel colorModel = new java.awt.image.IndexColorModel(
2, // 2 bits per pixel (4 colors)
4, // 4 colors in palette
reds, greens, blues
);

java.awt.image.BufferedImage bufferedImage = new java.awt.image.BufferedImage(
width, height, java.awt.image.BufferedImage.TYPE_BYTE_INDEXED, colorModel
);

// Fill with some pattern
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
bufferedImage.getRaster().setSample(x, y, 0, (x + y) % 4);
}
}

// Convert to Image
Image image = Image.getInstance(bufferedImage, null);

assertNotNull(image);
// Should be indexed (colorspace = 1), not RGB (colorspace = 3)
assertThat(image.getColorspace()).isEqualTo(1);

// Verify that additional colorspace info is set
assertThat(image.getAdditional()).isNotNull();
assertThat(image.getAdditional().get(org.openpdf.text.pdf.PdfName.COLORSPACE)).isNotNull();
}

}
Loading