Skip to content

Commit df4013c

Browse files
committed
Add support processing inline image with ICCBased color space in resources
DEVSIX-5295
1 parent 20c774d commit df4013c

File tree

3 files changed

+206
-42
lines changed

3 files changed

+206
-42
lines changed

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

Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,39 @@ public static PdfStream parse(PdfCanvasParser ps, PdfDictionary colorSpaceDic) t
166166
return inlineImageAsStreamObject;
167167
}
168168

169+
/**
170+
* @param colorSpaceName the name of the color space. If null, a bi-tonal (black and white) color space is assumed.
171+
* @return the components per pixel for the specified color space
172+
*/
173+
static int getComponentsPerPixel(PdfName colorSpaceName, PdfDictionary colorSpaceDic) {
174+
if (colorSpaceName == null)
175+
return 1;
176+
if (colorSpaceName.equals(PdfName.DeviceGray))
177+
return 1;
178+
if (colorSpaceName.equals(PdfName.DeviceRGB))
179+
return 3;
180+
if (colorSpaceName.equals(PdfName.DeviceCMYK))
181+
return 4;
182+
183+
if (colorSpaceDic != null) {
184+
PdfArray colorSpace = colorSpaceDic.getAsArray(colorSpaceName);
185+
if (colorSpace != null) {
186+
if (PdfName.Indexed.equals(colorSpace.getAsName(0))) {
187+
return 1;
188+
} else if (PdfName.ICCBased.equals(colorSpace.getAsName(0))) {
189+
return colorSpace.getAsStream(1).getAsNumber(PdfName.N).intValue();
190+
}
191+
} else {
192+
PdfName tempName = colorSpaceDic.getAsName(colorSpaceName);
193+
if (tempName != null) {
194+
return getComponentsPerPixel(tempName, colorSpaceDic);
195+
}
196+
}
197+
}
198+
199+
throw new InlineImageParseException(PdfException.UnexpectedColorSpace1).setMessageParams(colorSpaceName);
200+
}
201+
169202
/**
170203
* Parses the next inline image dictionary from the parser. The parser must be positioned immediately following the BI operator.
171204
* The parser will be left with position immediately following the whitespace character that follows the ID operator that ends the inline image dictionary.
@@ -226,37 +259,6 @@ private static PdfObject getAlternateValue(PdfName key, PdfObject value) {
226259
return value;
227260
}
228261

229-
/**
230-
* @param colorSpaceName the name of the color space. If null, a bi-tonal (black and white) color space is assumed.
231-
* @return the components per pixel for the specified color space
232-
*/
233-
private static int getComponentsPerPixel(PdfName colorSpaceName, PdfDictionary colorSpaceDic) {
234-
if (colorSpaceName == null)
235-
return 1;
236-
if (colorSpaceName.equals(PdfName.DeviceGray))
237-
return 1;
238-
if (colorSpaceName.equals(PdfName.DeviceRGB))
239-
return 3;
240-
if (colorSpaceName.equals(PdfName.DeviceCMYK))
241-
return 4;
242-
243-
if (colorSpaceDic != null) {
244-
PdfArray colorSpace = colorSpaceDic.getAsArray(colorSpaceName);
245-
if (colorSpace != null) {
246-
if (PdfName.Indexed.equals(colorSpace.getAsName(0))) {
247-
return 1;
248-
}
249-
} else {
250-
PdfName tempName = colorSpaceDic.getAsName(colorSpaceName);
251-
if (tempName != null) {
252-
return getComponentsPerPixel(tempName, colorSpaceDic);
253-
}
254-
}
255-
}
256-
257-
throw new InlineImageParseException(PdfException.UnexpectedColorSpace1).setMessageParams(colorSpaceName);
258-
}
259-
260262
/**
261263
* Computes the number of unfiltered bytes that each row of the image will contain.
262264
* If the number of bytes results in a partial terminating byte, this number is rounded up

kernel/src/test/java/com/itextpdf/kernel/pdf/canvas/parser/PdfCanvasProcessorIntegrationTest.java

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ This file is part of the iText (R) project.
5858
import com.itextpdf.kernel.pdf.canvas.parser.data.TextRenderInfo;
5959
import com.itextpdf.kernel.pdf.canvas.parser.listener.IEventListener;
6060
import com.itextpdf.kernel.pdf.canvas.parser.listener.LocationTextExtractionStrategy;
61+
import com.itextpdf.kernel.pdf.canvas.parser.util.InlineImageParsingUtils.InlineImageParseException;
6162
import com.itextpdf.kernel.pdf.colorspace.PdfColorSpace;
6263
import com.itextpdf.kernel.pdf.colorspace.PdfSpecialCs;
6364
import com.itextpdf.test.AssertUtil;
@@ -74,26 +75,33 @@ This file is part of the iText (R) project.
7475
import java.util.Map;
7576
import java.util.HashMap;
7677
import org.junit.Assert;
78+
import org.junit.BeforeClass;
7779
import org.junit.Ignore;
7880
import org.junit.Rule;
7981
import org.junit.Test;
8082
import org.junit.experimental.categories.Category;
81-
8283
import java.io.IOException;
8384
import java.util.Set;
8485
import org.junit.rules.ExpectedException;
8586

8687
@Category(IntegrationTest.class)
8788
public class PdfCanvasProcessorIntegrationTest extends ExtendedITextTest {
8889

89-
public static final String sourceFolder = "./src/test/resources/com/itextpdf/kernel/parser/PdfCanvasProcessorTest/";
90+
private static final String SOURCE_FOLDER = "./src/test/resources/com/itextpdf/kernel/parser/PdfCanvasProcessorTest/";
91+
92+
private static final String DESTINATION_FOLDER = "./target/test/com/itextpdf/kernel/parser/PdfCanvasProcessorTest/";
93+
94+
@BeforeClass
95+
public static void setUp() {
96+
createDestinationFolder(DESTINATION_FOLDER);
97+
}
9098

9199
@Rule
92100
public ExpectedException junitExpectedException = ExpectedException.none();
93101

94102
@Test
95103
public void contentStreamProcessorTest() throws IOException {
96-
PdfDocument document = new PdfDocument(new PdfReader(sourceFolder + "tableWithImageAndText.pdf"), new PdfWriter(new ByteArrayOutputStream()));
104+
PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "tableWithImageAndText.pdf"), new PdfWriter(new ByteArrayOutputStream()));
97105

98106
StringBuilder pageEventsLog = new StringBuilder();
99107
for (int i = 1; i <= document.getNumberOfPages(); ++i) {
@@ -105,15 +113,15 @@ public void contentStreamProcessorTest() throws IOException {
105113

106114
}
107115

108-
byte[] logBytes = Files.readAllBytes(Paths.get(sourceFolder + "contentStreamProcessorTest_events_log.dat"));
116+
byte[] logBytes = Files.readAllBytes(Paths.get(SOURCE_FOLDER + "contentStreamProcessorTest_events_log.dat"));
109117
String expectedPageEventsLog = new String(logBytes, StandardCharsets.UTF_8);
110118

111119
Assert.assertEquals(expectedPageEventsLog, pageEventsLog.toString());
112120
}
113121

114122
@Test
115123
public void processGraphicsStateResourceOperatorFillOpacityTest() throws IOException {
116-
PdfDocument document = new PdfDocument(new PdfReader(sourceFolder + "transparentText.pdf"));
124+
PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "transparentText.pdf"));
117125
Float expOpacity = 0.5f;
118126

119127
Map<String, Object> textRenderInfo = new HashMap<>();
@@ -127,7 +135,7 @@ public void processGraphicsStateResourceOperatorFillOpacityTest() throws IOExcep
127135

128136
@Test
129137
public void processGraphicsStateResourceOperatorStrokeOpacityTest() throws IOException {
130-
PdfDocument document = new PdfDocument(new PdfReader(sourceFolder + "hiddenText.pdf"));
138+
PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "hiddenText.pdf"));
131139
Float expOpacity = 0.0f;
132140

133141
Map<String, Object> textRenderInfo = new HashMap<>();
@@ -142,7 +150,7 @@ public void processGraphicsStateResourceOperatorStrokeOpacityTest() throws IOExc
142150
@Test
143151
public void testClosingEmptyPath() throws IOException {
144152
String fileName = "closingEmptyPath.pdf";
145-
PdfDocument document = new PdfDocument(new PdfReader(sourceFolder + fileName));
153+
PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + fileName));
146154
PdfCanvasProcessor processor = new PdfCanvasProcessor(new NoOpEventListener());
147155
// Assert than no exception is thrown when an empty path is handled
148156
AssertUtil.doesNotThrow(() -> processor.processPageContent(document.getPage(1)));
@@ -152,7 +160,7 @@ public void testClosingEmptyPath() throws IOException {
152160
@LogMessages(messages = @LogMessage(messageTemplate = LogMessageConstant.FAILED_TO_PROCESS_A_TRANSFORMATION_MATRIX, count = 1))
153161
public void testNoninvertibleMatrix() throws IOException {
154162
String fileName = "noninvertibleMatrix.pdf";
155-
PdfDocument pdfDocument = new PdfDocument(new PdfReader(sourceFolder + fileName));
163+
PdfDocument pdfDocument = new PdfDocument(new PdfReader(SOURCE_FOLDER + fileName));
156164

157165
LocationTextExtractionStrategy strategy = new LocationTextExtractionStrategy();
158166
PdfCanvasProcessor processor = new PdfCanvasProcessor(strategy);
@@ -171,7 +179,7 @@ public void parseCircularReferencesInResourcesTest() throws IOException {
171179
junitExpectedException.expect(StackOverflowError.class);
172180

173181
String fileName = "circularReferencesInResources.pdf";
174-
PdfDocument pdfDocument = new PdfDocument(new PdfReader(sourceFolder + fileName));
182+
PdfDocument pdfDocument = new PdfDocument(new PdfReader(SOURCE_FOLDER + fileName));
175183

176184
PdfCanvasProcessor processor = new PdfCanvasProcessor(new NoOpEventListener());
177185
PdfPage page = pdfDocument.getFirstPage();
@@ -184,7 +192,7 @@ public void parseCircularReferencesInResourcesTest() throws IOException {
184192
@Test
185193
@LogMessages(messages = @LogMessage(messageTemplate = KernelLogMessageConstant.UNABLE_TO_PARSE_COLOR_WITHIN_COLORSPACE))
186194
public void patternColorParsingNotValidPdfTest() throws IOException {
187-
String inputFile = sourceFolder + "patternColorParsingNotValidPdfTest.pdf";
195+
String inputFile = SOURCE_FOLDER + "patternColorParsingNotValidPdfTest.pdf";
188196
PdfDocument pdfDocument = new PdfDocument(new PdfReader(inputFile));
189197

190198
for (int i = 1; i <= pdfDocument.getNumberOfPages(); ++i) {
@@ -203,7 +211,7 @@ public void patternColorParsingNotValidPdfTest() throws IOException {
203211

204212
@Test
205213
public void patternColorParsingValidPdfTest() throws IOException {
206-
String inputFile = sourceFolder + "patternColorParsingValidPdfTest.pdf";
214+
String inputFile = SOURCE_FOLDER + "patternColorParsingValidPdfTest.pdf";
207215
PdfDocument pdfDocument = new PdfDocument(new PdfReader(inputFile));
208216

209217
for (int i = 1; i <= pdfDocument.getNumberOfPages(); ++i) {
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/*
2+
This file is part of the iText (R) project.
3+
Copyright (c) 1998-2021 iText Group NV
4+
Authors: Bruno Lowagie, Paulo Soares, et al.
5+
6+
This program is free software; you can redistribute it and/or modify
7+
it under the terms of the GNU Affero General Public License version 3
8+
as published by the Free Software Foundation with the addition of the
9+
following permission added to Section 15 as permitted in Section 7(a):
10+
FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
11+
ITEXT GROUP. ITEXT GROUP DISCLAIMS THE WARRANTY OF NON INFRINGEMENT
12+
OF THIRD PARTY RIGHTS
13+
14+
This program is distributed in the hope that it will be useful, but
15+
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16+
or FITNESS FOR A PARTICULAR PURPOSE.
17+
See the GNU Affero General Public License for more details.
18+
You should have received a copy of the GNU Affero General Public License
19+
along with this program; if not, see http://www.gnu.org/licenses or write to
20+
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21+
Boston, MA, 02110-1301 USA, or download the license from the following URL:
22+
http://itextpdf.com/terms-of-use/
23+
24+
The interactive user interfaces in modified source and object code versions
25+
of this program must display Appropriate Legal Notices, as required under
26+
Section 5 of the GNU Affero General Public License.
27+
28+
In accordance with Section 7(b) of the GNU Affero General Public License,
29+
a covered work must retain the producer line in every PDF that is created
30+
or manipulated using iText.
31+
32+
You can be released from the requirements of the license by purchasing
33+
a commercial license. Buying such a license is mandatory as soon as you
34+
develop commercial activities involving the iText software without
35+
disclosing the source code of your own applications.
36+
These activities include: offering paid services to customers as an ASP,
37+
serving PDFs on the fly in a web application, shipping iText with a closed
38+
source product.
39+
40+
For more information, please contact iText Software Corp. at this
41+
42+
*/
43+
package com.itextpdf.kernel.pdf.canvas.parser.util;
44+
45+
import com.itextpdf.io.util.MessageFormatUtil;
46+
import com.itextpdf.kernel.PdfException;
47+
import com.itextpdf.kernel.pdf.PdfArray;
48+
import com.itextpdf.kernel.pdf.PdfDictionary;
49+
import com.itextpdf.kernel.pdf.PdfName;
50+
import com.itextpdf.kernel.pdf.PdfNumber;
51+
import com.itextpdf.kernel.pdf.PdfStream;
52+
import com.itextpdf.kernel.pdf.canvas.parser.util.InlineImageParsingUtils.InlineImageParseException;
53+
import com.itextpdf.test.ExtendedITextTest;
54+
import com.itextpdf.test.annotations.type.UnitTest;
55+
56+
import org.junit.Assert;
57+
import org.junit.Rule;
58+
import org.junit.Test;
59+
import org.junit.experimental.categories.Category;
60+
import org.junit.rules.ExpectedException;
61+
62+
@Category(UnitTest.class)
63+
public class InlineImageParsingUtilsTest extends ExtendedITextTest {
64+
@Rule
65+
public ExpectedException junitExpectedException = ExpectedException.none();
66+
67+
@Test
68+
public void iccBasedCsTest() {
69+
PdfName colorSpace = PdfName.ICCBased;
70+
71+
PdfDictionary dictionary = new PdfDictionary();
72+
PdfArray array = new PdfArray();
73+
array.add(colorSpace);
74+
PdfStream stream = new PdfStream();
75+
stream.put(PdfName.N, new PdfNumber(4));
76+
array.add(stream);
77+
dictionary.put(colorSpace, array);
78+
79+
Assert.assertEquals(4, InlineImageParsingUtils.getComponentsPerPixel(colorSpace, dictionary));
80+
}
81+
82+
@Test
83+
public void indexedCsTest() {
84+
PdfName colorSpace = PdfName.Indexed;
85+
86+
PdfDictionary dictionary = new PdfDictionary();
87+
PdfArray array = new PdfArray();
88+
array.add(colorSpace);
89+
dictionary.put(colorSpace, array);
90+
91+
Assert.assertEquals(1, InlineImageParsingUtils.getComponentsPerPixel(colorSpace, dictionary));
92+
}
93+
94+
@Test
95+
public void csInDictAsNameTest() {
96+
PdfName colorSpace = PdfName.ICCBased;
97+
98+
PdfDictionary dictionary = new PdfDictionary();
99+
dictionary.put(colorSpace, PdfName.DeviceCMYK);
100+
101+
Assert.assertEquals(4, InlineImageParsingUtils.getComponentsPerPixel(colorSpace, dictionary));
102+
}
103+
104+
@Test
105+
public void csInDictAsNameNullTest() {
106+
PdfName colorSpace = PdfName.ICCBased;
107+
108+
PdfDictionary dictionary = new PdfDictionary();
109+
110+
junitExpectedException.expect(InlineImageParseException.class);
111+
junitExpectedException.expectMessage(MessageFormatUtil.format(PdfException.UnexpectedColorSpace1, "/ICCBased"));
112+
InlineImageParsingUtils.getComponentsPerPixel(colorSpace, dictionary);
113+
}
114+
115+
@Test
116+
public void notSupportedCsWithCsDictionaryTest() {
117+
PdfName colorSpace = PdfName.ICCBased;
118+
119+
PdfDictionary dictionary = new PdfDictionary();
120+
PdfArray array = new PdfArray();
121+
array.add(PdfName.Pattern);
122+
PdfStream stream = new PdfStream();
123+
stream.put(PdfName.N, new PdfNumber(4));
124+
array.add(stream);
125+
dictionary.put(colorSpace, array);
126+
127+
junitExpectedException.expect(InlineImageParseException.class);
128+
junitExpectedException.expectMessage(MessageFormatUtil.format(PdfException.UnexpectedColorSpace1, "/ICCBased"));
129+
InlineImageParsingUtils.getComponentsPerPixel(colorSpace, dictionary);
130+
}
131+
132+
@Test
133+
public void nullCsTest() {
134+
Assert.assertEquals(1, InlineImageParsingUtils.getComponentsPerPixel(null, null));
135+
}
136+
137+
@Test
138+
public void deviceGrayCsTest() {
139+
PdfName colorSpace = PdfName.DeviceGray;
140+
Assert.assertEquals(1, InlineImageParsingUtils.getComponentsPerPixel(colorSpace, null));
141+
}
142+
143+
@Test
144+
public void deviceRGBCsTest() {
145+
PdfName colorSpace = PdfName.DeviceRGB;
146+
Assert.assertEquals(3, InlineImageParsingUtils.getComponentsPerPixel(colorSpace, null));
147+
}
148+
149+
@Test
150+
public void deviceCMYKCsTest() {
151+
PdfName colorSpace = PdfName.DeviceCMYK;
152+
Assert.assertEquals(4, InlineImageParsingUtils.getComponentsPerPixel(colorSpace, null));
153+
}
154+
}

0 commit comments

Comments
 (0)