package org.rr.jeborker.metadata.pdf;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.util.List;
import java.util.logging.Level;
import org.rr.commons.log.LoggerFactory;
import org.rr.commons.mufs.IResourceHandler;
import org.rr.jeborker.converter.ConverterUtils;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.io.FileChannelRandomAccessSource;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfImportedPage;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.text.pdf.RandomAccessFileOrArray;
public class PDFUtils {
/**
* Create a {@link PdfReader} for the given file. A {@link RandomAccessFile} will be used so the result {@link PdfReader} did not load the
* whole pdf at once.
*
* @param pdfFile The pdf file to be loaded with the {@link PdfReader}.
* @return The desired {@link PdfReader}
* @throws IOException
*/
public static PdfReader getReader(File pdfFile) throws IOException {
RandomAccessFile file = new RandomAccessFile(pdfFile, "r");
FileChannel fileChannelI = file.getChannel();
FileChannelRandomAccessSource fileChannelRandomAccessSource = new FileChannelRandomAccessSource(fileChannelI);
RandomAccessFileOrArray rafPdfIn = new RandomAccessFileOrArray(fileChannelRandomAccessSource);
return new PDFReaderDelegate(rafPdfIn, file, fileChannelI);
}
private static class PDFReaderDelegate extends PdfReader {
private FileChannel fileChannelI;
private RandomAccessFile file;
PDFReaderDelegate(RandomAccessFileOrArray rafPdfIn, RandomAccessFile file, FileChannel fileChannelI) throws IOException {
super(rafPdfIn, null);
this.file = file;
this.fileChannelI = fileChannelI;
}
@Override
public void close() {
super.close();
try {
this.file.close();
} catch (IOException e) {
LoggerFactory.getLogger(this).log(Level.WARNING, "Failed to close File " + file, e);
}
if (fileChannelI != null) {
try {
fileChannelI.close();
this.fileChannelI = null;
} catch (IOException e) {
LoggerFactory.getLogger(this).log(Level.WARNING, "Failed to close File " + file, e);
}
}
}
}
/**
* Merge multiple pdf into one pdf and transfers the metadata from the first document in <code>inputPdfDocuments</code> to the new pdf.
*
* @param inputPdfDocuments of pdf input stream
* @param outputStream output file output stream
* @throws DocumentException
* @throws IOException
*/
public static void merge(List<IResourceHandler> inputPdfDocuments, IResourceHandler outputPdfDocument)
throws DocumentException, IOException {
Document document = new Document();
try (OutputStream outputStream = outputPdfDocument.getContentOutputStream(false)) {
PdfWriter writer = PdfWriter.getInstance(document, outputStream);
document.open();
PdfContentByte cb = writer.getDirectContent();
for (IResourceHandler in : inputPdfDocuments) {
PdfReader reader = getReader(in.toFile());
for (int i = 1; i <= reader.getNumberOfPages(); i++) {
document.setPageSize(reader.getPageSize(i));
document.newPage();
// import the page from source pdf
PdfImportedPage page = writer.getImportedPage(reader, i);
// add the page to the destination pdf
cb.addTemplate(page, 0, 0);
}
}
outputStream.flush();
document.close();
}
if (!inputPdfDocuments.isEmpty()) {
ConverterUtils.transferMetadata(inputPdfDocuments.get(0), outputPdfDocument);
}
}
}