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.ImageCropper; 036import org.nuxeo.ecm.platform.picture.magick.utils.ImageIdentifier; 037import org.nuxeo.ecm.platform.picture.magick.utils.ImageResizer; 038import org.nuxeo.ecm.platform.picture.magick.utils.ImageRotater; 039import org.nuxeo.runtime.api.Framework; 040 041public class IMImageUtils implements ImageUtils { 042 043 private static final Log log = LogFactory.getLog(IMImageUtils.class); 044 045 public static abstract class ImageMagickCaller { 046 047 protected File sourceFile; 048 049 // a tmp file is needed if the blob doesn't have a file, or 050 // if it has one but with an incorrect extension 051 protected File tmpFile; 052 053 protected File targetFile; 054 055 public Blob call(Blob blob, String targetExt, String commandName) { 056 CommandLineExecutorService cles = Framework.getLocalService(CommandLineExecutorService.class); 057 CommandAvailability availability = cles.getCommandAvailability(commandName); 058 if (!availability.isAvailable()) { 059 return null; 060 } 061 062 try { 063 makeFiles(blob, targetExt); 064 065 callImageMagick(); 066 067 Blob targetBlob = Blobs.createBlob(targetFile); 068 Framework.trackFile(targetFile, targetBlob); 069 return targetBlob; 070 } catch (CommandNotAvailable | CommandException | IOException e) { 071 log.error("ImageMagick failed on command: " + commandName, e); 072 return null; 073 } finally { 074 if (tmpFile != null) { 075 tmpFile.delete(); 076 } 077 } 078 } 079 080 protected void makeFiles(Blob blob, String targetExt) throws CommandNotAvailable, CommandException, IOException { 081 sourceFile = blob.getFile(); 082 083 // check extension 084 String ext = FilenameUtils.getExtension(blob.getFilename()); 085 if (ext == null || "".equals(ext)) { 086 // no known extension 087 if (sourceFile == null) { 088 sourceFile = createTempSource(blob, "tmp"); 089 } 090 // detect extension 091 ext = ImageIdentifier.getInfo(sourceFile.getPath()).getFormat(); 092 if (tmpFile == null) { 093 // copy source with proper name 094 sourceFile = createTempSource(blob, ext); 095 } else { 096 // rename tmp file 097 File newTmpFile = new File(FilenameUtils.removeExtension(tmpFile.getPath()) + "." + ext); 098 tmpFile.renameTo(newTmpFile); 099 tmpFile = newTmpFile; 100 sourceFile = newTmpFile; 101 } 102 } else { 103 // check that extension on source is correct 104 if (sourceFile != null && !ext.equals(FilenameUtils.getExtension(sourceFile.getName()))) { 105 sourceFile = null; 106 } 107 } 108 109 if (sourceFile == null) { 110 sourceFile = createTempSource(blob, ext); 111 } 112 113 if (targetExt == null) { 114 targetExt = ext; 115 } 116 targetFile = Framework.createTempFile("nuxeoImageTarget", "." + targetExt); 117 } 118 119 protected File createTempSource(Blob blob, String ext) throws IOException { 120 tmpFile = Framework.createTempFile("nuxeoImageSource", "." + ext); 121 blob.transferTo(tmpFile); 122 return tmpFile; 123 } 124 125 public abstract void callImageMagick() throws CommandNotAvailable, CommandException; 126 } 127 128 @Override 129 public Blob crop(Blob blob, final int x, final int y, final int width, final int height) { 130 return new ImageMagickCaller() { 131 @Override 132 public void callImageMagick() throws CommandNotAvailable, CommandException { 133 ImageCropper.crop(sourceFile.getAbsolutePath(), targetFile.getAbsolutePath(), width, height, x, y); 134 } 135 }.call(blob, null, "resizer"); 136 } 137 138 @Override 139 public Blob resize(Blob blob, String finalFormat, final int width, final int height, final int depth) { 140 return new ImageMagickCaller() { 141 @Override 142 public void callImageMagick() throws CommandNotAvailable, CommandException { 143 ImageResizer.resize(sourceFile.getAbsolutePath(), targetFile.getAbsolutePath(), width, height, depth); 144 } 145 }.call(blob, finalFormat, "resizer"); 146 } 147 148 @Override 149 public Blob rotate(Blob blob, final int angle) { 150 return new ImageMagickCaller() { 151 @Override 152 public void callImageMagick() throws CommandNotAvailable, CommandException { 153 ImageRotater.rotate(sourceFile.getAbsolutePath(), targetFile.getAbsolutePath(), angle); 154 } 155 }.call(blob, null, "rotate"); 156 } 157 158 @Override 159 public boolean isAvailable() { 160 CommandLineExecutorService cles = Framework.getLocalService(CommandLineExecutorService.class); 161 CommandAvailability commandAvailability = cles.getCommandAvailability("identify"); 162 return commandAvailability.isAvailable(); 163 } 164 165}