/*
* Copyright 2014 Matthias Braun, Martin Gangl
*
* This library is free software: you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.bges.pdf;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.geom.Rectangle2D;
import java.awt.print.Book;
import java.awt.print.PageFormat;
import java.awt.print.Paper;
import java.awt.print.PrinterJob;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import com.lowagie.text.Document;
import com.lowagie.text.PageSize;
import com.lowagie.text.Rectangle;
import com.lowagie.text.pdf.BaseFont;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfImportedPage;
import com.lowagie.text.pdf.PdfReader;
import com.lowagie.text.pdf.PdfWriter;
import com.sun.pdfview.PDFFile;
import com.sun.pdfview.PDFPage;
import com.sun.pdfview.PDFPrintPage;
/**
* Provides utility functions for handling PDF files.
*
* @author Matthias Braun
* @author Martin Gangl
*
*/
public final class PdfUtil {
private static final float DEFAULT_DPI = 72;
private static final float DEFAULT_PDF_LEFTMARGIN = 25;
private static final float DEFAULT_PDF_RIGHTMARGIN = 25;
private static final float DEFAULT_PDF_TOPMARGIN = 25;
private static final float DEFAULT_PDF_BOTTOMMARGIN = 25;
/**
* @param image
* to be embedded into a pdf file
* @param leftMargin
* of the pdf document
* @param rightMargin
* of the pdf document
* @param topMargin
* of the pdf document
* @param bottomMargin
* of the pdf document
* @param description
* of the pdf document
* @return a byte array containing the pdf file
* @throws IllegalArgumentException
* if invalid parameters have been given (negative margins, null
* image etc.)
*/
public static PDFByteArray convertImageToPDF(final Image image,
final float leftMargin, final float rightMargin,
final float topMargin, final float bottomMargin,
final String description) throws IllegalArgumentException {
if (leftMargin <= 0 || rightMargin <= 0 || topMargin <= 0
|| bottomMargin <= 0) {
throw new IllegalArgumentException(
"Invalid document margins, only positive values are allowed");
}
try {
final com.lowagie.text.Image img = com.lowagie.text.Image
.getInstance(image, null);
final Document document = new Document(new Rectangle(PageSize.A4),
leftMargin, rightMargin, topMargin, bottomMargin);
img.scaleToFit(document.getPageSize().getWidth()
- (leftMargin + rightMargin), document.getPageSize()
.getHeight() - (topMargin + bottomMargin));
final ByteArrayOutputStream ba = new ByteArrayOutputStream();
PdfWriter.getInstance(document, ba);
document.open();
document.add(img);
document.close();
return new PDFByteArray(ba.toByteArray(), description);
} catch (final Exception e) {
throw new IllegalArgumentException(e.getMessage());
}
}
/**
* @param image
* to be embedded into a pdf file
* @param description
* of the image
* @return a byte array containing the pdf file
* @throws IllegalArgumentException
* if invalid parameters have been given (negative margins, null
* image etc.)
*/
public static PDFByteArray convertImageToPDF(final Image image,
final String description) throws IllegalArgumentException {
return convertImageToPDF(image, DEFAULT_PDF_LEFTMARGIN,
DEFAULT_PDF_RIGHTMARGIN, DEFAULT_PDF_TOPMARGIN,
DEFAULT_PDF_BOTTOMMARGIN, description);
}
/**
* @param pdfData
* in form of a PDFByteArray
* @return a list containing the images (one for each pdf page)
* @throws IllegalArgumentException
* if the pdfData is null
*/
public static List<Image> convertPDFToImage(final PDFByteArray pdfData)
throws IllegalArgumentException {
return convertPDFToImage(pdfData, 1);
}
/**
* @param pdfData
* in form of a PDFByteArray
* @param scaleFactor
* for the images
* @return a list containing the images (one for each pdf page)
* @throws IllegalArgumentException
* if an invalid scaleFactor has been given or the pdfData is
* null
*/
public static List<Image> convertPDFToImage(final PDFByteArray pdfData,
final double scaleFactor) throws IllegalArgumentException {
if (scaleFactor <= 0 || pdfData == null) {
throw new IllegalArgumentException(
"Invalid scale factor (must be greater than zero) or PDF data (must not be null)");
}
final ByteBuffer buf = ByteBuffer.wrap(pdfData.getByteArray());
PDFFile pdfFile;
try {
pdfFile = new PDFFile(buf);
} catch (final IOException e) {
throw new IllegalArgumentException(e.getMessage());
}
final List<Image> list = new ArrayList<Image>();
for (int i = 1; i <= pdfFile.getNumPages(); i++) {
// Retrieve page i from document
final PDFPage page = pdfFile.getPage(i);
final Rectangle2D r2d = page.getBBox();
// Prepare page scaling according to systems default DPI
double width = r2d.getWidth();
double height = r2d.getHeight();
width /= DEFAULT_DPI;
height /= DEFAULT_DPI;
// Determine current screen DPI and scale the image accordingly
final int res = Toolkit.getDefaultToolkit().getScreenResolution();
width *= res * scaleFactor;
height *= res * scaleFactor;
// Add image to list
list.add(page.getImage((int) width, (int) height, r2d, null, true,
true));
}
return list;
}
/**
* @param file
* to be checked whether it is a pdf file or not
* @return true if the file is a pdf file (warning - only the extension of
* the file will be checked, not it's size or actual content)
*/
public static boolean isPDFFile(final File file) {
if (file == null || !file.exists()) {
return false;
}
if (file.getName().toLowerCase().endsWith("pdf")) {
return true;
}
return false;
}
/**
* @param pdfs
* list of PDFByteArray containing the pdf files
* @param paginate
* true if each page should contain a page number
* @param pageDescription
* something like 'page' (defaults to "" if null)
* @param paginationOfDescription
* middle word between current page number and overall page
* number (defaults to 'of' if null)
* @param description
* of the merged pdf file
* @return an PDFByteArray containing the merged PDF file
* @throws IllegalArgumentException
* if invalid input parameters (PDFByteArray with less than 2
* pdf files) were given
* @throws Exception
* if an internal error occurs while processing the given files
*/
public static PDFByteArray mergePDFs(final List<PDFByteArray> pdfs,
final boolean paginate, String pageDescription,
String paginationOfDescription, final String description)
throws Exception {
if (pdfs == null || pdfs.size() < 2) {
throw new IllegalArgumentException(
"method must be called with at least 2 pdf files");
}
final List<InputStream> isPDFs = new ArrayList<InputStream>();
for (final PDFByteArray curPdf : pdfs) {
isPDFs.add(new ByteArrayInputStream(curPdf.getByteArray()));
}
if (pageDescription == null) {
pageDescription = "";
}
if (paginationOfDescription == null) {
paginationOfDescription = "of";
}
// Create the working document and initialize the outputstream
final Document document = new Document();
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
final List<PdfReader> readers = new ArrayList<PdfReader>();
int totalPages = 0;
// Create Readers for the PDFs
for (final InputStream pdf : isPDFs) {
final PdfReader pdfReader = new PdfReader(pdf);
readers.add(pdfReader);
totalPages += pdfReader.getNumberOfPages();
}
// Create a writer for the outputstream
final PdfWriter writer = PdfWriter.getInstance(document,
outputStream);
document.open();
final BaseFont bf = BaseFont.createFont(BaseFont.HELVETICA,
BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
final PdfContentByte cb = writer.getDirectContent(); // Holds the
// PDF data
PdfImportedPage page;
int currentPageNumber = 0;
int pageOfCurrentReaderPDF = 0;
final Iterator<PdfReader> iteratorPDFReader = readers.iterator();
// Loop through the PDF files and add to the output
while (iteratorPDFReader.hasNext()) {
final PdfReader pdfReader = iteratorPDFReader.next();
// Create a new page in the target for each source page
while (pageOfCurrentReaderPDF < pdfReader.getNumberOfPages()) {
document.newPage();
pageOfCurrentReaderPDF++;
currentPageNumber++;
page = writer.getImportedPage(pdfReader,
pageOfCurrentReaderPDF);
cb.addTemplate(page, 0, 0);
// Code for pagination
if (paginate) {
cb.beginText();
cb.setFontAndSize(bf, 9);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER,
pageDescription + "" + currentPageNumber + " "
+ paginationOfDescription + " "
+ totalPages, 520, 5, 0);
cb.endText();
}
}
pageOfCurrentReaderPDF = 0;
}
document.close();
final PDFByteArray retVal = new PDFByteArray(
outputStream.toByteArray(), description);
return retVal;
} catch (final Exception e) {
throw e;
} finally {
// Close document in case of errors
if (document.isOpen()) {
document.close();
}
}
}
/**
* @param pdf
* byte array to be printed
* @param jobName
* for the print job
* @return true if the user did print, false otherwise
* @throws IllegalArgumentException
* if invalid arguments have been given (invalid byte array)
*/
public static boolean printPDFByteArray(final PDFByteArray pdf,
String jobName) throws IllegalArgumentException {
if (jobName == null) {
jobName = "";
}
try {
final ByteBuffer bb = ByteBuffer.wrap(pdf.getByteArray());
final PDFFile pdfFile = new PDFFile(bb); // Create PDF Print Page
final PDFPrintPage printPages = new PDFPrintPage(pdfFile); // Create
// Print
// Job
final PrinterJob pjob = PrinterJob.getPrinterJob();
final PageFormat pf = PrinterJob.getPrinterJob().defaultPage();
// Format for DIN A4 page size
final Paper paper = new Paper();
paper.setSize(595.275591, 841.889764); // width, height of DIN-A4 at
// 72dpi
paper.setImageableArea(0, 0, paper.getWidth() * 2,
paper.getHeight());
pf.setPaper(paper);
pjob.setJobName(jobName);
final Book book = new Book();
book.append(printPages, pf, pdfFile.getNumPages());
pjob.setPageable(book); // Send print job to default printer
if (pjob.printDialog()) {
pjob.print();
return true;
}
return false;
} catch (final Exception ex) {
throw new IllegalArgumentException(ex.getMessage());
}
}
}