Skip to content

Commit d559048

Browse files
committed
[RELEASE] iText pdfOCR 4.1.0
2 parents cabe77b + 540f637 commit d559048

File tree

315 files changed

+169174
-534
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

315 files changed

+169174
-534
lines changed

pdfocr-api/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<parent>
66
<groupId>com.itextpdf</groupId>
77
<artifactId>pdfocr-root</artifactId>
8-
<version>4.0.2</version>
8+
<version>4.1.0</version>
99
</parent>
1010

1111
<artifactId>pdfocr-api</artifactId>
@@ -76,7 +76,7 @@
7676
</dependency>
7777
</dependencies>
7878
<configuration>
79-
<projectName>pdfocr</projectName>
79+
<projectName>pdfocr-api</projectName>
8080
<cSharpTargetFolder>./../../../sharp/pdfocr</cSharpTargetFolder>
8181
<cSharpSourceCodeDestination>itext/itext.pdfocr.api</cSharpSourceCodeDestination>
8282
<cSharpTestCodeDestination>itext.tests/itext.pdfocr.api.tests</cSharpTestCodeDestination>
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
/*
2+
This file is part of the iText (R) project.
3+
Copyright (c) 1998-2025 Apryse Group NV
4+
Authors: Apryse Software.
5+
6+
This program is offered under a commercial and under the AGPL license.
7+
For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
8+
9+
AGPL licensing:
10+
This program is free software: you can redistribute it and/or modify
11+
it under the terms of the GNU Affero General Public License as published by
12+
the Free Software Foundation, either version 3 of the License, or
13+
(at your option) any later version.
14+
15+
This program is distributed in the hope that it will be useful,
16+
but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+
GNU Affero General Public License for more details.
19+
20+
You should have received a copy of the GNU Affero General Public License
21+
along with this program. If not, see <https://www.gnu.org/licenses/>.
22+
*/
23+
package com.itextpdf.pdfocr;
24+
25+
import com.itextpdf.commons.utils.FileUtil;
26+
import com.itextpdf.kernel.geom.AffineTransform;
27+
import com.itextpdf.kernel.geom.Matrix;
28+
import com.itextpdf.kernel.geom.Point;
29+
import com.itextpdf.kernel.geom.Rectangle;
30+
import com.itextpdf.kernel.pdf.PdfPage;
31+
import com.itextpdf.kernel.pdf.canvas.parser.EventType;
32+
import com.itextpdf.kernel.pdf.canvas.parser.PdfCanvasProcessor;
33+
import com.itextpdf.kernel.pdf.canvas.parser.data.IEventData;
34+
import com.itextpdf.kernel.pdf.canvas.parser.data.ImageRenderInfo;
35+
import com.itextpdf.kernel.pdf.canvas.parser.listener.IEventListener;
36+
import com.itextpdf.kernel.pdf.xobject.PdfImageXObject;
37+
import com.itextpdf.pdfocr.util.PdfOcrFileUtil;
38+
39+
import java.io.File;
40+
import java.io.IOException;
41+
import java.io.OutputStream;
42+
import java.util.ArrayList;
43+
import java.util.Arrays;
44+
import java.util.Collections;
45+
import java.util.HashSet;
46+
import java.util.LinkedHashMap;
47+
import java.util.List;
48+
import java.util.Map;
49+
import java.util.Objects;
50+
import java.util.Set;
51+
import java.util.UUID;
52+
53+
/**
54+
* Class to extract images on page content stream processing, see {@link PdfCanvasProcessor}.
55+
*/
56+
final class ImageExtraction {
57+
58+
private ImageExtraction() {
59+
// Empty constructor to forbid instantiation
60+
}
61+
62+
/**
63+
* @return map where the key is an image path and the value is a position on the page
64+
*/
65+
static List<PageImageData> extractImagesFromPdfPage(PdfPage pdfPage) throws IOException {
66+
CanvasImageExtractor listener = new CanvasImageExtractor();
67+
PdfCanvasProcessor processor = new PdfCanvasProcessor(listener);
68+
processor.processPageContent(pdfPage);
69+
Map<PdfImageXObject, Rectangle> images = listener.getImages();
70+
71+
// Now output to temp files
72+
List<PageImageData> pageImageData = new ArrayList<>(images.size());
73+
for (Map.Entry<PdfImageXObject, Rectangle> image : images.entrySet()) {
74+
final String extension = image.getKey().identifyImageFileExtension();
75+
final String imageFilePath = PdfOcrFileUtil.getTempFilePath(
76+
"pdfocr_img_" + UUID.randomUUID(), "." + extension);
77+
try (OutputStream fos = FileUtil.getFileOutputStream(imageFilePath)) {
78+
byte[] imageBytes = image.getKey().getImageBytes();
79+
fos.write(imageBytes, 0, imageBytes.length);
80+
pageImageData.add(new PageImageData(new File(imageFilePath), image.getKey(), image.getValue()));
81+
}
82+
}
83+
84+
return pageImageData;
85+
}
86+
87+
static final class PageImageData {
88+
private File file;
89+
private PdfImageXObject xObject;
90+
private Rectangle pagePosition;
91+
92+
PageImageData(File file, PdfImageXObject xObject, Rectangle pagePosition) {
93+
this.file = file;
94+
this.xObject = xObject;
95+
this.pagePosition = pagePosition;
96+
}
97+
98+
File getPath() {
99+
return file;
100+
}
101+
102+
PdfImageXObject getXObject() {
103+
return xObject;
104+
}
105+
106+
Rectangle getPagePosition() {
107+
return pagePosition;
108+
}
109+
110+
@Override
111+
public int hashCode() {
112+
return Objects.hash((Object) file, xObject, pagePosition);
113+
}
114+
115+
@Override
116+
public boolean equals(Object o) {
117+
if (this == o) {
118+
return true;
119+
}
120+
if (o == null || getClass() != o.getClass()) {
121+
return false;
122+
}
123+
124+
PageImageData that = (PageImageData) o;
125+
return file == that.file && xObject == that.xObject && pagePosition == that.pagePosition;
126+
}
127+
}
128+
129+
// Consider moving to kernel if reused anywhere else
130+
private static final class CanvasImageExtractor implements IEventListener {
131+
// Image xobject - position on a page
132+
private final Map<PdfImageXObject, Rectangle> images = new LinkedHashMap<>();
133+
134+
CanvasImageExtractor() {
135+
// Empty constructor
136+
}
137+
138+
public void eventOccurred(IEventData data, EventType type) {
139+
if (type == EventType.RENDER_IMAGE) {
140+
ImageRenderInfo renderInfo = (ImageRenderInfo) data;
141+
final Matrix imageCtm = renderInfo.getImageCtm();
142+
final Rectangle bbox = calcImageRect(imageCtm);
143+
images.put(renderInfo.getImage(), bbox);
144+
}
145+
}
146+
147+
@Override
148+
public Set<EventType> getSupportedEvents() {
149+
return new HashSet<>(Collections.singletonList(EventType.RENDER_IMAGE));
150+
}
151+
152+
Map<PdfImageXObject, Rectangle> getImages() {
153+
return images;
154+
}
155+
156+
private Rectangle calcImageRect(Matrix ctm) {
157+
Point[] points = transformPoints(ctm,
158+
new Point(0, 0), new Point(0, 1),
159+
new Point(1, 0), new Point(1, 1));
160+
161+
return Rectangle.calculateBBox(Arrays.asList(points));
162+
}
163+
164+
private Point[] transformPoints(Matrix transformationMatrix, Point... points) {
165+
AffineTransform t = new AffineTransform(transformationMatrix.get(Matrix.I11),
166+
transformationMatrix.get(Matrix.I12),
167+
transformationMatrix.get(Matrix.I21), transformationMatrix.get(Matrix.I22),
168+
transformationMatrix.get(Matrix.I31), transformationMatrix.get(Matrix.I32));
169+
Point[] transformed = new Point[points.length];
170+
t.transform(points, 0, transformed, 0, points.length);
171+
172+
return transformed;
173+
}
174+
}
175+
}

0 commit comments

Comments
 (0)