我想问一个关于iText的问题。 我在PDF文件中搜索文本时遇到问题。
我可以使用getTextfromPage()
方法创建纯文本文件,如以下代码示例所述:
/** The original PDF that will be parsed. */
public static final String PREFACE = "D:/B.pdf";
/** The resulting text file. */
public static final String RESULT = "D:/Result.txt";
public void ParsePDF(String From, String Destination) throws IOException{
PdfReader reader = new PdfReader(PREFACE);
PrintWriter out = new PrintWriter(new FileOutputStream(RESULT));
for (int i = 1; i <= reader.getNumberOfPages(); i++) {
out.println(PdfTextExtractor.getTextFromPage(reader, i));
}
out.flush();
out.close();
reader.close();
}
我正在尝试在结果文本中找到特定的String
,如下所示:
public void FindWords(String From) {
try{
String ligneLue;
LineNumberReader lnr=new LineNumberReader(new FileReader(RESULT));
try{
while((ligneLue=lnr.readLine())!=null){
SearchForSVHC(ligneLue,SvhcList);
}
}
finally{
lnr.close();
}
}
catch(IOException e){
System.out.println(e);}
}
public void SearchForSVHC(String Ligne,List<String> List){
for(String CAS :List){
if(Ligne.contains(CAS)){
System.out.print("Yes "+CAS);
break;
}}
}
我的问题是我正在解析的一些PDF包含扫描图像,这意味着没有真实文本,只有像素。
iText是否支持光学字符识别(OCR)以及后续问题:有没有办法确定PDF是否包含扫描图像?
答案 0 :(得分:4)
在回答问题之前,我已经对您的问题进行了彻底的编辑。
当PDF由扫描图像组成时,没有要解析的真实文本,只有像素看起来的图像就像文本一样。您需要进行OCR才能知道在这样的扫描页面上实际写入的内容,并且iText不支持OCR。
关于后续问题:很难确定PDF是否包含扫描图像。第一个赠品将是:页面中只有一个图像,而且没有文字。
但是:由于您对图像的性质一无所知(也许您的PDF只包含假日照片),因此很难确定PDF是否是一个充满扫描文本页面的文档(那就是:栅格化文本。)
答案 1 :(得分:1)
没有iText与OCR无关。源自扫描书籍的PDF可以包含作为图像的页面,文本或(通常用于保持视觉原件和文本搜索能力)两者;当然,如果它包含文本,那是因为已经完成了一些OCR。
要检测PDF只包含图像而没有文本可能是一个棘手的事情,但一个简单的启发式方法是尝试提取文本(请参阅example)并确定PDF是纯图像它返回(几乎)所有(大多数)页面的空文本。
答案 2 :(得分:1)
This支持案例称iText不支持OCR。识别图像是否包含文本就像将图像传递给OCR处理器并检查结果是否有意义一样简单。
答案 3 :(得分:1)
可以结合使用iText和Tesseract(谷歌OCR实现)。
首先,我将在OCR引擎周围放置一个接口。这可以让我稍后将其换出。
public interface IOpticalCharacterRecognitionEngine {
class OCRChunk {
private Rectangle location;
private String text;
public OCRChunk(Rectangle rectangle, String text){
this.location = rectangle;
this.text = text;
}
public String getText(){ return text; }
public Rectangle getLocation(){return location;}
}
List<OCRChunk> doOCR(BufferedImage bufferedImage);
}
此界面从本质上说:“ OCR引擎返回的对象是位置(矩形)和文本的组合”
然后,我们需要创建一个ITextExtractionStrategy,使用OCREngine将ImageRenderInfo
事件转换为TextRenderInfo
public class OCRTextExtractionStrategy implements ITextExtractionStrategy {
private final ITextExtractionStrategy innerStrategy;
private final IOpticalCharacterRecognitionEngine opticalCharacterRecognitionEngine;
private final Logger logger = Logger.getLogger(OCRTextExtractionStrategy.class.getSimpleName());
public OCRTextExtractionStrategy(ITextExtractionStrategy innerStrategy, IOpticalCharacterRecognitionEngine opticalCharacterRecognitionEngine){
this.innerStrategy = innerStrategy;
this.opticalCharacterRecognitionEngine = opticalCharacterRecognitionEngine;
}
public String getResultantText() {
return innerStrategy.getResultantText();
}
public void eventOccurred(IEventData iEventData, EventType eventType) {
// handle images
if(eventType == EventType.RENDER_IMAGE){
// extract coordinates
ImageRenderInfo imageRenderInfo = (ImageRenderInfo) iEventData;
float x = imageRenderInfo.getImageCtm().get(Matrix.I11);
float y = imageRenderInfo.getImageCtm().get(Matrix.I22);
// attempt to parse image
try {
BufferedImage bufferedImage = imageRenderInfo.getImage().getBufferedImage();
for(IOpticalCharacterRecognitionEngine.OCRChunk chunk : opticalCharacterRecognitionEngine.doOCR(bufferedImage)){
if(chunk.getText() != null && !chunk.getText().isEmpty()) {
chunk.getLocation().translate((int) x, (int) y);
TextRenderInfo textRenderInfo = pseudoTextRenderInfo(chunk);
if(textRenderInfo != null)
innerStrategy.eventOccurred( textRenderInfo, EventType.RENDER_TEXT);
}
}
} catch (IOException e) { logger.severe(e.getLocalizedMessage()); }
}
// handle anything else
else {
innerStrategy.eventOccurred(iEventData, eventType);
}
}
private TextRenderInfo pseudoTextRenderInfo(IOpticalCharacterRecognitionEngine.OCRChunk chunk){
// dummy graphics state
ModifiableGraphicsState mgs = new ModifiableGraphicsState();
try {
mgs.setFont(PdfFontFactory.createFont());
mgs.setCtm(new Matrix( 1,0,0,
0,1,0,
0,0,1));
} catch (IOException e) { }
// dummy text matrix
float x = chunk.getLocation().x;
float y = chunk.getLocation().y;
Matrix textMatrix = new Matrix( x, 0,0,
0, y, 0,
0,0,0);
// return TextRenderInfo object
return new TextRenderInfo(
new PdfString(chunk.getText(), ""),
mgs,
textMatrix,
new Stack<CanvasTag>()
);
}
public Set<EventType> getSupportedEvents() { return null; }
}
此类执行该翻译。 坐标变换有一些神奇之处(我可能还没有完全正确)。
使用pseudoTextRenderInfo
方法将工作IOpticalCharacterRecognitionEngine
转换为TextRenderInfo
对象,从而完成了工作。
为了使其正常工作,我们需要一个CanvasGraphicsState
可修改。
哪个不是默认的实现。
因此,让我们扩展默认值。
class ModifiableGraphicsState extends CanvasGraphicsState{
private Matrix ctm;
public ModifiableGraphicsState(){ super(); }
public Matrix getCtm() { return ctm; }
public ModifiableGraphicsState setCtm(Matrix ctm){this.ctm = ctm; return this;};
public void updateCtm(float a, float b, float c, float d, float e, float f) { updateCtm(new Matrix(a, b, c, d, e, f)); }
public void updateCtm(Matrix newCtm) {
ctm = newCtm.multiply(ctm);
}
}
最后,我们需要实现IOpticalCharacterRecognitionEngine
。
此具体实现是使用Tesseract(如果您使用Java,则是tess4j)完成的。
public class TesseractOpticalCharacterRecognitionEngine implements IOpticalCharacterRecognitionEngine {
private Tesseract tesseract;
public TesseractOpticalCharacterRecognitionEngine(File tesseractDataDirectory, String languageCode){
tesseract = new Tesseract();
// set data path
if(!tesseractDataDirectory.exists())
throw new IllegalArgumentException();
tesseract.setDatapath(tesseractDataDirectory.getAbsolutePath());
// set language code
if(!new File(tesseractDataDirectory, languageCode + ".traineddata").exists())
throw new IllegalArgumentException();
tesseract.setLanguage(languageCode);
}
public List<OCRChunk> doOCR(BufferedImage bufferedImage) {
List<OCRChunk> textChunkLocationList = new ArrayList<>();
try {
for(Rectangle rectangle : tesseract.getSegmentedRegions(bufferedImage, ITessAPI.TessPageIteratorLevel.RIL_WORD)){
String text = tesseract.doOCR(bufferedImage, rectangle);
textChunkLocationList.add(new OCRChunk(rectangle, text));
}
} catch (Exception e) { }
return textChunkLocationList;
}
}
然后您可以按以下方式调用代码:
// initialize tesseract
TesseractOpticalCharacterRecognitionEngine ocrEngine = new TesseractOpticalCharacterRecognitionEngine(new File("tessdata_fast"), "eng");
// create document
PdfDocument pdfDocument = new PdfDocument(new PdfReader(new File("scanned_document.pdf")));
// extract text
SimpleTextExtractionStrategy simpleTextExtractionStrategy = new SimpleTextExtractionStrategy();
OCRTextExtractionStrategy ocrTextExtractionStrategy = new OCRTextExtractionStrategy(simpleTextExtractionStrategy, ocrEngine);
new PdfCanvasProcessor(ocrTextExtractionStrategy).processPageContent(pdfDocument.getPage(1));
// display
System.out.println(simpleTextExtractionStrategy.getResultantText());
答案 4 :(得分:1)
到目前为止,iText确实有一个OCR product,它使用Tesseract4.x。您可以在其Knowledge Base上找到其所有文档。
下面是一个简单的示例,说明如何将图像OCR转换为PDF / A-3u文件。
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.pdfocr.OcrPdfCreator;
import com.itextpdf.pdfocr.tesseract4.Tesseract4LibOcrEngine;
import com.itextpdf.pdfocr.tesseract4.Tesseract4OcrEngineProperties;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
public class JDoodle {
private static List LIST_IMAGES_OCR = Arrays.asList(new File("invoice_front.jpg"));
private static String OUTPUT_PDF = "/myfiles/hello.pdf";
private static final String DEFAULT_RGB_COLOR_PROFILE_PATH = "profiles/sRGB_CS_profile.icm";
public static void main(String[] args) throws IOException {
OcrPdfCreatorProperties properties = new OcrPdfCreatorProperties();
properties.setPdfLang("en"); //we need to define a language to make it PDF/A compliant
OcrPdfCreator ocrPdfCreator = new OcrPdfCreator(new Tesseract4LibOcrEngine(new Tesseract4OcrEngineProperties()), properties);
try (PdfWriter writer = new PdfWriter(OUTPUT_PDF)) {
ocrPdfCreator.createPdfA(LIST_IMAGES_OCR, writer, getRGBPdfOutputIntent()).close();
}
}
public static PdfOutputIntent getRGBPdfOutputIntent() throws FileNotFoundException {
InputStream is = new FileInputStream(DEFAULT_RGB_COLOR_PROFILE_PATH);
return new PdfOutputIntent("", "",
"", "sRGB IEC61966-2.1", is);
}
}
它来晚了,但我希望对您有帮助。