Skip to content

Commit 748c60e

Browse files
committed
Add support for more colorspaces for inline images
DEVSIX-8614
1 parent 3db6279 commit 748c60e

File tree

8 files changed

+79
-25
lines changed

8 files changed

+79
-25
lines changed

kernel/src/main/java/com/itextpdf/kernel/pdf/canvas/parser/util/InlineImageParsingUtils.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,10 @@ public InlineImageParseException(String message) {
141141
public static PdfStream parse(PdfCanvasParser ps, PdfDictionary colorSpaceDic) throws IOException {
142142
PdfDictionary inlineImageDict = parseDictionary(ps);
143143
byte[] samples = parseSamples(inlineImageDict, colorSpaceDic, ps);
144+
PdfName colorSpaceName = inlineImageDict.getAsName(PdfName.ColorSpace);
145+
if (colorSpaceDic != null && colorSpaceName != null) {
146+
inlineImageDict.put(PdfName.ColorSpace, colorSpaceDic.get(colorSpaceName));
147+
}
144148
PdfStream inlineImageAsStreamObject = new PdfStream(samples);
145149
inlineImageAsStreamObject.putAll(inlineImageDict);
146150
return inlineImageAsStreamObject;
@@ -187,7 +191,6 @@ static int getComponentsPerPixel(PdfName colorSpaceName, PdfDictionary colorSpac
187191
}
188192
}
189193
}
190-
191194
throw new InlineImageParseException(KernelExceptionMessageConstant.UNEXPECTED_COLOR_SPACE).setMessageParams(colorSpaceName);
192195
}
193196

kernel/src/main/java/com/itextpdf/kernel/pdf/xobject/ImagePdfBytesInfo.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,18 +56,18 @@ class ImagePdfBytesInfo {
5656
private byte[] icc;
5757
private int stride;
5858

59-
public ImagePdfBytesInfo(PdfImageXObject imageXObject) {
59+
ImagePdfBytesInfo(int width, int height, int bpc, PdfObject colorspace, PdfArray decode) {
6060
pngColorType = -1;
61-
bpc = imageXObject.getPdfObject().getAsNumber(PdfName.BitsPerComponent).intValue();
61+
this.bpc = bpc;
6262
pngBitDepth = bpc;
6363

6464
palette = null;
6565
icc = null;
6666
stride = 0;
67-
width = (int) imageXObject.getWidth();
68-
height = (int) imageXObject.getHeight();
69-
colorspace = imageXObject.getPdfObject().get(PdfName.ColorSpace);
70-
decode = imageXObject.getPdfObject().getAsArray(PdfName.Decode);
67+
this.width = width;
68+
this.height = height;
69+
this.colorspace = colorspace;
70+
this.decode = decode;
7171
findColorspace(colorspace, false);
7272
}
7373

kernel/src/main/java/com/itextpdf/kernel/pdf/xobject/PdfImageXObject.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ This file is part of the iText (R) project.
6868
*/
6969
public class PdfImageXObject extends PdfXObject {
7070

71-
7271
private float width;
7372
private float height;
7473
private boolean mask;
@@ -130,6 +129,7 @@ public float getHeight() {
130129
return height;
131130
}
132131

132+
133133
/**
134134
* To manually flush a {@code PdfObject} behind this wrapper, you have to ensure
135135
* that this object is added to the document, i.e. it has an indirect reference.
@@ -196,7 +196,10 @@ public byte[] getImageBytes(boolean decoded) {
196196
ImageType imageType = identifyImageType();
197197
if (imageType == ImageType.TIFF || imageType == ImageType.PNG) {
198198
try {
199-
bytes = new ImagePdfBytesInfo(this).decodeTiffAndPngBytes(bytes);
199+
bytes = new ImagePdfBytesInfo((int)width, (int)height,
200+
getPdfObject().getAsNumber(PdfName.BitsPerComponent).intValue(),
201+
getPdfObject().get(PdfName.ColorSpace),
202+
getPdfObject().getAsArray(PdfName.Decode)).decodeTiffAndPngBytes(bytes);
200203
} catch (IOException e) {
201204
throw new RuntimeException("IO exception in PdfImageXObject", e);
202205
}
@@ -235,9 +238,11 @@ public ImageType identifyImageType() {
235238
return ImageType.JPEG2000;
236239
}
237240
}
238-
239241
// None of the previous types match
240-
ImagePdfBytesInfo imageInfo = new ImagePdfBytesInfo(this);
242+
ImagePdfBytesInfo imageInfo = new ImagePdfBytesInfo((int)width, (int)height,
243+
getPdfObject().getAsNumber(PdfName.BitsPerComponent).intValue(),
244+
getPdfObject().get(PdfName.ColorSpace),
245+
getPdfObject().getAsArray(PdfName.Decode));
241246
if (imageInfo.getPngColorType() < 0) {
242247
return ImageType.TIFF;
243248
} else {

kernel/src/test/java/com/itextpdf/kernel/pdf/xobject/GetImageBytesTest.java

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@ This file is part of the iText (R) project.
2222
*/
2323
package com.itextpdf.kernel.pdf.xobject;
2424

25+
import com.itextpdf.commons.datastructures.Tuple2;
2526
import com.itextpdf.commons.utils.FileUtil;
2627
import com.itextpdf.commons.utils.MessageFormatUtil;
28+
import com.itextpdf.commons.utils.Pair;
2729
import com.itextpdf.io.codec.TIFFConstants;
2830
import com.itextpdf.io.codec.TIFFDirectory;
2931
import com.itextpdf.io.codec.TIFFField;
@@ -47,13 +49,15 @@ This file is part of the iText (R) project.
4749
import com.itextpdf.test.ExtendedITextTest;
4850
import com.itextpdf.test.TestUtil;
4951

52+
import java.io.FileOutputStream;
5053
import java.io.IOException;
5154
import java.io.InputStream;
5255
import java.io.OutputStream;
5356
import java.nio.file.Files;
5457
import java.nio.file.Paths;
5558
import java.util.ArrayList;
5659
import java.util.Arrays;
60+
import java.util.List;
5761
import java.util.Set;
5862

5963
import org.junit.jupiter.api.Assertions;
@@ -154,6 +158,7 @@ public void testSeparationCSWithICCBasedAsAlternative() throws Exception {
154158
}
155159

156160
@Test
161+
// TODO: DEVSIX-6757 (update test after fix)
157162
// Android-Conversion-Ignore-Test (TODO DEVSIX-6445 fix different DeflaterOutputStream behavior)
158163
public void testSeparationCSWithDeviceCMYKAsAlternative() throws Exception {
159164
Assertions.assertThrows(UnsupportedOperationException.class, () ->
@@ -168,12 +173,14 @@ public void testGrayScalePng() throws Exception {
168173
}
169174

170175
@Test
176+
// TODO: DEVSIX-6757 (update test after fix)
171177
// Android-Conversion-Ignore-Test (TODO DEVSIX-6445 fix different DeflaterOutputStream behavior)
172178
public void testSeparationCSWithDeviceRGBAsAlternative() throws Exception {
173179
testFile("separationCSWithDeviceRgbAsAlternative.pdf", "Im1", "png");
174180
}
175181

176182
@Test
183+
// TODO: DEVSIX-6757 (update test after fix)
177184
// Android-Conversion-Ignore-Test (TODO DEVSIX-6445 fix different DeflaterOutputStream behavior)
178185
public void testSeparationCSWithDeviceRGBAsAlternative2() throws Exception {
179186
testFile("spotColorImagesSmall.pdf", "Im1", "png");
@@ -228,27 +235,27 @@ public void extractByteAlignedG4TiffImageTest() throws IOException {
228235

229236
PdfDocument pdfDocument = new PdfDocument(new PdfReader(inFileName));
230237

231-
ImageExtractor listener = new ImageExtractor();
238+
ImageAndTypeExtractor listener = new ImageAndTypeExtractor();
232239
PdfCanvasProcessor processor = new PdfCanvasProcessor(listener);
233240
processor.processPageContent(pdfDocument.getPage(1));
234241

235-
java.util.List<byte[]> images = listener.getImages();
242+
List<Tuple2<String, byte[]>> images = listener.getImages();
236243
Assertions.assertEquals(1, images.size());
237244

238245
try (OutputStream fos = FileUtil.getFileOutputStream(outImageFileName)) {
239-
fos.write(images.get(0), 0, images.size());
246+
fos.write(images.get(0).getSecond(), 0, images.size());
240247
}
241248

242249
// expected and actual are swapped here for simplicity
243-
int expectedLen = images.get(0).length;
250+
int expectedLen = images.get(0).getSecond().length;
244251
byte[] buf = new byte[expectedLen];
245252
try (InputStream is = FileUtil.getInputStreamForFile(cmpImageFileName)) {
246253
int read = is.read(buf, 0, buf.length);
247254
Assertions.assertEquals(expectedLen, read);
248255
read = is.read(buf, 0, buf.length);
249256
Assertions.assertTrue(read <= 0);
250257
}
251-
Assertions.assertArrayEquals(images.get(0), buf);
258+
Assertions.assertArrayEquals(images.get(0).getSecond(), buf);
252259
}
253260

254261
@Test
@@ -258,7 +265,7 @@ public void expectedByteAlignedTiffImageExtractionTest() throws IOException {
258265

259266
PdfDocument pdfDocument = new PdfDocument(new PdfReader(inFileName));
260267

261-
ImageExtractor listener = new ImageExtractor();
268+
ImageAndTypeExtractor listener = new ImageAndTypeExtractor();
262269
PdfCanvasProcessor processor = new PdfCanvasProcessor(listener);
263270

264271
Exception e = Assertions.assertThrows(com.itextpdf.io.exceptions.IOException.class,
@@ -269,6 +276,34 @@ public void expectedByteAlignedTiffImageExtractionTest() throws IOException {
269276
e.getMessage());
270277
}
271278

279+
280+
@Test
281+
public void inlineImageColorDepth1Test() throws IOException {
282+
//Byte-aligned image is expected in pdf file, but in fact it's not
283+
String inFileName = SOURCE_FOLDER + "inline_image_with_cs_object.pdf";
284+
285+
PdfDocument pdfDocument = new PdfDocument(new PdfReader(inFileName));
286+
287+
ImageAndTypeExtractor listener = new ImageAndTypeExtractor();
288+
PdfCanvasProcessor processor = new PdfCanvasProcessor(listener);
289+
processor.processPageContent(pdfDocument.getPage(1));
290+
291+
292+
Files.write(Paths.get(
293+
DESTINATION_FOLDER,
294+
"inline_image_with_cs_object.new." + listener.images.get(0).getFirst()),
295+
listener.images.get(0).getSecond());
296+
297+
298+
Assertions.assertEquals(1, listener.images.size());
299+
Assertions.assertEquals("png", listener.images.get(0).getFirst());
300+
301+
byte[] cmpBytes = Files.readAllBytes(Paths.get(
302+
SOURCE_FOLDER, "inline_image_with_cs_object.png"));
303+
Assertions.assertArrayEquals(cmpBytes, listener.images.get(0).getSecond());
304+
305+
}
306+
272307
@Test
273308
public void deviceGray8bitTest() throws Exception {
274309
testFile("deviceGray8bit.pdf", "fzImg0", "png");
@@ -281,7 +316,7 @@ public void deviceGray8bitFlateDecodeTest() throws Exception {
281316

282317
@Test
283318
public void deviceGray1bitFlateDecodeInvertedTest() throws Exception {
284-
testFile("deviceGray1bitFlateDecodeInverted.pdf", "Im0", "png", true);
319+
testFile("deviceGray1bitFlateDecodeInverted.pdf", "Im0", "png");
285320
}
286321

287322
@Test
@@ -1018,15 +1053,16 @@ private byte[] subArray(byte[] array, int beg, int end) {
10181053
return Arrays.copyOfRange(array, beg, end + 1);
10191054
}
10201055

1021-
private class ImageExtractor implements IEventListener {
1022-
private final java.util.List<byte[]> images = new ArrayList<>();
1056+
private static class ImageAndTypeExtractor implements IEventListener {
1057+
public final java.util.List<Tuple2<String, byte[]>> images = new ArrayList<>();
1058+
10231059

10241060
public void eventOccurred(IEventData data, EventType type) {
10251061
switch (type) {
10261062
case RENDER_IMAGE:
10271063
ImageRenderInfo renderInfo = (ImageRenderInfo) data;
10281064
byte[] bytes = renderInfo.getImage().getImageBytes();
1029-
images.add(bytes);
1065+
images.add(new Tuple2<>( renderInfo.getImage().identifyImageFileExtension(), bytes));
10301066
break;
10311067
default:
10321068
break;
@@ -1037,7 +1073,7 @@ public Set<EventType> getSupportedEvents() {
10371073
return null;
10381074
}
10391075

1040-
public java.util.List<byte[]> getImages() {
1076+
public List<Tuple2<String, byte[]>> getImages() {
10411077
return images;
10421078
}
10431079
}

kernel/src/test/java/com/itextpdf/kernel/pdf/xobject/ImagePdfBytesInfoTest.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,10 @@ public void calGrayTest() throws IOException {
101101
public void negativeNTest() throws IOException {
102102
try (PdfDocument pdfDoc = new PdfDocument(new PdfReader(SOURCE_FOLDER + "negativeN.pdf"))) {
103103
PdfImageXObject img = getPdfImageCObject(pdfDoc, 1, "Im1");
104-
ImagePdfBytesInfo imagePdfBytesInfo = new ImagePdfBytesInfo(img);
104+
ImagePdfBytesInfo imagePdfBytesInfo = new ImagePdfBytesInfo((int)img.getWidth(), (int)img.getHeight(),
105+
img.getPdfObject().getAsNumber(PdfName.BitsPerComponent).intValue(),
106+
img.getPdfObject().get(PdfName.ColorSpace),
107+
img.getPdfObject().getAsArray(PdfName.Decode));
105108

106109
int pngColorType = imagePdfBytesInfo.getPngColorType();
107110
Assertions.assertEquals(-1, pngColorType);
@@ -116,7 +119,10 @@ public void negativeNTest() throws IOException {
116119
public void undefinedCSArrayTest() throws IOException {
117120
try (PdfDocument pdfDoc = new PdfDocument(new PdfReader(SOURCE_FOLDER + "undefinedInCSArray.pdf"))) {
118121
PdfImageXObject img = getPdfImageCObject(pdfDoc, 1, "Im1");
119-
ImagePdfBytesInfo imagePdfBytesInfo = new ImagePdfBytesInfo(img);
122+
ImagePdfBytesInfo imagePdfBytesInfo = new ImagePdfBytesInfo((int)img.getWidth(), (int)img.getHeight(),
123+
img.getPdfObject().getAsNumber(PdfName.BitsPerComponent).intValue(),
124+
img.getPdfObject().get(PdfName.ColorSpace),
125+
img.getPdfObject().getAsArray(PdfName.Decode));
120126

121127
int pngColorType = imagePdfBytesInfo.getPngColorType();
122128
Assertions.assertEquals(-1, pngColorType);
@@ -128,7 +134,11 @@ public void undefinedCSArrayTest() throws IOException {
128134
}
129135

130136
private int getPngColorTypeFromObject(PdfDocument pdfDocument, int pageNum, String objectId) {
131-
ImagePdfBytesInfo imagePdfBytesInfo = new ImagePdfBytesInfo(getPdfImageCObject(pdfDocument, pageNum, objectId));
137+
PdfImageXObject img = getPdfImageCObject(pdfDocument, pageNum, objectId);
138+
ImagePdfBytesInfo imagePdfBytesInfo = new ImagePdfBytesInfo((int)img.getWidth(), (int)img.getHeight(),
139+
img.getPdfObject().getAsNumber(PdfName.BitsPerComponent).intValue(),
140+
img.getPdfObject().get(PdfName.ColorSpace),
141+
img.getPdfObject().getAsArray(PdfName.Decode));
132142
return imagePdfBytesInfo.getPngColorType();
133143
}
134144

75 Bytes
Loading

0 commit comments

Comments
 (0)