001/* 002 * (C) Copyright 2006-2013 Nuxeo SA (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl-2.1.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * Laurent Doguin 016 * Florent Guillaume 017 */ 018package org.nuxeo.ecm.platform.picture.core.im; 019 020import java.io.File; 021import java.io.IOException; 022 023import org.apache.commons.io.FilenameUtils; 024import org.apache.commons.logging.Log; 025import org.apache.commons.logging.LogFactory; 026import org.nuxeo.ecm.core.api.Blob; 027import org.nuxeo.ecm.core.api.Blobs; 028import org.nuxeo.ecm.platform.commandline.executor.api.CommandAvailability; 029import org.nuxeo.ecm.platform.commandline.executor.api.CommandException; 030import org.nuxeo.ecm.platform.commandline.executor.api.CommandLineExecutorService; 031import org.nuxeo.ecm.platform.commandline.executor.api.CommandNotAvailable; 032import org.nuxeo.ecm.platform.picture.core.ImageUtils; 033import org.nuxeo.ecm.platform.picture.magick.utils.ImageCropper; 034import org.nuxeo.ecm.platform.picture.magick.utils.ImageIdentifier; 035import org.nuxeo.ecm.platform.picture.magick.utils.ImageResizer; 036import org.nuxeo.ecm.platform.picture.magick.utils.ImageRotater; 037import org.nuxeo.runtime.api.Framework; 038 039public class IMImageUtils implements ImageUtils { 040 041 private static final Log log = LogFactory.getLog(IMImageUtils.class); 042 043 public static abstract class ImageMagickCaller { 044 045 protected File sourceFile; 046 047 // a tmp file is needed if the blob doesn't have a file, or 048 // if it has one but with an incorrect extension 049 protected File tmpFile; 050 051 protected File targetFile; 052 053 public Blob call(Blob blob, String targetExt, String commandName) { 054 CommandLineExecutorService cles = Framework.getLocalService(CommandLineExecutorService.class); 055 CommandAvailability availability = cles.getCommandAvailability(commandName); 056 if (!availability.isAvailable()) { 057 return null; 058 } 059 060 try { 061 makeFiles(blob, targetExt); 062 063 callImageMagick(); 064 065 Blob targetBlob = Blobs.createBlob(targetFile); 066 Framework.trackFile(targetFile, targetBlob); 067 return targetBlob; 068 } catch (CommandNotAvailable | CommandException | IOException e) { 069 log.error("ImageMagick failed on command: " + commandName, e); 070 return null; 071 } finally { 072 if (tmpFile != null) { 073 tmpFile.delete(); 074 } 075 } 076 } 077 078 protected void makeFiles(Blob blob, String targetExt) throws CommandNotAvailable, CommandException, IOException { 079 sourceFile = blob.getFile(); 080 081 // check extension 082 String ext = FilenameUtils.getExtension(blob.getFilename()); 083 if (ext == null || "".equals(ext)) { 084 // no known extension 085 if (sourceFile == null) { 086 sourceFile = createTempSource(blob, "tmp"); 087 } 088 // detect extension 089 ext = ImageIdentifier.getInfo(sourceFile.getPath()).getFormat(); 090 if (tmpFile == null) { 091 // copy source with proper name 092 sourceFile = createTempSource(blob, ext); 093 } else { 094 // rename tmp file 095 File newTmpFile = new File(FilenameUtils.removeExtension(tmpFile.getPath()) + "." + ext); 096 tmpFile.renameTo(newTmpFile); 097 tmpFile = newTmpFile; 098 sourceFile = newTmpFile; 099 } 100 } else { 101 // check that extension on source is correct 102 if (sourceFile != null && !ext.equals(FilenameUtils.getExtension(sourceFile.getName()))) { 103 sourceFile = null; 104 } 105 } 106 107 if (sourceFile == null) { 108 sourceFile = createTempSource(blob, ext); 109 } 110 111 if (targetExt == null) { 112 targetExt = ext; 113 } 114 targetFile = File.createTempFile("nuxeoImageTarget", "." + targetExt); 115 } 116 117 protected File createTempSource(Blob blob, String ext) throws IOException { 118 tmpFile = File.createTempFile("nuxeoImageSource", "." + ext); 119 blob.transferTo(tmpFile); 120 return tmpFile; 121 } 122 123 public abstract void callImageMagick() throws CommandNotAvailable, CommandException; 124 } 125 126 @Override 127 public Blob crop(Blob blob, final int x, final int y, final int width, final int height) { 128 return new ImageMagickCaller() { 129 @Override 130 public void callImageMagick() throws CommandNotAvailable, CommandException { 131 ImageCropper.crop(sourceFile.getAbsolutePath(), targetFile.getAbsolutePath(), width, height, x, y); 132 } 133 }.call(blob, null, "resizer"); 134 } 135 136 @Override 137 public Blob resize(Blob blob, String finalFormat, final int width, final int height, final int depth) { 138 return new ImageMagickCaller() { 139 @Override 140 public void callImageMagick() throws CommandNotAvailable, CommandException { 141 ImageResizer.resize(sourceFile.getAbsolutePath(), targetFile.getAbsolutePath(), width, height, depth); 142 } 143 }.call(blob, finalFormat, "resizer"); 144 } 145 146 @Override 147 public Blob rotate(Blob blob, final int angle) { 148 return new ImageMagickCaller() { 149 @Override 150 public void callImageMagick() throws CommandNotAvailable, CommandException { 151 ImageRotater.rotate(sourceFile.getAbsolutePath(), targetFile.getAbsolutePath(), angle); 152 } 153 }.call(blob, null, "rotate"); 154 } 155 156 @Override 157 public boolean isAvailable() { 158 CommandLineExecutorService cles = Framework.getLocalService(CommandLineExecutorService.class); 159 CommandAvailability commandAvailability = cles.getCommandAvailability("identify"); 160 return commandAvailability.isAvailable(); 161 } 162 163}