The following method is made in order to scale a pdf document to a desired format :
Document document = new Document(MarginsPDFHelper.DIM_PAGE, MarginsPDFHelper.MARGIN_GEN, MarginsPDFHelper.MARGIN_GEN, MarginsPDFHelper.MARGIN_TOP, MarginsPDFHelper.MARGIN_BOT);
try {
PdfReader reader = new PdfReader(pdfByte);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
PdfWriter writer = PdfWriter.getInstance(document, outputStream);
document.open();
PdfContentByte content = writer.getDirectContent();
for (int i = 1; i <= reader.getNumberOfPages(); i++) {
document.newPage();
PdfImportedPage page = writer.getImportedPage(reader, i);
Rectangle pageSize = reader.getPageSizeWithRotation(i);
float scaleX = MarginsPDFHelper.DIM_PAGE.getWidth() / pageSize.getWidth();
float scaleY = MarginsPDFHelper.DIM_PAGE.getHeight() / pageSize.getHeight();
float scale = Math.min(scaleX, scaleY);
content.addTemplate(page, scale, 0, 0, scale, 0, 0);
}
return outputStream.toByteArray();
} catch (Exception e) {
LOGGER.error("Can not scale pdf", e);
} finally {
if (document.isOpen()) {
document.close();
}
The problem that i'm confronted to is that the object com.itextpdf.text.Document is closed in the finally block and that way, it seems that it's not closed properly,
I get the following exception :
Caused by: com.itextpdf.text.exceptions.InvalidPdfException: PDF header signature not found.
at com.itextpdf.text.pdf.PRTokeniser.getHeaderOffset(PRTokeniser.java:227)
at com.itextpdf.text.pdf.PdfReader.getOffsetTokeniser(PdfReader.java:486)
at com.itextpdf.text.pdf.PdfReader.<init>(PdfReader.java:176)
at com.itextpdf.text.pdf.PdfReader.<init>(PdfReader.java:250)
at com.itextpdf.text.pdf.PdfReader.<init>(PdfReader.java:240)
at be.fgov.minfin.taxi.facade.docx.utils.PdfHelper.scalePDF(PdfHelper.java:370)
However, when i move the document.close() in the try block, i don't get that exception anymore.
I don't understand why, as the finally block should be always treated before the return statement ?
The finally block always executes when the try block exits. 1
Related
I'm working on PDF related project and I want to create a PDF from the existing PDF.
all things are done but when I created a final PDF at that time this exception was thrown at the line of document.close(); at the method of savePDF which describe below.
Create new PDF from existing PDF adding PdfImportedPage.
here is my code
The app crashes only when we add a new page
private void createAndAddPage(Bitmap bitmap) {
try {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray();
Document document = new Document();
File file = getPdfFile();
FileOutputStream fileOutputStream = new FileOutputStream(file);
PdfWriter pdfWriter = PdfWriter.getInstance(document, fileOutputStream); // Change pdf's name.
document.open();
Image image = Image.getInstance(byteArray); // Change image's name and extension.
float scaler = ((document.getPageSize().getWidth() - document.leftMargin()
- document.rightMargin() - 0) / image.getWidth()) * 100; // 0 means you have no indentation. If you have any, change it.
image.scalePercent(scaler);
image.setAlignment(Image.ALIGN_CENTER);
document.add(image);
document.close();
PdfReader pdfReader = new PdfReader(file.getPath());
PdfImportedPage pdfImportedPage = pdfWriter.getImportedPage(pdfReader, 1);
pageAdjustmentAdapter.AddPage(new PageAjdustAdapter.PdfPage(pdfImportedPage, bitmap));
} catch (Exception e) {
e.printStackTrace();
}
}
method when saves a final PDF(crash happens in this method)
private void savePDF(PageAjdustAdapter pageAdjustment) {
mPDFpages = pageAdjustment.getUpdatedList();
try {
pdfWriter.setPageEvent(new RotateEvent());
document.open();
PdfContentByte pdfContentByte = pdfWriter.getDirectContent();
for (int i = 0; i < mPDFpages.size(); i++) {
pdfContentByte.addTemplate(mPDFpages.get(i).page, 0, 0);
document.newPage();
}
} catch (Exception e) {
Log.d(TAG, "run: -> " + e.getMessage());
e.printStackTrace();
} finally {
if (document.isOpen()) document.close();
actionListener.onEnd("Success");
}
}
logcat
ExceptionConverter: java.io.IOException: Stream Closed
at java.io.FileOutputStream.write(FileOutputStream.java:391)
at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82)
at java.io.BufferedOutputStream.write(BufferedOutputStream.java:121)
at java.io.FilterOutputStream.write(FilterOutputStream.java:103)
at com.itextpdf.text.pdf.OutputStreamCounter.write(OutputStreamCounter.java:104)
at com.itextpdf.text.pdf.PRStream.toPdf(PRStream.java:244)
at com.itextpdf.text.pdf.PdfIndirectObject.writeTo(PdfIndirectObject.java:157)
at com.itextpdf.text.pdf.PdfWriter$PdfBody.write(PdfWriter.java:402)
at com.itextpdf.text.pdf.PdfWriter$PdfBody.add(PdfWriter.java:380)
at com.itextpdf.text.pdf.PdfWriter$PdfBody.add(PdfWriter.java:359)
at com.itextpdf.text.pdf.PdfWriter.addToBody(PdfWriter.java:854)
at com.itextpdf.text.pdf.PdfReaderInstance.writeAllVisited(PdfReaderInstance.java:160)
at com.itextpdf.text.pdf.PdfReaderInstance.writeAllPages(PdfReaderInstance.java:176)
at com.itextpdf.text.pdf.PdfWriter.addSharedObjectsToBody(PdfWriter.java:1368)
at com.itextpdf.text.pdf.PdfWriter.close(PdfWriter.java:1251)
at com.itextpdf.text.pdf.PdfDocument.close(PdfDocument.java:901)
at com.itextpdf.text.Document.close(Document.java:415)
at com.mobilix.docscanner.PageAdjustment$8.run(PageAdjustment.java:233)
at java.lang.Thread.run(Thread.java:923)
In createAndAddPage you import the page into the wrong PdfWriter:
Document document = new Document();
File file = getPdfFile();
FileOutputStream fileOutputStream = new FileOutputStream(file);
PdfWriter pdfWriter = PdfWriter.getInstance(document, fileOutputStream); // Change pdf's name.
document.open();
[...]
document.close();
PdfReader pdfReader = new PdfReader(file.getPath());
PdfImportedPage pdfImportedPage = pdfWriter.getImportedPage(pdfReader, 1);
Here you import the new page into the PdfWriter used for creating that same new page. You instead have to import it into the PdfWriter in which you eventually want to use the pdfImportedPage.
I'm using itext5 to scale down pdf document, however i notice that the rotation information is rarely used in pdf documents metadata, it's still possible to find out the orientation by checking the width and the height of the page but with the following pdf document i end up with a pdf that has both pages in landscape orientation and in portrait orientation however all the pages are rendered in the same orientation (in the common pdf viewers).
My question is where is that information stored ? How the pdf viewer is able to render the document as it should ?
This the orignal pdf file
This is the metadata i'm able to retrieve
This is the method used :
Document document = new Document(MarginsPDFHelper.DIM_PAGE, MarginsPDFHelper.MARGIN_GEN, MarginsPDFHelper.MARGIN_GEN, MarginsPDFHelper.MARGIN_TOP, MarginsPDFHelper.MARGIN_BOT);
try {
PdfReader reader = new PdfReader(pdfByte);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
PdfWriter writer = PdfWriter.getInstance(document, outputStream);
document.open();
PdfContentByte content = writer.getDirectContent();
for (int i = 1; i <= reader.getNumberOfPages(); i++) {
document.newPage();
PdfImportedPage page = writer.getImportedPage(reader, i);
Rectangle pageSize = reader.getPageSizeWithRotation(i);
float scaleX = MarginsPDFHelper.DIM_PAGE.getWidth() / pageSize.getWidth();
float scaleY = MarginsPDFHelper.DIM_PAGE.getHeight() / pageSize.getHeight();
float scale = Math.min(scaleX, scaleY);
content.addTemplate(page, scale, 0, 0, scale, 0, 0);
}
return outputStream.toByteArray();
} catch (Exception e) {
LOGGER.error("Can not scale pdf", e);
} finally {
if (document.isOpen()) {
document.close();
} }
This the result i get after scaling down with itext5
i'm using iText 5.5.5 with Java5.
I'm trying to merge some PDF/A. when I got a "PdfAConformanceException: PDF array is out of bounds".
Trying to find error I find the "bad PDF" that cause the error and when I try to copy just it exception throw again. This error don't appear always, it appear just when this PDF/A is in the "job chain"; I tried with some other files and it's all fine. I cant share with you source PDF 'couse it's restricted.
That's my piece of code:
_log.info("Start Document Merge");
// Output pdf
ByteArrayOutputStream bos = new ByteArrayOutputStream();
com.itextpdf.text.Document document = new com.itextpdf.text.Document();
PdfCopy copy = new PdfACopy(document, bos, PdfAConformanceLevel.PDF_A_1B);
PageStamp stamp = null;
PdfReader reader = null;
PdfContentByte content = null;
int outPdfPageCount = 0;
BaseFont baseFont = BaseFont.createFont("Arial", BaseFont.WINANSI, BaseFont.EMBEDDED);
copyOutputIntents(reader, copy);
// Loop over the pages in that document
try {
int numberOfPages = reader.getNumberOfPages();
for (int i = 1; i <= numberOfPages; i++) {
PdfImportedPage pagecontent = copy.getImportedPage(reader, i);
_log.debug("Handling page numbering [" + i + "]");
stamp = copy.createPageStamp(pagecontent);
content = stamp.getUnderContent();
content.beginText();
content.setFontAndSize(baseFont, Configuration.NumPagSize);
content.showTextAligned(PdfContentByte.ALIGN_CENTER, String.format("%s %s ", Configuration.NumPagPrefix, i), Configuration.NumPagX, Configuration.NumPagY, 0);
content.endText();
stamp.alterContents();
copy.addPage(pagecontent);
outPdfPageCount++;
if (outPdfPageCount > Configuration.MaxPages) {
_log.error("Pdf Page Count > MaxPages");
throw new PackageException(Constants.ERROR_104_TEXT, Constants.ERROR_104);
}
}
copy.freeReader(reader);
reader.close();
copy.createXmpMetadata();
document.close();
} catch (Exception e) {
_log.error("Error during mergin Document, skip");
_log.debug(MiscUtil.stackToString(e));
}
return bos.toByteArray();
That's the full stacktrace:
com.itextpdf.text.pdf.PdfAConformanceException: PDF array is out of bounds.
at com.itextpdf.text.pdf.internal.PdfA1Checker.checkPdfObject(PdfA1Checker.java:269)
at com.itextpdf.text.pdf.internal.PdfAChecker.checkPdfAConformance(PdfAChecker.java:208)
at com.itextpdf.text.pdf.internal.PdfAConformanceImp.checkPdfIsoConformance(PdfAConformanceImp.java:71)
at com.itextpdf.text.pdf.PdfWriter.checkPdfIsoConformance(PdfWriter.java:3480)
at com.itextpdf.text.pdf.PdfWriter.checkPdfIsoConformance(PdfWriter.java:3476)
at com.itextpdf.text.pdf.PdfArray.toPdf(PdfArray.java:165)
at com.itextpdf.text.pdf.PdfDictionary.toPdf(PdfDictionary.java:149)
at com.itextpdf.text.pdf.PdfArray.toPdf(PdfArray.java:175)
at com.itextpdf.text.pdf.PdfDictionary.toPdf(PdfDictionary.java:149)
at com.itextpdf.text.pdf.PdfIndirectObject.writeTo(PdfIndirectObject.java:158)
at com.itextpdf.text.pdf.PdfWriter$PdfBody.write(PdfWriter.java:420)
at com.itextpdf.text.pdf.PdfWriter$PdfBody.add(PdfWriter.java:398)
at com.itextpdf.text.pdf.PdfWriter$PdfBody.add(PdfWriter.java:373)
at com.itextpdf.text.pdf.PdfWriter$PdfBody.add(PdfWriter.java:369)
at com.itextpdf.text.pdf.PdfWriter.addToBody(PdfWriter.java:843)
at com.itextpdf.text.pdf.PdfCopy.addToBody(PdfCopy.java:839)
at com.itextpdf.text.pdf.PdfCopy.addToBody(PdfCopy.java:821)
at com.itextpdf.text.pdf.PdfCopy.copyIndirect(PdfCopy.java:426)
at com.itextpdf.text.pdf.PdfCopy.copyIndirect(PdfCopy.java:446)
at com.itextpdf.text.pdf.PdfCopy.copyObject(PdfCopy.java:577)
at com.itextpdf.text.pdf.PdfCopy.copyDictionary(PdfCopy.java:503)
at com.itextpdf.text.pdf.PdfCopy.copyObject(PdfCopy.java:573)
at com.itextpdf.text.pdf.PdfCopy.copyDictionary(PdfCopy.java:503)
at com.itextpdf.text.pdf.PdfCopy.copyObject(PdfCopy.java:573)
at com.itextpdf.text.pdf.PdfCopy.copyDictionary(PdfCopy.java:493)
at com.itextpdf.text.pdf.PdfCopy.copyDictionary(PdfCopy.java:519)
at com.itextpdf.text.pdf.PdfCopy.addPage(PdfCopy.java:663)
at com.itextpdf.text.pdf.PdfACopy.addPage(PdfACopy.java:115)
at it.m2sc.engageone.documentpackage.generator.PackageGenerator.mergePDF(PackageGenerator.java:256)
In that specific case, the problem depends by a specific Font ( Gulim ) that is too big to be embedded in PDF/A-1 file. When that font was removed, everything war run fine.
I'm working with a label printer which prints my pdf file. I create the file like this:
public static Document createPDF() {
float pageWidth = 176f;
float pageHeight = 200f;
Rectangle pageSize = new Rectangle(pageWidth, pageHeight);
Document document = new Document(pageSize);
document.setMargins(5, 5, 1, 0);
String file = MainActivity.FILE_URI;
try {
PdfWriter.getInstance(document,new FileOutputStream(file));
} catch (FileNotFoundException e1) {
e1.printStackTrace();
} catch (DocumentException e1) {
e1.printStackTrace();
}
return document;
}
Then I add some paragraphs to it. The paragraphs have variabel length. When I exceed the length of the page IText adds another page. The pages are 62 x 75 mm.
I want to change the length based on the amount of text so I don't waste paper. I tried this:
try {
PdfReader reader = new PdfReader(MainActivity.FILE_URI);
while (reader.getNumberOfPages() > 1) {
float pageHeight = document.getPageSize().getHeight();
float pageWidth = document.getPageSize().getWidth();
pageHeight += 50f;
document.setPageSize(new Rectangle(pageWidth, pageHeight));
}
} catch (IOException e) {
e.printStackTrace();
}
But it's not working. The pages aren't getting longer so the number of pages stays the same and causes a loop.
Anyone knows a solution?
Essentially you need to first draw on a very long page, longer than any plausible input to your use case, and then cut off the lower, empty parts.
This means that you have to start by setting your pageHeight value accordingly. A safe value is
float pageHeight = 14400f;
The result you get now is a PDF document with one extremely long page, more than 5m in length.
This page has to be shortened to match the contents. This can be done in a second pass like this (I use the pageSize rectangle you have already defined; INTERMEDIATE points to the PDF you generated in the first pass):
PdfReader reader = new PdfReader(INTERMEDIATE);
PdfStamper stamper = new PdfStamper(reader, FINAL_RESULT);
PdfReaderContentParser parser = new PdfReaderContentParser(reader);
TextMarginFinder finder = parser.processContent(1, new TextMarginFinder());
PdfDictionary page = reader.getPageN(1);
page.put(PdfName.CROPBOX, new PdfArray(new float[]{pageSize.getLeft(), finder.getLly(), pageSize.getRight(), pageSize.getTop()}));
stamper.markUsed(page);
stamper.close();
reader.close();
Depending on your document data you can keep the intermediary PDF in memory by using a ByteArrayOutputStream for your PdfWriter, retrieving the intermdeiate PDF as byte[] after document.close(), and feeding that byte[] into the PdfReader in the second step.
Document document = new Document(reader.getPageSizeWithRotation(1));
PdfCopy writer = new PdfCopy(document, new FileOutputStream(outFile));
document.open();
PdfImportedPage page = writer.getImportedPage(reader, ++i);
writer.setFullCompression();
writer.addPage(page);
document.close();
writer.close();
I am using iText to split and merger the PDF, I need your help to reduce (compress) the output PDF size programmatically. Please let me know the steps to achieve the same.
use iText
PdfReader reader = new PdfReader(new FileInputStream("input.pdf"));
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream("output.pdf"));
int total = reader.getNumberOfPages() + 1;
for ( int i=1; i<total; i++) {
reader.setPageContent(i + 1, reader.getPageContent(i + 1));
}
stamper.setFullCompression();
stamper.close();
With writer.setFullCompression() you already compressed file as much as possible. With iText you can't do anything more.
Also change the PdfCopy to PdfSmartCopy. It will eliminate duplicate streams which have the same hash (md5).
You can use ghostscript, invoking the exe with specific parameters for print your pdf with the ghostscript's pdfwriter (example: sDEVICE=pdfwrite -sOutputFile=myfile.pdf). There are several accepted parameters, for compression or quality levels, etc.
It may result and optimized and smaller file.
Multiple Bitmap Image to pdf converter --> Compressed Pdf
public static String createPDFWithMultipleImage(Bitmap[] bitmaps, String pdf_name){
String directoryPath = Environment.getExternalStorageDirectory() + "/OpPath/";
File file = new File(directoryPath,pdf_name);
try {
FileOutputStream fileOutputStream = new FileOutputStream(file);
PdfDocument pdfDocument = new PdfDocument();
for (int i = 0; i < bitmaps.length; i++){
Bitmap original = bitmaps[i];
int nh = (int) ( original.getHeight() * (512.0 / original.getWidth()) );
Bitmap bitmap = Bitmap.createScaledBitmap(original, 512, nh, true);
PdfDocument.PageInfo pageInfo = new PdfDocument.PageInfo.Builder(bitmap.getWidth(), bitmap.getHeight(), (i + 1)).create();
PdfDocument.Page page = pdfDocument.startPage(pageInfo);
Canvas canvas = page.getCanvas();
Paint paint = new Paint();
paint.setColor(Color.BLUE);
canvas.drawPaint(paint);
canvas.drawBitmap(bitmap, 0f, 0f, null);
pdfDocument.finishPage(page);
bitmap.recycle();
}
pdfDocument.writeTo(fileOutputStream);
pdfDocument.close();
return file.toString();
} catch (IOException e) {
e.printStackTrace();
return file.toString();
}
}