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);
}
Related
I'm using this depo git application that allows you to capture or choose images and save them in a pdf document. It's working great except the image saved does not fit the whole screen.
So I tried changing theses numbers Document document = new Document(PageSize.A4, 38, 38, 50, 38); or image.setBorderWidth(15);
(documentRect.getWidth() - image.getScaledWidth()) / 2,
(documentRect.getHeight() - image.getScaledHeight()) / 2);
but no luck...
Furthermore the author didn't not use any imageview on layout so I have to edit problematically. Any ideas ?
Update : apparently changing image.scaleAbsolute(bmp.getWidth(), bmp.getHeight()); to
image.scaleAbsolute(500f, 500f); or image.scalePercent(500f); does the trick (I have to play with the numbers to fit the page).
Downside the quality image is horrible...
PdfUtils
public static final String LOG_ACTIVITY = "PdfUtils";
public static String ImgPdf(Activity activity, ArrayList<String> listPathImg,String folder, String pdfName) {
String result ="";
Image image;
String path = FileUtils.createFolderApp(folder);
path = path + pdfName + ".pdf";
Document document = new Document(PageSize.A4, 38, 38, 50, 38);
Log.v(LOG_ACTIVITY, "Document Created");
Rectangle documentRect = document.getPageSize();
try {
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(path));
document.open();
for (int i = 0; i < listPathImg.size(); i++) {
Bitmap bmp = MediaStore
.Images
.Media
.getBitmap(
activity.getContentResolver(),
Uri.fromFile(new File(listPathImg.get(i))));
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.PNG, 70, stream);
image = Image.getInstance(listPathImg.get(i));
if (bmp.getWidth() > documentRect.getWidth() || bmp.getHeight() > documentRect.getHeight()) {
//bitmap is larger than page,so set bitmap's size similar to the whole page
image.scaleAbsolute(documentRect.getWidth(), documentRect.getHeight());
} else {
//bitmap is smaller than page, so add bitmap simply.
//[note: if you want to fill page by stretching image,
// you may set size similar to page as above]
image.scaleAbsolute(bmp.getWidth(), bmp.getHeight());
}
image.setAbsolutePosition(
(documentRect.getWidth() - image.getScaledWidth()) / 2,
(documentRect.getHeight() - image.getScaledHeight()) / 2);
image.setBorder(Image.BOX);
image.setBorderWidth(15);
document.add(image);
document.newPage();
}
result=path;
} catch (Exception err) {
err.printStackTrace();
result="";
} finally {
document.close();
}
return result;
}
Instead of image.scaleAbsolute(width,height) try image.scaleToFit(width,height)
I think what you want to do is change the size of
Rectangle documentRect = document.getPageSize();
Rectangle has 4 parameters that you can set on construction (x , y, width, height).
Changing those might solve your problem.
I use PDF24 Creator if I understand the question correctly.
Solution from this answer.
I changed image.scaleAbsolute(bmp.getWidth(), bmp.getHeight()); to
image.scalePercent(scaler); ofc you have to include
float scaler = ((document.getPageSize().getWidth() - document.leftMargin()
- document.rightMargin() - indentation) / image.getWidth()) * 100;
Now it fit. Though the image quality is not perfect.
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 have a small (quarter inch) one page PDF I created with PDFBOX with text (A). I want to put that small one page PDF (A) on the top of an existing PDF page (B), preserving the existing content of the PDF page (B). In the end, I will have a one page PDF, representing the small PDF on top(A), and the existing PDF intact making up the rest (B). How can I accomplish this with PDFBOX?
To join two pages one atop the other onto one target page, you can make use of the PDFBox LayerUtility for importing pages as form XObjects in a fashion similar to PDFBox SuperimposePage example, e.g. with this helper method:
void join(PDDocument target, PDDocument topSource, PDDocument bottomSource) throws IOException {
LayerUtility layerUtility = new LayerUtility(target);
PDFormXObject topForm = layerUtility.importPageAsForm(topSource, 0);
PDFormXObject bottomForm = layerUtility.importPageAsForm(bottomSource, 0);
float height = topForm.getBBox().getHeight() + bottomForm.getBBox().getHeight();
float width, topMargin, bottomMargin;
if (topForm.getBBox().getWidth() > bottomForm.getBBox().getWidth()) {
width = topForm.getBBox().getWidth();
topMargin = 0;
bottomMargin = (topForm.getBBox().getWidth() - bottomForm.getBBox().getWidth()) / 2;
} else {
width = bottomForm.getBBox().getWidth();
topMargin = (bottomForm.getBBox().getWidth() - topForm.getBBox().getWidth()) / 2;
bottomMargin = 0;
}
PDPage targetPage = new PDPage(new PDRectangle(width, height));
target.addPage(targetPage);
PDPageContentStream contentStream = new PDPageContentStream(target, targetPage);
if (bottomMargin != 0)
contentStream.transform(Matrix.getTranslateInstance(bottomMargin, 0));
contentStream.drawForm(bottomForm);
contentStream.transform(Matrix.getTranslateInstance(topMargin - bottomMargin, bottomForm.getBBox().getHeight()));
contentStream.drawForm(topForm);
contentStream.close();
}
(JoinPages method join)
You use it like this:
try ( PDDocument document = new PDDocument();
PDDocument top = ...;
PDDocument bottom = ...) {
join(document, top, bottom);
document.save("joinedPage.pdf");
}
(JoinPages test testJoinSmallAndBig)
The result looks like this:
Just as an additional point to #mkl's answer.
If anybody is looking to scale the PDFs before placing them on the page use,
contentStream.transform(Matrix.getScaleInstance(<scaling factor in x axis>, <scaling factor in y axis>)); //where 1 is the scaling factor if you want the page as the original size
This way you can rescale your PDFs.
I have a bunch of PDF documents in a folder and I want to augment them with a watermark. What are my options from a Java serverside context?
Preferably the watermark will support transparency. Both vector and raster is desirable.
Please take a look at the TransparentWatermark2 example. It adds transparent text on each odd page and a transparent image on each even page of an existing PDF document.
This is how it's done:
public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
PdfReader reader = new PdfReader(src);
int n = reader.getNumberOfPages();
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
// text watermark
Font f = new Font(FontFamily.HELVETICA, 30);
Phrase p = new Phrase("My watermark (text)", f);
// image watermark
Image img = Image.getInstance(IMG);
float w = img.getScaledWidth();
float h = img.getScaledHeight();
// transparency
PdfGState gs1 = new PdfGState();
gs1.setFillOpacity(0.5f);
// properties
PdfContentByte over;
Rectangle pagesize;
float x, y;
// loop over every page
for (int i = 1; i <= n; i++) {
pagesize = reader.getPageSizeWithRotation(i);
x = (pagesize.getLeft() + pagesize.getRight()) / 2;
y = (pagesize.getTop() + pagesize.getBottom()) / 2;
over = stamper.getOverContent(i);
over.saveState();
over.setGState(gs1);
if (i % 2 == 1)
ColumnText.showTextAligned(over, Element.ALIGN_CENTER, p, x, y, 0);
else
over.addImage(img, w, 0, 0, h, x - (w / 2), y - (h / 2));
over.restoreState();
}
stamper.close();
reader.close();
}
As you can see, we create a Phrase object for the text and an Image object for the image. We also create a PdfGState object for the transparency. In our case, we go for 50% opacity (change the 0.5f into something else to experiment).
Once we have these objects, we loop over every page. We use the PdfReader object to get information about the existing document, for instance the dimensions of every page. We use the PdfStamper object when we want to stamp extra content on the existing document, for instance adding a watermark on top of each single page.
When changing the graphics state, it is always safe to perform a saveState() before you start and to restoreState() once you're finished. You code will probably also work if you don't do this, but believe me: it can save you plenty of debugging time if you adopt the discipline to do this as you can get really strange effects if the graphics state is out of balance.
We apply the transparency using the setGState() method and depending on whether the page is an odd page or an even page, we add the text (using ColumnText and an (x, y) coordinate calculated so that the text is added in the middle of each page) or the image (using the addImage() method and the appropriate parameters for the transformation matrix).
Once you've done this for every page in the document, you have to close() the stamper and the reader.
Caveat:
You'll notice that pages 3 and 4 are in landscape, yet there is a difference between those two pages that isn't visible to the naked eye. Page 3 is actually a page of which the size is defined as if it were a page in portrait, but it is rotated by 90 degrees. Page 4 is a page of which the size is defined in such a way that the width > the height.
This can have an impact on the way you add a watermark, but if you use getPageSizeWithRotation(), iText will adapt. This may not be what you want: maybe you want the watermark to be added differently.
Take a look at TransparentWatermark3:
public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
PdfReader reader = new PdfReader(src);
int n = reader.getNumberOfPages();
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
stamper.setRotateContents(false);
// text watermark
Font f = new Font(FontFamily.HELVETICA, 30);
Phrase p = new Phrase("My watermark (text)", f);
// image watermark
Image img = Image.getInstance(IMG);
float w = img.getScaledWidth();
float h = img.getScaledHeight();
// transparency
PdfGState gs1 = new PdfGState();
gs1.setFillOpacity(0.5f);
// properties
PdfContentByte over;
Rectangle pagesize;
float x, y;
// loop over every page
for (int i = 1; i <= n; i++) {
pagesize = reader.getPageSize(i);
x = (pagesize.getLeft() + pagesize.getRight()) / 2;
y = (pagesize.getTop() + pagesize.getBottom()) / 2;
over = stamper.getOverContent(i);
over.saveState();
over.setGState(gs1);
if (i % 2 == 1)
ColumnText.showTextAligned(over, Element.ALIGN_CENTER, p, x, y, 0);
else
over.addImage(img, w, 0, 0, h, x - (w / 2), y - (h / 2));
over.restoreState();
}
stamper.close();
reader.close();
}
In this case, we don't use getPageSizeWithRotation() but simply getPageSize(). We also tell the stamper not to compensate for the existing page rotation: stamper.setRotateContents(false);
Take a look at the difference in the resulting PDFs:
In the first screen shot (showing page 3 and 4 of the resulting PDF of TransparentWatermark2), the page to the left is actually a page in portrait rotated by 90 degrees. iText however, treats it as if it were a page in landscape just like the page to the right.
In the second screen shot (showing page 3 and 4 of the resulting PDF of TransparentWatermark3), the page to the left is a page in portrait rotated by 90 degrees and we add the watermark as if the page is in portrait. As a result, the watermark is also rotated by 90 degrees. This doesn't happen with the page to the right, because that page has a rotation of 0 degrees.
This is a subtle difference, but I thought you'd want to know.
If you want to read this answer in French, please read Comment créer un filigrane transparent en PDF?
Best option is iText. Check a watermark demo here
Important part of the code (where the watermar is inserted) is this:
public class Watermark extends PdfPageEventHelper {
#Override
public void onEndPage(PdfWriter writer, Document document) {
// insert here your watermark
}
Read carefully the example.
onEndPage() method will be something like (in my logo-watermarks I use com.itextpdf.text.Image;):
Image image = Image.getInstance(this.getClass().getResource("/path/to/image.png"));
// set transparency
image.setTransparency(transparency);
// set position
image.setAbsolutePosition(absoluteX, absoluteY);
// put into document
document.add(image);
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()));
...