package org.rr.jeborker.converter; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.List; 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.preferences.APreferenceStore; import org.rr.jeborker.app.preferences.PreferenceStoreFactory; import org.rr.jeborker.gui.ConverterPreferenceController; import org.rr.jeborker.gui.MainController; import org.rr.pm.image.IImageProvider; import org.rr.pm.image.ImageProviderFactory; import com.itextpdf.text.Document; import com.itextpdf.text.DocumentException; import com.itextpdf.text.Element; import com.itextpdf.text.Image; import com.itextpdf.text.Rectangle; import com.itextpdf.text.pdf.PdfContentByte; import com.itextpdf.text.pdf.PdfWriter; abstract class ACompressedImageToPdfConverter implements IEBookConverter { private static String IMAGE_QUALITY_LABEL = Bundle.getString("MultipleConverter.imageQuality.label"); private static String IMAGE_QUALITY_KEY = ACompressedImageToPdfConverter.class.getName() + "." + IMAGE_QUALITY_LABEL; private APreferenceStore preferenceStore = PreferenceStoreFactory.getPreferenceStore(PreferenceStoreFactory.DB_STORE); protected IResourceHandler comicBookResource; private ConverterPreferenceController converterPreferenceController = null; ACompressedImageToPdfConverter(IResourceHandler comicBookResource) { this.comicBookResource = comicBookResource; } @Override public IResourceHandler convert() throws IOException { final ConverterPreferenceController converterPreferenceController = getConverterPreferenceController(); if(!converterPreferenceController.isConfirmed()) { return null; } final List<String> compressedImageEntries = listEntries(this.comicBookResource); if(compressedImageEntries == null || compressedImageEntries.isEmpty()) { LoggerFactory.getLogger(this).log(Level.WARNING, "The Comic book archive " + comicBookResource.getName() + " is empty."); return null; } final Document document = new Document(); final IResourceHandler targetPdfResource = ResourceHandlerFactory.getUniqueResourceHandler(this.comicBookResource, "pdf"); OutputStream contentOutputStream = null; PdfWriter pdfWriter = null; try { contentOutputStream = targetPdfResource.getContentOutputStream(false); pdfWriter = this.createPdfWriter(document, contentOutputStream); attachImagesToPdf(compressedImageEntries, document, pdfWriter); contentOutputStream.flush(); } catch(Exception e) { LoggerFactory.getLogger(this).log(Level.WARNING, "Could not convert " + comicBookResource.getName() + " to Pdf." , e); } finally { closePdfWriter(pdfWriter); IOUtils.closeQuietly(contentOutputStream); } ConverterUtils.transferMetadata(this.comicBookResource, targetPdfResource); preferenceStore.addGenericEntryAsNumber(IMAGE_QUALITY_KEY, getImageQuality()); return targetPdfResource; } protected void closePdfWriter(PdfWriter pdfWriter) { if(pdfWriter != null) { pdfWriter.flush(); try { pdfWriter.close(); } catch(Exception e) {} } } private PdfWriter createPdfWriter(final Document document, final OutputStream targetPdfOutputStream) throws DocumentException, IOException { PdfWriter writer = PdfWriter.getInstance(document, targetPdfOutputStream); // add the image now and not later when itext think it's good to do that. writer.setStrictImageSequence(true); return writer; } private void attachImagesToPdf(final List<String> compressedImageEntries, final Document document, final PdfWriter pdfWriter) throws IOException, DocumentException { boolean documentOpen = false; for(int i= 0; i < compressedImageEntries.size(); i++) { String imageEntry = compressedImageEntries.get(i); if(ConverterUtils.isImageFileName(imageEntry)) { BufferedImage image = getBufferedImageFromArchive(imageEntry); List<BufferedImage> processImageModifications = ConverterUtils.processImageModifications(image, getImageQuality(), getConverterPreferenceController()); for(BufferedImage bufferedImage : processImageModifications) { float pageWidth = ((float)bufferedImage.getWidth()); float pageHeight = ((float)bufferedImage.getHeight()); document.setPageSize(new Rectangle(pageWidth, pageHeight)); if(!documentOpen) { documentOpen = true; document.open(); } else { document.newPage(); } PdfContentByte cb = pdfWriter.getDirectContent(); Image pdfImage = Image.getInstance(cb, bufferedImage, 1); pdfImage.setAlignment(Element.ALIGN_CENTER); pdfImage.setAbsolutePosition(0, 0); cb.addImage(pdfImage); } pdfWriter.flush(); } } } private BufferedImage getBufferedImageFromArchive(String imageEntry) { InputStream compressionEntryStream = getCompressionEntryStream(this.comicBookResource, imageEntry); IImageProvider imageProvider = ImageProviderFactory.getImageProvider(ResourceHandlerFactory.getResourceHandler(compressionEntryStream)); BufferedImage bufferedImage = imageProvider.getImage(); return bufferedImage; } /** * 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.addCommonSlider(IMAGE_QUALITY_LABEL, preferenceStore.getGenericEntryAsNumber(IMAGE_QUALITY_KEY, 100).intValue()); preferenceController.setShowLandscapePageEntries(true); return preferenceController; } private int getImageQuality() { return getConverterPreferenceController().getCommonValueAsInt(IMAGE_QUALITY_LABEL); } public void setConverterPreferenceController(ConverterPreferenceController controller) { this.converterPreferenceController = controller; } protected abstract InputStream getCompressionEntryStream(IResourceHandler resourceHandler, String entry); protected abstract List<String> listEntries(IResourceHandler cbzResource); }