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
Related
My app is using user-selected images and creating a PDF out of it using itextpdf.
It is successfully creating PDF but there is no space between images
Ex.
My Code
public void createPdf(String dest) throws IOException, DocumentException {
Image img = Image.getInstance(allSelectedImages.get(0).getPath());
Document document = new Document(img);
PdfWriter.getInstance(document, new FileOutputStream(dest));
document.open();
for (Uri image : allSelectedImages) {
img = Image.getInstance(image.getPath());
document.newPage();
document.setMargins(100, 100, 100, 100);
img.setAbsolutePosition(0, 0);
document.add(img);
}
document.close();
}
You are adding one image per page, so the space between images is equivalent to the space between pages which is determined by your PDF viewer.
What you can do it adding some margins around your images - this is what you are trying to do already but there are some things that need to be fixed.
Here is an example on how your code can be adapted to add 100pt margin for all sides of the page (note that I am calculating page sizes dynamically so that the page size adapts to the image size in case images are of different size):
Document document = new Document();
PdfWriter.getInstance(document, new FileOutputStream("path/to.pdf"));
document.open();
for (File image : allSelectedImages) {
Image img = Image.getInstance(image.getPath());
float leftMargin = 100;
float rightMargin = 100;
float topMargin = 100;
float bottomMargin = 100;
document.setPageSize(new Rectangle(img.getWidth() + leftMargin + rightMargin, img.getHeight() + topMargin + bottomMargin));
document.newPage();
document.setMargins(leftMargin, rightMargin, topMargin, bottomMargin);
img.setAbsolutePosition(leftMargin, bottomMargin);
document.add(img);
}
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
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.
I am currently in the process of writing an app that "formats" PDF's to our requirements, using iText 2.1.7.
We basically take a portrait PDF, and scale down the pages, so we can fit 2 pages of the original PDF, on one landscape page of the new PDF. We also leave some space at the bottom of the page which is used for post processing.
This process works 90% of the time as it should.
However, we received a PDF that has been cropped/trimmed by the content department, and when we view this PDF in Acrobat, it looks as excpected. However, when we process it, the new PDF includes the entire original MediaBox, and the crop lines.
Here is the code we use, and how a problem output looks.
File tempFile = new File(tempFilename);
PdfReader reader = new PdfReader(originalPdfFile);
Document doc = new Document(new RectangleReadOnly(842f, 595f), 0, 0, 0, 0);
PdfWriter writer = PdfWriter.getInstance(doc, new FileOutputStream(tempFile));
doc.open();
for (int i = 1; i < reader.getNumberOfPages(); i = i + 2) {
doc.newPage();
PdfContentByte cb = writer.getDirectContent();
PdfImportedPage page = writer.getImportedPage(reader, i); // page #1
float documentWidth = doc.getPageSize().getWidth() / 2;
float documentHeight = doc.getPageSize().getHeight() - 65f;
float pageWidth = page.getWidth();
float pageHeight = page.getHeight();
float widthScale = documentWidth / pageWidth;
float heightScale = documentHeight / pageHeight;
float scale = Math.min(widthScale, heightScale);
float offsetX = (documentWidth - (pageWidth * scale)) / 2;
float offsetY = 65f; //100f
cb.addTemplate(page, scale, 0, 0, scale, offsetX, offsetY);
PdfImportedPage page2 = writer.getImportedPage(reader, i+1); // page #2
pageWidth = page.getWidth();
pageHeight = page.getHeight();
widthScale = documentWidth / pageWidth;
heightScale = documentHeight / pageHeight;
scale = Math.min(widthScale, heightScale);
offsetX = ((documentWidth - (pageWidth * scale)) / 2) + documentWidth;
offsetY = 65f; //100f
cb.addTemplate(page2, scale, 0, 0, scale, offsetX, offsetY);//430f
}
doc.close();
original in acrobat:
modified in acrobat, showing unwanted pretrim content:
Although it's hard to be sure without seeing the PDF itself, I suspect your problem is that this PDF specifies a CropBox on at least some of its pages. If that's the case, then I think that you would want to do something like page.setBoundingBox(reader.getCropBox(i)); right after you get the page reference.
Note that the default value of a page's CropBox is it's MediaBox, so the addition of the above line shouldn't negatively impact the layout of PDF pages that do not specify a CropBox.
(I am not an iText user, so this is a bit of speculation on my part...)
Good luck!
after a lot of frustration, I finally got this to work, by "hard cropping" the PDF before doing my scaling and layout processing.
The hard cropping takes an Acrobat cropped PDF (cropped = hidden), and uses PdfStamper to create a new PDF only containing the contents from inside the crop box.
public String cropPdf(String pdfFilePath) throws DocumentException, IOException {
String filename = FilenameUtils.getBaseName(pdfFilePath) + "_cropped." + FilenameUtils.getExtension(pdfFilePath);
filename = FilenameUtils.concat(System.getProperty("java.io.tmpdir"), filename);
PdfReader reader = new PdfReader(pdfFilePath);
try {
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(filename));
try {
for (int i = 1; i <= reader.getNumberOfPages(); i++) {
PdfDictionary pdfDictionary = reader.getPageN(i);
PdfArray cropArray = new PdfArray();
Rectangle cropbox = reader.getCropBox(i);
cropArray.add(new PdfNumber(cropbox.getLeft()));
cropArray.add(new PdfNumber(cropbox.getBottom()));
cropArray.add(new PdfNumber(cropbox.getLeft() + cropbox.getWidth()));
cropArray.add(new PdfNumber(cropbox.getBottom() + cropbox.getHeight()));
pdfDictionary.put(PdfName.CROPBOX, cropArray);
pdfDictionary.put(PdfName.MEDIABOX, cropArray);
pdfDictionary.put(PdfName.TRIMBOX, cropArray);
pdfDictionary.put(PdfName.BLEEDBOX, cropArray);
}
return filename;
} finally {
stamper.close();
}
} finally {
reader.close();
}
}
One minor but important fix to Kabals answer: the boxes expect width/height instead of coordinates:
...
cropArray.add(new PdfNumber(cropbox.getLeft()));
cropArray.add(new PdfNumber(cropbox.getBottom()));
cropArray.add(new PdfNumber(cropbox.getWidth()));
cropArray.add(new PdfNumber(cropbox.getHeight()));
...
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();
}
}