I am using iText PdfTextExtractor to extract text from the PdfReader, where the PdfReader is created from a byte array,
byte[] pdfbytes = outputStream.toByteArray();
PdfReader reader = new PdfReader(pdfbytes);
int pagenumber = reader.getNumberOfPages();
PdfTextExtractor extractor = new PdfTextExtractor(reader);
for(int i = 1; i<= pagenumber; i++) {
System.out.println("============PAGE NUMBER " + i + "=============" );
String line = extractor.getTextFromPage(i);
System.out.println(line);
}
The first test pdf is from: http://www.gnostice.com/downloads/Gnostice_PathQuest.pdf
I can print out the first page, but get the follow exception at the second page
Exception:
Exception in thread "main" ExceptionConverter: java.io.IOException: Error reading string at file pointer 238291
at com.lowagie.text.pdf.PRTokeniser.throwError(Unknown Source)
at com.lowagie.text.pdf.PRTokeniser.nextToken(Unknown Source)
at com.lowagie.text.pdf.PdfContentParser.nextValidToken(Unknown Source)
at com.lowagie.text.pdf.PdfContentParser.readPRObject(Unknown Source)
at com.lowagie.text.pdf.PdfContentParser.parse(Unknown Source)
at com.lowagie.text.pdf.parser.PdfContentStreamProcessor.processContent(Unknown Source)
at com.lowagie.text.pdf.parser.PdfTextExtractor.getTextFromPage(Unknown Source)
at org.xxx.services.pdfparser.xxxExtensionPdfParser.main(xxxExtensionPdfParser.java:114)
where xxxExtensionPdfParser.java:114 is String line = extractor.getTextFromPage(i);
But at second test at http://www.irs.gov/pub/irs-pdf/fw4.pdf, I can get text content without exception. So i think it must be the format issue of first pdf that causes the exception.
So my question is, what is this format issue and is there anyway to avoid it? Thanks.
I am getting the same error and upon some investigation, it seems that the problem with my pdf documents is that they contain 'header' or 'footer' as opposed to the irs document you've linked. I indexed a 900 page pdf document and about 70 of the pages fail to extract. Apparently, all these pages have a footer copyright information. Any ideas how to resolve this issue ?
------EDIT ----------
I applied the following method to get text out from the aforementioned pdf. Hope this works for you as well.
PdfReader pdfReader = new PdfReader(file);
PdfReaderContentParser parser = new PdfReaderContentParser(pdfReader);
strategy = parser.processContent(currentPage, new SimpleTextExtractionStrategy());
content = strategy.getResultantText();
byte[] pdfbytes = outputStream.toByteArray();
PdfReader reader = new PdfReader(pdfbytes);
int pagenumber = reader.getNumberOfPages();
PdfTextExtractor extractor = new PdfTextExtractor(reader);
for(int i = 1; i<= pagenumber; i++) {
System.out.println("============PAGE NUMBER " + i + "=============" );
String line = PdfTextExtractor.getTextFromPage(reader,i);
System.out.println(line);
}
replace your code with this it will work fine..
Related
I am using iText for extraction of data from PDFs. My application is able to read PDFs with English characters, but we found a new file with Chinese characters. When I tried to extract that data, I get an error:
ExceptionConverter: com.itextpdf.text.DocumentException: Font 'STSong-Light' with 'UniGB-UCS2-H' is not recognized.
So I added itext-asian.jar. Now I am not getting an error, but getTextFromPage()
returns an empty string. Am I missing something?
PdfReader pr = new PdfReader(inputPdf);
// get the number of pages in the document
PdfTextExtractor pte =
new PdfTextExtractor(pr, new CustomLocationAwarePdfRenderListener(scanDepth));
int pNum = pr.getNumberOfPages();
String text = "";
// extract text from each page and write it to the output text file
for (int page = 1; page <= pNum; page++) {
text = text.concat("\n").concat(pte.getTextFromPage(page));
}
URL stringfile = getXsl("test.xml");
File originFile = new File(stringfile.getFile());
String xml = null;
ByteArrayOutputStream pdfStream = null;
try {
FileInputStream fis = new FileInputStream(originFile);
int length = fis.available();
byte[] readData = new byte[length];
fis.read(readData);
xml = (new String(readData)).trim();
fis.close();
xml = xml.substring(xml.lastIndexOf("<HttpCommandList>")+17, xml.lastIndexOf("</HttpCommandList>"));
String[] splitxml = xml.split("</HttpCommand>");
for (int i = 0; i < splitxml.length; i++) {
tmpxml = splitxml[i].trim() + "</HttpCommand>";
System.out.println("splitxml:" +tmpxml);
pdfStream = new ByteArrayOutputStream();
pdf = new com.lowagie.text.Document();
PdfWriter.getInstance(pdf, pdfStream);
pdf.open();
URL xslToUse = getXsl("test.xsl");
// Here am using some utility class to transform
// generate the XML needed by iText to generate the PDF using MessageBuffer contents
String iTextXml = XmlUtil.transformXml(tmpxml.toString(), xslToUse).trim();
// generate the PDF document by parsing the specified XML file
XmlParser.parse(pdf, new ByteArrayInputStream(iTextXml.getBytes()));
}
For the above code, during the XmlParser am getting java.net.malformedURL exception : no protocol
Am trying to generate the pdf document by parsing the specified xml file.
We could need the actual xml-file to decide what is missing. I expect, that there is no protocol defined, just like this:
192.168.1.2/ (no protocol)
file://192.168.1.2/ (there is one)
And URL seems to need one.
Also try:
new File("somexsl.xlt").toURI().toURL();
See here and here.
It always helps spoilering the complete stacktrace. No one knows, where the exception actually occured, if you dont post the line numbers.
To read a pdf file, I have used below code segment working with the iText library. However, for some pdf documents, it throws an exception which is shown at below of the code. I donot understand why this exception is sent for some document but for some other it is not thrown. Moreover, how can I solve this problem?
NOTE: Below code is for extracting text from pdf, i.e. pd fto txt converter
private ArrayList<byte[]> contentOfPdf() {
PdfReader reader = null;
PdfDictionary dictionary = null;
PRIndirectReference reference = null;
PRStream contentStream = null;
ArrayList<byte []> byteStream = new ArrayList<byte []>();
try{
reader = new PdfReader(this.filename);
for(int currentPage = 0 ; currentPage <= this.totalPageNumber ; currentPage ++ ) {
dictionary = reader.getPageN(currentPage);
reference = (PRIndirectReference) dictionary.get(PdfName.CONTENTS);
/*line 166*/ contentStream = (PRStream) PdfReader.getPdfObject(reference);
byteStream.add( PdfReader.getStreamBytes(contentStream) );
}
} catch(Exception e){
e.printStackTrace();
} finally {
reader.close();
}
return byteStream;
}
Exception :
java.lang.ClassCastException: com.itextpdf.text.pdf.PdfArray cannot be cast to com.itextpdf.text.pdf.PRStream
at pdfCrawler.retrieveContentOfPdf(CrawlerTask.java:166)
at pdfCrawler.call(CrawlerTask.java:55)
at pdfCrawler..call(CrawlerTask.java:1)
at java.util.concurrent.FutureTask$Sync.innerRun(Unknown Source)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Whenever you manually walk a PDF I'd strongly recommend that you have a copy of the PDF spec nearby and look up each and every key. In your case, if you look up the CONTENTS key you'll see that it says:
The value shall be either a single stream or an array of streams.
I'm not a Java guy but the below C# code should be easily converted to Java and should do what you're looking for:
//Will hold an array of references
PdfArray refs = null;
//If we have an array, use it directly
if (dictionary.Get(PdfName.CONTENTS).IsArray()) {
refs = dictionary.GetAsArray(PdfName.CONTENTS);
//If we have just a reference, wrap it in a single item array for convenience
} else if (dictionary.Get(PdfName.CONTENTS).IsIndirect()) {
refs = new PdfArray(dictionary.Get(PdfName.CONTENTS));
//Sanity check, should never happen for conforming PDFs
} else {
throw new ApplicationException("Unknown CONTENTS types");
}
//Loop through each reference
foreach (var r in refs) {
//Same code here
reference = (PRIndirectReference)r;
contentStream = (PRStream)PdfReader.GetPdfObject(reference);
byteStream.Add(PdfReader.GetStreamBytes(contentStream));
}
I'm trying to create an auto-filled PDF of a government payroll form, which involves the possibility of a variable number of pages. I'm currently storing each page as a Map, with the keys being the names of the fields and the values being their contents.
At the moment, I have this code:
in = new FileInputStream(inputPDF);
PdfCopyFields adder = new PdfCopyFields(outStream);
PdfReader reader = null;
PdfStamper stamper = null;
ByteArrayOutputStream baos = null;
for (int pageNum = 0; pageNum < numPages; pageNum++) {
reader = new PdfReader(in);
baos = new ByteArrayOutputStream();
stamper = new PdfStamper(reader, baos);
AcroFields form = stamper.getAcroFields();
Map<String, String> page = pages.get(pageNum);
setFieldsToPage(form, pageNum);
populatePage(form, page, pageNum);
stamper.close();
reader = new PdfReader(baos.toByteArray());
adder.addDocument(reader);
}
The methods called are:
private void populatePage(AcroFields form, Map<String, String> pageMap, int pageNum) throws Exception {
ArrayList<String> fieldNames = new ArrayList<String>();
for (String key : pageMap.keySet()) {
fieldNames.add(key);
}
for (String key : fieldNames) {
form.setField(key + pageNum, pageMap.get(key));
}
}
and
private void setFieldsToPage(AcroFields form, int pageNum) {
ArrayList<String> fieldNames = new ArrayList<String>();
Map<String, AcroFields.Item> fields = form.getFields();
for (String fieldName : fields.keySet()) {
fieldNames.add(fieldName);
}
for (String fieldName : fieldNames) {
form.renameField(fieldName, fieldName + pageNum);
}
}
The issue is that this throws an exception on the second iteration through the loop: at reader = new PdfReader(in); I get the following exception:
java.io.IOException: PDF header signature not found.
What am I doing wrong here, and how do I fix it?
EDIT:
Here is the exception:
java.io.IOException: PDF header signature not found.
at com.lowagie.text.pdf.PRTokeniser.checkPdfHeader(Unknown Source)
at com.lowagie.text.pdf.PdfReader.readPdf(Unknown Source)
at com.lowagie.text.pdf.PdfReader.<init>(Unknown Source)
at com.lowagie.text.pdf.PdfReader.<init>(Unknown Source)
By the way, I'm sorry if the formatting is bad - this is my first time using stackoverflow.
Your issue is that you essentially try to read the same input stream multiple times while it is positioned at its end already after the first time:
in = new FileInputStream(inputPDF);
[...]
for (int pageNum = 0; pageNum < numPages; pageNum++) {
reader = new PdfReader(in);
[...]
}
The whole stream is read in the first iteration; thus, in the second one new PdfReader(in) essentially tries to parse an empty file resulting in your
java.io.IOException: PDF header signature not found
You can fix that by simply constructing the PdfReader with the input file path directly every time:
for (int pageNum = 0; pageNum < numPages; pageNum++) {
reader = new PdfReader(inputPDF);
[...]
}
Two more things, though:
You don't close your PdfReader instances after use. In the most recent iText versions implicit closing of readers has been taken out of the code as it collides with numerous use cases. Thus, after you finished working with a reader (this includes that any stamper etc using that reader also is closed), you should close the reader explicitly.
In general, if you have a PDF already in your file system, opening a PdfReader for it via a FileInputStream is very wasteful resource-wise --- a reader initialized with an input stream first completely reads that stream into memory (byte[]) and then parses the in-memory representation; a reader initialized with a file path directly parses on-disc representation.
The exception tells you that the file you're reading doesn't start with %PDF-.
Write a small example that doesn't involve iText and check the first 5 bytes of the InputStream in and you'll find out what you're doing wrong (we can't tell you unless you show us those 5 bytes).
I have been trying to split one big PDF file to multiple pdf files based on its size. I was able to split it but it only creates one single file and rest of the file data is lost. Means it does not create more than one files to split it. Can anyone please help? Here is my code
public static void main(String[] args) {
try {
PdfReader Split_PDF_By_Size = new PdfReader("C:\\Temp_Workspace\\TestZip\\input1.pdf");
Document document = new Document();
PdfCopy copy = new PdfCopy(document, new FileOutputStream("C:\\Temp_Workspace\\TestZip\\File1.pdf"));
document.open();
int number_of_pages = Split_PDF_By_Size.getNumberOfPages();
int pagenumber = 1; /* To generate file name dynamically */
// int Find_PDF_Size; /* To get PDF size in bytes */
float combinedsize = 0; /* To convert this to Kilobytes and estimate new PDF size */
for (int i = 1; i < number_of_pages; i++ ) {
float Find_PDF_Size;
if (combinedsize == 0 && i != 1) {
document = new Document();
pagenumber++;
String FileName = "File" + pagenumber + ".pdf";
copy = new PdfCopy(document, new FileOutputStream(FileName));
document.open();
}
copy.addPage(copy.getImportedPage(Split_PDF_By_Size, i));
Find_PDF_Size = copy.getCurrentDocumentSize();
combinedsize = (float)Find_PDF_Size / 1024;
if (combinedsize > 496 || i == number_of_pages) {
document.close();
combinedsize = 0;
}
}
System.out.println("PDF Split By Size Completed. Number of Documents Created:" + pagenumber);
}
catch (Exception i)
{
System.out.println(i);
}
}
}
(BTW, it would have been great if you had tagged your question with itext, too.)
PdfCopy used to close the PdfReaders it imported pages from whenever the source PdfReader for page imports switched or the PdfCopy was closed. This was due to the original intended use case to create one target PDF from multiple source PDFs in combination with the fact that many users forget to close their PdfReaders.
Thus, after you close the first target PdfCopy, the PdfReader is closed, too, and no further pages are extracted.
If I interpret the most recent checkins into the iText SVN repository correctly, this implicit closing of PdfReaders is in the process of being removed from the code. Therefore, with one of the next iText versions, your code may work as intended.