package org.rr.jeborker.converter; import java.io.IOException; import java.io.OutputStream; import java.util.logging.Level; import org.apache.commons.io.IOUtils; import org.rr.commons.log.LoggerFactory; import org.rr.commons.mufs.IResourceHandler; import org.rr.commons.mufs.ResourceHandlerFactory; import org.rr.jeborker.app.JeboorkerConstants; import org.rr.jeborker.app.JeboorkerConstants.SUPPORTED_MIMES; import org.rr.jeborker.app.preferences.APreferenceStore; import org.rr.jeborker.app.preferences.PreferenceStoreFactory; import org.rr.jeborker.gui.ConverterPreferenceController; import org.rr.jeborker.gui.MainController; import org.rr.jeborker.metadata.pdf.PDFUtils; import com.itextpdf.text.Document; import com.itextpdf.text.Rectangle; import com.itextpdf.text.pdf.PdfContentByte; import com.itextpdf.text.pdf.PdfImportedPage; import com.itextpdf.text.pdf.PdfReader; import com.itextpdf.text.pdf.PdfWriter; public class PdfToPdfConverter implements IEBookConverter { private static String IMAGE_QUALITY_LABEL = Bundle.getString("MultipleConverter.imageQuality.label"); private static String IMAGE_QUALITY_KEY = PdfToPdfConverter.class.getName() + "." + IMAGE_QUALITY_LABEL; private APreferenceStore preferenceStore = PreferenceStoreFactory.getPreferenceStore(PreferenceStoreFactory.DB_STORE); private ConverterPreferenceController converterPreferenceController; private IResourceHandler pdfResource; PdfToPdfConverter(IResourceHandler pdfSource) { this.pdfResource = pdfSource; } @Override public IResourceHandler convert() throws IOException { ConverterPreferenceController converterPreferenceDialog = getConverterPreferenceController(); if(!converterPreferenceDialog.isConfirmed()) { return null; } final Document document = new Document(); final IResourceHandler targetPdfResource = ResourceHandlerFactory.getUniqueResourceHandler(this.pdfResource, "pdf"); final OutputStream pdfOutputStream = targetPdfResource.getContentOutputStream(false); PdfReader reader = null; PdfWriter writer = null; try { reader = PDFUtils.getReader(this.pdfResource.toFile()); writer = PdfWriter.getInstance(document, pdfOutputStream); if(writer == null) { throw new IOException("Failed to create PDF writer for " + pdfResource.getName()); } else { transferPdfContent(document, reader, writer); } } catch (IOException e) { throw e; } catch (Exception e) { throw new IOException("Failed to convert PDF " + pdfResource.getName(), e); } finally { if(writer != null) { try { writer.flush(); writer.close(); } catch(Exception e) { //It always throws a "already closed" exception because itext close the //output stream in the document and additional in the writer. Mute these exception. if(!e.getMessage().startsWith("Stream Closed")) { LoggerFactory.getLogger().log(Level.WARNING, "Failed to close pdf writer", e); } } } IOUtils.closeQuietly(pdfOutputStream); if(reader != null) { try { reader.close(); } catch(Exception e) { LoggerFactory.getLogger().log(Level.WARNING, "Failed to close pdf reader", e); } } } preferenceStore.addGenericEntryAsNumber(IMAGE_QUALITY_KEY, getImageQuality()); return targetPdfResource; } /** * Transfers the pdf content from the reader to the writer. */ private void transferPdfContent(final Document document, final PdfReader reader, final PdfWriter writer) { final float scale = (float) getImageQuality() / 100f; final int pageCount = reader.getNumberOfPages(); PdfContentByte directContent = null; for(int i = 0; i < pageCount; i++) { Rectangle pageSize = reader.getPageSizeWithRotation(i +1); pageSize.setTop(pageSize.getTop() * scale); pageSize.setRight(pageSize.getRight() * scale); document.setPageSize(pageSize); if(i == 0) { document.open(); directContent = writer.getDirectContent(); } else { document.newPage(); } PdfImportedPage page = writer.getImportedPage(reader, i + 1); directContent.addTemplate(page, scale, 0, 0, scale, 0, 0); } } @Override public SUPPORTED_MIMES getConversionSourceType() { return JeboorkerConstants.SUPPORTED_MIMES.MIME_PDF; } @Override public SUPPORTED_MIMES getConversionTargetType() { return JeboorkerConstants.SUPPORTED_MIMES.MIME_PDF; } /** * Gets the {@link ConverterPreferenceController} for this instance. Creates a new * {@link ConverterPreferenceController} if no one is created previously. * @see #createConverterPreferenceController() */ private ConverterPreferenceController getConverterPreferenceController() { if(this.converterPreferenceController == null) { this.converterPreferenceController = this.createConverterPreferenceController(); } if(!this.converterPreferenceController.hasShown()) { this.converterPreferenceController.showPreferenceDialog(); } return this.converterPreferenceController; } /** * Create a new {@link ConverterPreferenceController} instance. */ public ConverterPreferenceController createConverterPreferenceController() { ConverterPreferenceController preferenceController = MainController.getController().getConverterPreferenceController(); preferenceController.setShowLandscapePageEntries(false); preferenceController.addCommonSlider(IMAGE_QUALITY_LABEL, preferenceStore.getGenericEntryAsNumber(IMAGE_QUALITY_KEY, 100).intValue()); return preferenceController; } private int getImageQuality() { return getConverterPreferenceController().getCommonValueAsInt(IMAGE_QUALITY_LABEL); } public void setConverterPreferenceController(ConverterPreferenceController controller) { this.converterPreferenceController = controller; } }