package no.dusken.aranea.util; import com.sun.pdfview.PDFFile; import com.sun.pdfview.PDFPage; import no.dusken.aranea.model.Image; import no.dusken.aranea.service.StoreImageService; import no.dusken.aranea.service.StoreImageServiceImpl; import no.dusken.common.util.ImageReader; import no.dusken.common.util.impl.ImageReaderImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Required; import org.springframework.web.multipart.MultipartFile; import javax.imageio.IIOImage; import javax.imageio.ImageIO; import javax.imageio.ImageWriteParam; import javax.imageio.ImageWriter; import javax.imageio.stream.FileImageOutputStream; import java.awt.*; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.Set; /** * @author Marvin B. Lillehaug <lillehau@underdusken.no> */ public class ImageUtils { /** * The directory that images is saved relative to. */ private File imageDirectory; private StoreImageService storeImageService; private static final ImageReader imageReader = new ImageReaderImpl(); private static final Logger log = LoggerFactory.getLogger(StoreImageServiceImpl.class); private Set<String> currentResizeOperations = Collections.synchronizedSet(new HashSet<String>()); /** * The quality coefficient used when resizing images. */ private float jpegCompression = 0.75f; /** * Height of the pdf thumbnail. Defaults to 800 */ private int PDF_HEIGHT = 800; /** * Resize an image file, always saves to png. The file is cached. Aspect ratio is preserved * * @param in is the image file to resize * @param width is the maximum desired width * @param height is the maximum desired height * @return the resized file * @throws IOException if somethinges goes wrong */ public File resizeImage(File in, int width, int height) throws IOException { if (width < 1 && height < 1) { // no sense to return anything return null; } if (in == null || !in.exists()) { log.warn("Input file {} is null or does not exists! ", in); return null; } // the cached file String filename = width + "x" + height + "/" + in.getName(); File cachedFile = new File(imageDirectory + "/cache/" + filename); // has this been done before? if (cachedFile.exists() || resizingIsStarted(filename)) { return cachedFile; } // Retrieve image to be resize log.debug("resizing image: {}", cachedFile.getAbsolutePath()); try { currentResizeOperations.add(filename); BufferedImage thumbImage = imageReader.readImage(in, height, width); cachedFile.getParentFile().mkdirs(); saveImage(thumbImage, cachedFile); } catch (IOException e) { log.error("Failed to resize image:{}. {} ", in.getAbsolutePath(), e); throw e; }finally { currentResizeOperations.remove(filename); } return cachedFile; } private boolean resizingIsStarted(String name) { if (currentResizeOperations.contains(name)) { log.info("resizing of image {} was already started.", name); while (currentResizeOperations.contains(name)){ try { log.info("sleeping, waiting for {}", name); Thread.sleep(500); } catch (InterruptedException e) { log.error("Interrupted", e); } } log.info("Resize done! ", name); return true; } return false; } /** common * This creates an image object from a pdf file, and saves the image to the * database The image created is default 640x480, use setMaxHeight, * setMaxWidth to change * * @param pdfFile is the pdf file to use (first page is used) * @return the image object created * @throws IOException if anything goes wrong */ public Image makeThumbnail(File pdfFile) throws IOException { if (pdfFile == null || !pdfFile.exists() || !pdfFile.isFile()) { throw new IOException("No file"); } log.debug("making thumb for pdf: {}", pdfFile.getAbsolutePath()); RandomAccessFile raf = new RandomAccessFile(pdfFile, "r"); FileChannel channel = raf.getChannel(); ByteBuffer buf = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size()); PDFFile pdffile = new PDFFile(buf); // draw the first page to an image PDFPage page = pdffile.getPage(0); //get the width and height for the doc at the default zoom Rectangle rect = new Rectangle(0, 0, (int) page.getBBox().getWidth(), (int) page.getBBox().getHeight()); //generate the image java.awt.Image img = page.getImage( rect.width, rect.height, //width & height rect, // clip rect null, // null for the ImageObserver true, // fill background with white true // block until drawing is done ); //scaling img = img.getScaledInstance(-1, PDF_HEIGHT, BufferedImage.SCALE_SMOOTH); //making a bufferedImage from Image BufferedImage buffScaledPDFImage; { //Create the BufferedImage object with the width and height of the Image buffScaledPDFImage = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_RGB); //Create the graphics object from the BufferedImage Graphics g = buffScaledPDFImage.createGraphics(); //Draw the image on the graphics of the BufferedImage g.drawImage(img, 0, 0, null); //Dispose the Graphics g.dispose(); } File tmpImageFile = new File(imageDirectory + "/tmp/" + pdfFile.getName() + ".png"); tmpImageFile.getParentFile().mkdirs(); // delete if it exists: if (tmpImageFile.exists()) { tmpImageFile.delete(); } ImageIO.write(buffScaledPDFImage, "png", tmpImageFile); Image i = storeImageService.createImage(tmpImageFile); //tmpImageFile.delete(); i.setDescription("Thumb for pdf: " + pdfFile.getName()); i.setExternalSource("From PDF"); return i; } /** * @param mFile a MultipartFile that contains a pdf. * @return Image of the first page of the pdf. * @throws IOException */ public Image makeThumbnail(MultipartFile mFile) throws IOException { File file = new File(imageDirectory + "/tmp/" + mFile.getOriginalFilename()); mFile.transferTo(file); return makeThumbnail(file); } /** * This gets the file from a image object * * @param image the image object * @return the file containing the image */ public File getImageFile(Image image) { return new File(imageDirectory + "/" + image.getUrl()); } public String getBase64Image(Image image, int width, int height) throws IOException { File f = getImageFile(image); return getBase64Image(f, width, height); } public String getBase64Image(File image, int width, int height) throws IOException { File newFile = resizeImage(image, width, height); newFile.deleteOnExit(); return Base64.encodeFromFile(newFile.getAbsolutePath()); } private void saveImage(BufferedImage image, File file) throws IOException { Iterator iter = ImageIO.getImageWritersByFormatName("jpeg"); ImageWriter writer = (ImageWriter)iter.next(); ImageWriteParam iwp = writer.getDefaultWriteParam(); iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); iwp.setCompressionQuality(jpegCompression); FileImageOutputStream output = new FileImageOutputStream(file); writer.setOutput(output); IIOImage outimage = new IIOImage(image, null, null); writer.write(null, outimage, iwp); writer.dispose(); } @Required public void setImageDirectory(File imageDirectory) { this.imageDirectory = imageDirectory; } public void setPDF_HEIGHT(int PDF_HEIGHT) { this.PDF_HEIGHT = PDF_HEIGHT; } public void setStoreImageService(StoreImageService storeImageService) { this.storeImageService = storeImageService; } public void setJpegCompression(float jpegCompression) { this.jpegCompression = jpegCompression; } }