001/* 002 * (C) Copyright 2006-2013 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Laurent Doguin 018 * Florent Guillaume 019 */ 020package org.nuxeo.ecm.platform.picture.core.im; 021 022import java.io.File; 023import java.io.IOException; 024 025import org.apache.commons.io.FilenameUtils; 026import org.apache.commons.logging.Log; 027import org.apache.commons.logging.LogFactory; 028import org.nuxeo.ecm.core.api.Blob; 029import org.nuxeo.ecm.core.api.Blobs; 030import org.nuxeo.ecm.platform.commandline.executor.api.CommandAvailability; 031import org.nuxeo.ecm.platform.commandline.executor.api.CommandException; 032import org.nuxeo.ecm.platform.commandline.executor.api.CommandLineExecutorService; 033import org.nuxeo.ecm.platform.commandline.executor.api.CommandNotAvailable; 034import org.nuxeo.ecm.platform.picture.core.ImageUtils; 035import org.nuxeo.ecm.platform.picture.magick.utils.ImageConverter; 036import org.nuxeo.ecm.platform.picture.magick.utils.ImageCropper; 037import org.nuxeo.ecm.platform.picture.magick.utils.ImageIdentifier; 038import org.nuxeo.ecm.platform.picture.magick.utils.ImageResizer; 039import org.nuxeo.ecm.platform.picture.magick.utils.ImageRotater; 040import org.nuxeo.runtime.api.Framework; 041 042public class IMImageUtils implements ImageUtils { 043 044 private static final Log log = LogFactory.getLog(IMImageUtils.class); 045 046 public static abstract class ImageMagickCaller { 047 048 protected File sourceFile; 049 050 // a tmp file is needed if the blob doesn't have a file, or 051 // if it has one but with an incorrect extension 052 protected File tmpFile; 053 054 protected File targetFile; 055 056 public Blob call(Blob blob, String targetExt, String commandName) { 057 CommandLineExecutorService cles = Framework.getLocalService(CommandLineExecutorService.class); 058 CommandAvailability availability = cles.getCommandAvailability(commandName); 059 if (!availability.isAvailable()) { 060 return null; 061 } 062 063 try { 064 makeFiles(blob, targetExt); 065 066 callImageMagick(); 067 068 Blob targetBlob = Blobs.createBlob(targetFile); 069 Framework.trackFile(targetFile, targetBlob); 070 return targetBlob; 071 } catch (CommandNotAvailable | CommandException | IOException e) { 072 log.error("ImageMagick failed on command: " + commandName, e); 073 return null; 074 } finally { 075 if (tmpFile != null) { 076 tmpFile.delete(); 077 } 078 } 079 } 080 081 protected void makeFiles(Blob blob, String targetExt) throws CommandNotAvailable, CommandException, IOException { 082 sourceFile = blob.getFile(); 083 084 // check extension 085 String ext = FilenameUtils.getExtension(blob.getFilename()); 086 if (ext == null || "".equals(ext)) { 087 // no known extension 088 if (sourceFile == null) { 089 sourceFile = createTempSource(blob, "tmp"); 090 } 091 // detect extension 092 ext = ImageIdentifier.getInfo(sourceFile.getPath()).getFormat(); 093 if (tmpFile == null) { 094 // copy source with proper name 095 sourceFile = createTempSource(blob, ext); 096 } else { 097 // rename tmp file 098 File newTmpFile = new File(FilenameUtils.removeExtension(tmpFile.getPath()) + "." + ext); 099 tmpFile.renameTo(newTmpFile); 100 tmpFile = newTmpFile; 101 sourceFile = newTmpFile; 102 } 103 } else { 104 // check that extension on source is correct 105 if (sourceFile != null && !ext.equals(FilenameUtils.getExtension(sourceFile.getName()))) { 106 sourceFile = null; 107 } 108 } 109 110 if (sourceFile == null) { 111 sourceFile = createTempSource(blob, ext); 112 } 113 114 if (targetExt == null) { 115 targetExt = ext; 116 } 117 targetFile = Framework.createTempFile("nuxeoImageTarget", "." + targetExt); 118 } 119 120 protected File createTempSource(Blob blob, String ext) throws IOException { 121 tmpFile = Framework.createTempFile("nuxeoImageSource", "." + ext); 122 blob.transferTo(tmpFile); 123 return tmpFile; 124 } 125 126 public abstract void callImageMagick() throws CommandNotAvailable, CommandException; 127 } 128 129 @Override 130 public Blob crop(Blob blob, final int x, final int y, final int width, final int height) { 131 return new ImageMagickCaller() { 132 @Override 133 public void callImageMagick() throws CommandNotAvailable, CommandException { 134 ImageCropper.crop(sourceFile.getAbsolutePath(), targetFile.getAbsolutePath(), width, height, x, y); 135 } 136 }.call(blob, null, "resizer"); 137 } 138 139 @Override 140 public Blob resize(Blob blob, String finalFormat, final int width, final int height, final int depth) { 141 return new ImageMagickCaller() { 142 @Override 143 public void callImageMagick() throws CommandNotAvailable, CommandException { 144 ImageResizer.resize(sourceFile.getAbsolutePath(), targetFile.getAbsolutePath(), width, height, depth); 145 } 146 }.call(blob, finalFormat, "resizer"); 147 } 148 149 @Override 150 public Blob rotate(Blob blob, final int angle) { 151 return new ImageMagickCaller() { 152 @Override 153 public void callImageMagick() throws CommandNotAvailable, CommandException { 154 ImageRotater.rotate(sourceFile.getAbsolutePath(), targetFile.getAbsolutePath(), angle); 155 } 156 }.call(blob, null, "rotate"); 157 } 158 159 @Override 160 public Blob convertToPDF(Blob blob) { 161 return new ImageMagickCaller() { 162 @Override 163 public void callImageMagick() throws CommandNotAvailable, CommandException { 164 ImageConverter.convert(sourceFile.getAbsolutePath(), targetFile.getAbsolutePath()); 165 } 166 }.call(blob, "pdf", "converter"); 167 } 168 169 @Override 170 public boolean isAvailable() { 171 CommandLineExecutorService cles = Framework.getLocalService(CommandLineExecutorService.class); 172 CommandAvailability commandAvailability = cles.getCommandAvailability("identify"); 173 return commandAvailability.isAvailable(); 174 } 175 176}