diff --git a/portal-impl/src/META-INF/util-spring.xml b/portal-impl/src/META-INF/util-spring.xml index c968615a831d56..4cdef3385c486d 100644 --- a/portal-impl/src/META-INF/util-spring.xml +++ b/portal-impl/src/META-INF/util-spring.xml @@ -156,6 +156,11 @@ + + + + + diff --git a/portal-impl/src/com/liferay/portal/image/ImageMagickImpl.java b/portal-impl/src/com/liferay/portal/image/ImageMagickImpl.java new file mode 100644 index 00000000000000..a316f5b1cabb7d --- /dev/null +++ b/portal-impl/src/com/liferay/portal/image/ImageMagickImpl.java @@ -0,0 +1,253 @@ +/** + * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + */ + +package com.liferay.portal.image; + +import com.liferay.portal.kernel.configuration.Filter; +import com.liferay.portal.kernel.image.ImageMagick; +import com.liferay.portal.kernel.log.Log; +import com.liferay.portal.kernel.log.LogFactoryUtil; +import com.liferay.portal.kernel.process.ClassPathUtil; +import com.liferay.portal.kernel.process.ProcessCallable; +import com.liferay.portal.kernel.process.ProcessException; +import com.liferay.portal.kernel.process.ProcessExecutor; +import com.liferay.portal.kernel.util.OSDetector; +import com.liferay.portal.kernel.util.PropsKeys; +import com.liferay.portal.kernel.util.ServerDetector; +import com.liferay.portal.kernel.util.SystemEnv; +import com.liferay.portal.kernel.util.Validator; +import com.liferay.portal.log.Log4jLogFactoryImpl; +import com.liferay.portal.util.PrefsPropsUtil; +import com.liferay.portal.util.PropsUtil; +import com.liferay.util.log4j.Log4JUtil; + +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.Future; + +import javax.portlet.PortletPreferences; + +/** + * @author Alexander Chow + */ +public class ImageMagickImpl implements ImageMagick { + + public static ImageMagickImpl getInstance() { + return _instance; + } + + public void convert(List arguments, boolean fork) throws Exception { + if (!isEnabled()) { + throw new IllegalStateException( + "Cannot call \"convert\" when ImageMagick is disabled"); + } + + if (fork) { + ProcessCallable processCallable = + new ImageMagickProcessCallable( + ServerDetector.getServerId(), + PropsUtil.get(PropsKeys.LIFERAY_HOME), + Log4JUtil.getCustomLogSettings(), _globalSearchPath, + getResourceLimits(), arguments, true); + + Future future = ProcessExecutor.execute( + ClassPathUtil.getPortalClassPath(), processCallable); + + future.get(); + } + else { + LiferayConvertCmd.run( + _globalSearchPath, getResourceLimits(), arguments); + } + } + + public String getGlobalSearchPath() throws Exception { + PortletPreferences preferences = PrefsPropsUtil.getPreferences(); + + String globalSearchPath = preferences.getValue( + PropsKeys.IMAGEMAGICK_GLOBAL_SEARCH_PATH, null); + + if (Validator.isNotNull(globalSearchPath)) { + return globalSearchPath; + } + + String filterName = null; + + if (OSDetector.isApple()) { + filterName = "apple"; + } + else if (OSDetector.isWindows()) { + filterName = "windows"; + } + else { + filterName = "unix"; + } + + return PropsUtil.get( + PropsKeys.IMAGEMAGICK_GLOBAL_SEARCH_PATH, new Filter(filterName)); + } + + public Properties getResourceLimitsProperties() throws Exception { + Properties resourceLimitsProperties = PrefsPropsUtil.getProperties( + PropsKeys.IMAGEMAGICK_RESOURCE_LIMIT, true); + + if (resourceLimitsProperties.isEmpty()) { + resourceLimitsProperties = PropsUtil.getProperties( + PropsKeys.IMAGEMAGICK_RESOURCE_LIMIT, true); + } + + return resourceLimitsProperties; + } + + public String[] identify(List arguments, boolean fork) + throws Exception { + + if (!isEnabled()) { + throw new IllegalStateException( + "Cannot call \"identify\" when ImageMagick is disabled"); + } + + if (fork) { + ProcessCallable processCallable = + new ImageMagickProcessCallable( + ServerDetector.getServerId(), + PropsUtil.get(PropsKeys.LIFERAY_HOME), + Log4JUtil.getCustomLogSettings(), _globalSearchPath, + getResourceLimits(), arguments, false); + + Future future = ProcessExecutor.execute( + ClassPathUtil.getPortalClassPath(), processCallable); + + return future.get(); + } + else { + return LiferayIdentifyCmd.run( + _globalSearchPath, getResourceLimits(), arguments); + } + } + + public boolean isEnabled() { + try { + return PrefsPropsUtil.getBoolean(PropsKeys.IMAGEMAGICK_ENABLED); + } + catch (Exception e) { + _log.warn(e, e); + } + + return false; + } + + public void reset() { + if (isEnabled()) { + try { + _globalSearchPath = getGlobalSearchPath(); + + _resourceLimitsProperties = getResourceLimitsProperties(); + } + catch (Exception e) { + _log.warn(e, e); + } + } + } + + protected LinkedList getResourceLimits() { + LinkedList resourceLimits = new LinkedList(); + + if (_resourceLimitsProperties == null) { + return resourceLimits; + } + + for (Object key : _resourceLimitsProperties.keySet()) { + String value = (String)_resourceLimitsProperties.get(key); + + if (Validator.isNull(value)) { + continue; + } + + resourceLimits.add("-limit"); + resourceLimits.add((String)key); + resourceLimits.add(value); + } + + return resourceLimits; + } + + private static Log _log = LogFactoryUtil.getLog(ImageMagickImpl.class); + + private static ImageMagickImpl _instance = new ImageMagickImpl(); + + private String _globalSearchPath; + private Properties _resourceLimitsProperties; + + private static class ImageMagickProcessCallable + implements ProcessCallable { + + public ImageMagickProcessCallable( + String serverId, String liferayHome, + Map customLogSettings, String globalSearchPath, + List resourceLimits, + List commandArguments, boolean convert) { + + _serverId = serverId; + _liferayHome = liferayHome; + _customLogSettings = customLogSettings; + _globalSearchPath = globalSearchPath; + _commandArguments = commandArguments; + _resourceLimits = resourceLimits; + _convert = convert; + } + + public String[] call() throws ProcessException { + Class clazz = getClass(); + + ClassLoader classLoader = clazz.getClassLoader(); + + Properties systemProperties = System.getProperties(); + + SystemEnv.setProperties(systemProperties); + + Log4JUtil.initLog4J( + _serverId, _liferayHome, classLoader, new Log4jLogFactoryImpl(), + _customLogSettings); + + try { + if (_convert) { + LiferayConvertCmd.run( + _globalSearchPath, _resourceLimits, _commandArguments); + } + else { + return LiferayIdentifyCmd.run( + _globalSearchPath, _resourceLimits, _commandArguments); + } + } + catch (Exception e) { + throw new ProcessException(e); + } + + return null; + } + + private List _commandArguments; + private boolean _convert; + private Map _customLogSettings; + private String _globalSearchPath; + private String _liferayHome; + private List _resourceLimits; + private String _serverId; + + } + +} \ No newline at end of file diff --git a/portal-impl/src/com/liferay/portal/image/ImageToolImpl.java b/portal-impl/src/com/liferay/portal/image/ImageToolImpl.java index de28513642f84c..71e117d8545cb8 100644 --- a/portal-impl/src/com/liferay/portal/image/ImageToolImpl.java +++ b/portal-impl/src/com/liferay/portal/image/ImageToolImpl.java @@ -49,8 +49,11 @@ import net.jmge.gif.Gif89Encoder; +import org.im4java.core.IMOperation; + /** * @author Brian Wing Shun Chan + * @author Alexander Chow */ public class ImageToolImpl implements ImageTool { @@ -58,6 +61,58 @@ public static ImageTool getInstance() { return _instance; } + public RenderedImage convertCMYKtoRGB( + byte[] bytes, String type, boolean fork) { + + if (!getImageMagickUtil().isEnabled()) { + return null; + } + + File inputFile = _fileUtil.createTempFile(type); + File outputFile = _fileUtil.createTempFile(type); + + try { + _fileUtil.write(inputFile, bytes); + + IMOperation imOperation = new IMOperation(); + + imOperation.addRawArgs("-format", "%[colorspace]"); + imOperation.addImage(inputFile.getPath()); + + String[] output = getImageMagickUtil().identify( + imOperation.getCmdArgs(), fork); + + if ((output.length == 1) && output[0].equalsIgnoreCase("CMYK")) { + if (_log.isInfoEnabled()) { + _log.info("The image is in the CMYK colorspace"); + } + + imOperation = new IMOperation(); + + imOperation.addRawArgs("-colorspace", "RGB"); + imOperation.addImage(inputFile.getPath()); + imOperation.addImage(outputFile.getPath()); + + getImageMagickUtil().convert(imOperation.getCmdArgs(), fork); + + bytes = _fileUtil.getBytes(outputFile); + + return read(bytes, type); + } + } + catch (Exception e) { + if (_log.isErrorEnabled()) { + _log.error(e, e); + } + } + finally { + _fileUtil.delete(inputFile); + _fileUtil.delete(outputFile); + } + + return null; + } + public BufferedImage convertImageType(BufferedImage sourceImage, int type) { BufferedImage targetImage = new BufferedImage( sourceImage.getWidth(), sourceImage.getHeight(), type); @@ -122,8 +177,8 @@ public void encodeWBMP(RenderedImage renderedImage, OutputStream os) os.write(0); os.write(0); - os.write(_toMultiByte(bufferedImage.getWidth())); - os.write(_toMultiByte(bufferedImage.getHeight())); + os.write(toMultiByte(bufferedImage.getWidth())); + os.write(toMultiByte(bufferedImage.getHeight())); DataBuffer dataBuffer = bufferedImage.getData().getDataBuffer(); @@ -169,17 +224,7 @@ public ImageBag read(byte[] bytes) { if (codec.isFormatRecognized(bytes)) { type = codec.getFormatName(); - ImageDecoder decoder = ImageCodec.createImageDecoder( - type, new UnsyncByteArrayInputStream(bytes), null); - - try { - renderedImage = decoder.decodeAsRenderedImage(); - } - catch (IOException ioe) { - if (_log.isDebugEnabled()) { - _log.debug(type + ": " + ioe.getMessage()); - } - } + renderedImage = read(bytes, type); break; } @@ -344,7 +389,39 @@ else if (contentType.contains(TYPE_TIFF) || } } - private byte[] _toMultiByte(int intValue) { + protected ImageMagickImpl getImageMagickUtil() { + if (_imageMagickUtil == null) { + _imageMagickUtil = ImageMagickImpl.getInstance(); + + _imageMagickUtil.reset(); + } + + return _imageMagickUtil; + } + + protected RenderedImage read(byte[] bytes, String type) { + RenderedImage renderedImage = null; + + try { + if (type.equals(TYPE_JPEG)) { + type = "jpeg"; + } + + ImageDecoder decoder = ImageCodec.createImageDecoder( + type, new UnsyncByteArrayInputStream(bytes), null); + + renderedImage = decoder.decodeAsRenderedImage(); + } + catch (IOException ioe) { + if (_log.isDebugEnabled()) { + _log.debug(type + ": " + ioe.getMessage()); + } + } + + return renderedImage; + } + + protected byte[] toMultiByte(int intValue) { int numBits = 32; int mask = 0x80000000; @@ -375,4 +452,6 @@ private byte[] _toMultiByte(int intValue) { private static FileImpl _fileUtil = FileImpl.getInstance(); + private static ImageMagickImpl _imageMagickUtil; + } \ No newline at end of file diff --git a/portal-impl/src/com/liferay/portlet/documentlibrary/util/LiferayConvertCmd.java b/portal-impl/src/com/liferay/portal/image/LiferayConvertCmd.java similarity index 78% rename from portal-impl/src/com/liferay/portlet/documentlibrary/util/LiferayConvertCmd.java rename to portal-impl/src/com/liferay/portal/image/LiferayConvertCmd.java index 5bffa92e777840..98e6d0fca86094 100644 --- a/portal-impl/src/com/liferay/portlet/documentlibrary/util/LiferayConvertCmd.java +++ b/portal-impl/src/com/liferay/portal/image/LiferayConvertCmd.java @@ -12,16 +12,14 @@ * details. */ -package com.liferay.portlet.documentlibrary.util; +package com.liferay.portal.image; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.util.StringBundler; -import com.liferay.portal.kernel.util.Validator; import java.util.LinkedList; import java.util.List; -import java.util.Properties; import jodd.util.StringPool; @@ -33,7 +31,7 @@ public class LiferayConvertCmd extends ConvertCmd { public static void run( - String globalSearchPath, Properties resourceLimitsProperties, + String globalSearchPath, List resourceLimits, List commandArguments) throws Exception { @@ -42,19 +40,7 @@ public static void run( LinkedList arguments = new LinkedList(); arguments.addAll(_instance.getCommand()); - - for (Object key : resourceLimitsProperties.keySet()) { - String value = (String)resourceLimitsProperties.get(key); - - if (Validator.isNull(value)) { - continue; - } - - arguments.add("-limit"); - arguments.add((String)key); - arguments.add(value); - } - + arguments.addAll(resourceLimits); arguments.addAll(commandArguments); if (_log.isInfoEnabled()) { diff --git a/portal-impl/src/com/liferay/portal/image/LiferayIdentifyCmd.java b/portal-impl/src/com/liferay/portal/image/LiferayIdentifyCmd.java new file mode 100644 index 00000000000000..f0aed110109137 --- /dev/null +++ b/portal-impl/src/com/liferay/portal/image/LiferayIdentifyCmd.java @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + */ + +package com.liferay.portal.image; + +import com.liferay.portal.kernel.log.Log; +import com.liferay.portal.kernel.log.LogFactoryUtil; +import com.liferay.portal.kernel.util.StringBundler; + +import java.util.LinkedList; +import java.util.List; + +import jodd.util.StringPool; + +import org.im4java.core.IdentifyCmd; +import org.im4java.process.ArrayListOutputConsumer; + +/** + * @author Alexander Chow + */ +public class LiferayIdentifyCmd extends IdentifyCmd { + + public static String[] run( + String globalSearchPath, List resourceLimits, + List commandArguments) + throws Exception { + + setGlobalSearchPath(globalSearchPath); + + LinkedList arguments = new LinkedList(); + + arguments.addAll(_instance.getCommand()); + arguments.addAll(resourceLimits); + arguments.addAll(commandArguments); + + if (_log.isInfoEnabled()) { + StringBundler sb = new StringBundler(arguments.size() * 2); + + for (String argument : arguments) { + sb.append(argument); + sb.append(StringPool.SPACE); + } + + _log.info("Excecuting command '" + sb.toString() + "'"); + } + + ArrayListOutputConsumer outputConsumer = new ArrayListOutputConsumer(); + + _instance.setOutputConsumer(outputConsumer); + _instance.run(arguments); + + List outputList = outputConsumer.getOutput(); + + if (outputList != null) { + return outputList.toArray(new String[0]); + } + else { + return new String[0]; + } + } + + private static Log _log = LogFactoryUtil.getLog(LiferayIdentifyCmd.class); + + private static LiferayIdentifyCmd _instance = new LiferayIdentifyCmd(); + +} \ No newline at end of file diff --git a/portal-impl/src/com/liferay/portlet/admin/action/EditServerAction.java b/portal-impl/src/com/liferay/portlet/admin/action/EditServerAction.java index 5b4ca40288a62b..5d9cc89b60822e 100644 --- a/portal-impl/src/com/liferay/portlet/admin/action/EditServerAction.java +++ b/portal-impl/src/com/liferay/portlet/admin/action/EditServerAction.java @@ -28,6 +28,7 @@ import com.liferay.portal.kernel.cluster.ClusterLinkUtil; import com.liferay.portal.kernel.cluster.ClusterRequest; import com.liferay.portal.kernel.dao.shard.ShardUtil; +import com.liferay.portal.kernel.image.ImageMagickUtil; import com.liferay.portal.kernel.io.unsync.UnsyncByteArrayOutputStream; import com.liferay.portal.kernel.io.unsync.UnsyncPrintWriter; import com.liferay.portal.kernel.log.Log; @@ -75,7 +76,6 @@ import com.liferay.portlet.ActionResponseImpl; import com.liferay.portlet.admin.util.CleanUpPermissionsUtil; import com.liferay.portlet.documentlibrary.util.DLPreviewableProcessor; -import com.liferay.portlet.documentlibrary.util.PDFProcessorUtil; import com.liferay.util.log4j.Log4JUtil; import java.io.File; @@ -499,7 +499,7 @@ protected void updateExternalServices( preferences.store(); - PDFProcessorUtil.reset(); + ImageMagickUtil.reset(); } protected void updateFileUploads( diff --git a/portal-impl/src/com/liferay/portlet/documentlibrary/util/ImageProcessorImpl.java b/portal-impl/src/com/liferay/portlet/documentlibrary/util/ImageProcessorImpl.java index 4d35a8a0903daf..141c0ba872745c 100644 --- a/portal-impl/src/com/liferay/portlet/documentlibrary/util/ImageProcessorImpl.java +++ b/portal-impl/src/com/liferay/portlet/documentlibrary/util/ImageProcessorImpl.java @@ -258,6 +258,17 @@ private void _generateImages(FileVersion fileVersion) { return; } + if (renderedImage.getColorModel().getNumComponents() == 4) { + RenderedImage convertedRenderedImage = + ImageToolUtil.convertCMYKtoRGB( + bytes, imageBag.getType(), + PropsValues.DL_FILE_ENTRY_PREVIEW_FORK_PROCESS_ENABLED); + + if (convertedRenderedImage != null) { + renderedImage = convertedRenderedImage; + } + } + if (!_hasPreview(fileVersion)) { _storePreviewImage(fileVersion, renderedImage); } diff --git a/portal-impl/src/com/liferay/portlet/documentlibrary/util/PDFProcessorImpl.java b/portal-impl/src/com/liferay/portlet/documentlibrary/util/PDFProcessorImpl.java index d92dedcc6ddac5..3c3094730f7bce 100644 --- a/portal-impl/src/com/liferay/portlet/documentlibrary/util/PDFProcessorImpl.java +++ b/portal-impl/src/com/liferay/portlet/documentlibrary/util/PDFProcessorImpl.java @@ -14,7 +14,7 @@ package com.liferay.portlet.documentlibrary.util; -import com.liferay.portal.kernel.configuration.Filter; +import com.liferay.portal.kernel.image.ImageMagickUtil; import com.liferay.portal.kernel.image.ImageToolUtil; import com.liferay.portal.kernel.lar.PortletDataContext; import com.liferay.portal.kernel.log.Log; @@ -22,10 +22,6 @@ import com.liferay.portal.kernel.messaging.DestinationNames; import com.liferay.portal.kernel.messaging.MessageBusException; import com.liferay.portal.kernel.messaging.MessageBusUtil; -import com.liferay.portal.kernel.process.ClassPathUtil; -import com.liferay.portal.kernel.process.ProcessCallable; -import com.liferay.portal.kernel.process.ProcessException; -import com.liferay.portal.kernel.process.ProcessExecutor; import com.liferay.portal.kernel.repository.model.FileEntry; import com.liferay.portal.kernel.repository.model.FileVersion; import com.liferay.portal.kernel.util.ContentTypes; @@ -33,17 +29,11 @@ import com.liferay.portal.kernel.util.GetterUtil; import com.liferay.portal.kernel.util.InstancePool; import com.liferay.portal.kernel.util.MimeTypesUtil; -import com.liferay.portal.kernel.util.OSDetector; -import com.liferay.portal.kernel.util.PropsKeys; import com.liferay.portal.kernel.util.StreamUtil; import com.liferay.portal.kernel.util.StringBundler; -import com.liferay.portal.kernel.util.StringPool; -import com.liferay.portal.kernel.util.SystemEnv; import com.liferay.portal.kernel.util.Validator; import com.liferay.portal.kernel.xml.Element; import com.liferay.portal.repository.liferayrepository.model.LiferayFileVersion; -import com.liferay.portal.util.PrefsPropsUtil; -import com.liferay.portal.util.PropsUtil; import com.liferay.portal.util.PropsValues; import com.liferay.portlet.documentlibrary.NoSuchFileEntryException; import com.liferay.portlet.documentlibrary.store.DLStoreUtil; @@ -57,17 +47,12 @@ import java.io.InputStream; import java.util.Arrays; -import java.util.LinkedList; import java.util.List; -import java.util.Properties; import java.util.Set; import java.util.Vector; -import java.util.concurrent.Future; import javax.imageio.ImageIO; -import javax.portlet.PortletPreferences; - import org.apache.commons.lang.time.StopWatch; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocumentCatalog; @@ -92,32 +77,6 @@ public void generateImages(FileVersion fileVersion) throws Exception { Initializer._initializedInstance._generateImages(fileVersion); } - public String getGlobalSearchPath() throws Exception { - PortletPreferences preferences = PrefsPropsUtil.getPreferences(); - - String globalSearchPath = preferences.getValue( - PropsKeys.IMAGEMAGICK_GLOBAL_SEARCH_PATH, null); - - if (Validator.isNotNull(globalSearchPath)) { - return globalSearchPath; - } - - String filterName = null; - - if (OSDetector.isApple()) { - filterName = "apple"; - } - else if (OSDetector.isWindows()) { - filterName = "windows"; - } - else { - filterName = "unix"; - } - - return PropsUtil.get( - PropsKeys.IMAGEMAGICK_GLOBAL_SEARCH_PATH, new Filter(filterName)); - } - public InputStream getPreviewAsStream(FileVersion fileVersion, int index) throws Exception { @@ -144,18 +103,6 @@ public long getPreviewFileSize(FileVersion fileVersion, int index) fileVersion, index); } - public Properties getResourceLimitsProperties() throws Exception { - Properties resourceLimitsProperties = PrefsPropsUtil.getProperties( - PropsKeys.IMAGEMAGICK_RESOURCE_LIMIT, true); - - if (resourceLimitsProperties.isEmpty()) { - resourceLimitsProperties = PropsUtil.getProperties( - PropsKeys.IMAGEMAGICK_RESOURCE_LIMIT, true); - } - - return resourceLimitsProperties; - } - public InputStream getThumbnailAsStream(FileVersion fileVersion, int index) throws Exception { @@ -194,7 +141,7 @@ public boolean isDocumentSupported(String mimeType) { } public boolean isImageMagickEnabled() throws Exception { - if (PrefsPropsUtil.getBoolean(PropsKeys.IMAGEMAGICK_ENABLED)) { + if (ImageMagickUtil.isEnabled()) { return true; } @@ -244,14 +191,6 @@ public boolean isSupported(String mimeType) { return false; } - public void reset() throws Exception { - if (isImageMagickEnabled()) { - _globalSearchPath = getGlobalSearchPath(); - - _resourceLimitsProperties = getResourceLimitsProperties(); - } - } - public void trigger(FileVersion fileVersion) { Initializer._initializedInstance._queueGeneration(fileVersion); } @@ -338,7 +277,7 @@ protected void initialize() { FileUtil.mkdirs(PREVIEW_TMP_PATH); FileUtil.mkdirs(THUMBNAIL_TMP_PATH); - reset(); + ImageMagickUtil.reset(); } catch (Exception e) { _log.warn(e, e); @@ -503,22 +442,9 @@ private void _generateImagesIM( imOperation.addImage(getPreviewTempFilePath(tempFileId, -1)); } - if (PropsValues.DL_FILE_ENTRY_PREVIEW_FORK_PROCESS_ENABLED) { - ProcessCallable processCallable = - new ImageMagickProcessCallable( - _globalSearchPath, _resourceLimitsProperties, - imOperation.getCmdArgs()); - - Future future = ProcessExecutor.execute( - ClassPathUtil.getPortalClassPath(), processCallable); - - future.get(); - } - else { - LiferayConvertCmd.run( - _globalSearchPath, _resourceLimitsProperties, - imOperation.getCmdArgs()); - } + ImageMagickUtil.convert( + imOperation.getCmdArgs(), + PropsValues.DL_FILE_ENTRY_PREVIEW_FORK_PROCESS_ENABLED); // Store images @@ -791,44 +717,8 @@ else if (DocumentConversionUtil.isEnabled()) { } private List _fileVersionIds = new Vector(); - private String _globalSearchPath; - private Properties _resourceLimitsProperties; private boolean _warned; - private static class ImageMagickProcessCallable - implements ProcessCallable { - - public ImageMagickProcessCallable( - String globalSearchPath, Properties resourceLimits, - LinkedList commandArguments) { - - _globalSearchPath = globalSearchPath; - _commandArguments = commandArguments; - _resourceLimits = resourceLimits; - } - - public String call() throws ProcessException { - Properties systemProperties = System.getProperties(); - - SystemEnv.setProperties(systemProperties); - - try { - LiferayConvertCmd.run( - _globalSearchPath, _resourceLimits, _commandArguments); - } - catch (Exception e) { - throw new ProcessException(e); - } - - return StringPool.BLANK; - } - - private LinkedList _commandArguments; - private String _globalSearchPath; - private Properties _resourceLimits; - - } - private static class Initializer { private static PDFProcessorImpl _initializedInstance; diff --git a/portal-service/src/com/liferay/portal/kernel/image/ImageMagick.java b/portal-service/src/com/liferay/portal/kernel/image/ImageMagick.java new file mode 100644 index 00000000000000..5da58702a45814 --- /dev/null +++ b/portal-service/src/com/liferay/portal/kernel/image/ImageMagick.java @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + */ + +package com.liferay.portal.kernel.image; + +import java.util.List; +import java.util.Properties; + +/** + * @author Alexander Chow + */ +public interface ImageMagick { + + public void convert(List arguments, boolean fork) + throws Exception; + + public String getGlobalSearchPath() throws Exception; + + public Properties getResourceLimitsProperties() throws Exception; + + public String[] identify(List arguments, boolean fork) + throws Exception; + + public boolean isEnabled(); + + public void reset(); + +} \ No newline at end of file diff --git a/portal-service/src/com/liferay/portal/kernel/image/ImageMagickUtil.java b/portal-service/src/com/liferay/portal/kernel/image/ImageMagickUtil.java new file mode 100644 index 00000000000000..bd7bf46f40c2ce --- /dev/null +++ b/portal-service/src/com/liferay/portal/kernel/image/ImageMagickUtil.java @@ -0,0 +1,107 @@ +/** + * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + */ + +package com.liferay.portal.kernel.image; + +import java.util.List; +import java.util.Properties; + +/** + * The ImageMagick utility class. + * + * @author Alexander Chow + */ +public class ImageMagickUtil { + + /** + * Executes the convert command in ImageMagick. + * + * @param arguments the command arguments being passed to convert + * + * @param fork whether to fork the process + * @throws Exception if an unexpected error occurred while executing command + * @see Convert + * documentation + */ + public static void convert(List arguments, boolean fork) + throws Exception { + + getImageMagick().convert(arguments, fork); + } + + /** + * Returns the global search path configured for ImageMagick. + * + * @return the global search path + * @throws Exception if an unexpected error occurred + */ + public static String getGlobalSearchPath() throws Exception { + return getImageMagick().getGlobalSearchPath(); + } + + public static ImageMagick getImageMagick() { + return _imageMagick; + } + + /** + * Returns the cache and resource usage limits configured for ImageMagick. + * + * @return the resource limits + * @throws Exception if an unexpected error occurred + */ + public static Properties getResourceLimitsProperties() throws Exception { + return getImageMagick().getResourceLimitsProperties(); + } + + /** + * Executes the identify command in ImageMagick. + * + * @param arguments the command arguments being passed to identify + * + * @param fork whether to fork the process + * @return the results of the identify call + * @throws Exception if an unexpected error occurred while executing command + * @see Identify + * documentation + */ + public static String[] identify(List arguments, boolean fork) + throws Exception { + + return getImageMagick().identify(arguments, fork); + } + + /** + * Returns true if ImageMagick is enabled. + * + * @return true if ImageMagick is enabled; false + * otherwise + */ + public static boolean isEnabled() { + return getImageMagick().isEnabled(); + } + + /** + * Resets the global search path and resource limits for ImageMagick. + */ + public static void reset() { + getImageMagick().reset(); + } + + public void setImageMagick(ImageMagick imageMagick) { + _imageMagick = imageMagick; + } + + private static ImageMagick _imageMagick; + +} \ No newline at end of file diff --git a/portal-service/src/com/liferay/portal/kernel/image/ImageTool.java b/portal-service/src/com/liferay/portal/kernel/image/ImageTool.java index 04a4c44c423adf..67c885e670a97c 100644 --- a/portal-service/src/com/liferay/portal/kernel/image/ImageTool.java +++ b/portal-service/src/com/liferay/portal/kernel/image/ImageTool.java @@ -23,6 +23,7 @@ /** * @author Brian Wing Shun Chan + * @author Alexander Chow */ public interface ImageTool { @@ -38,13 +39,16 @@ public interface ImageTool { public static final String TYPE_TIFF = "tiff"; + public RenderedImage convertCMYKtoRGB( + byte[] bytes, String type, boolean fork); + public BufferedImage convertImageType(BufferedImage sourceImage, int type); public void encodeGIF(RenderedImage renderedImage, OutputStream os) throws IOException; public void encodeWBMP(RenderedImage renderedImage, OutputStream os) - throws InterruptedException, IOException; + throws IOException; public BufferedImage getBufferedImage(RenderedImage renderedImage); @@ -55,26 +59,8 @@ public byte[] getBytes(RenderedImage renderedImage, String contentType) public ImageBag read(File file) throws IOException; - /** - * Scales the image based on the given width with the height calculated to - * preserve aspect ratio. - * - * @param renderedImage image to scale - * @param width used as new width and to calculate for new height - * @return scaled image - */ public RenderedImage scale(RenderedImage renderedImage, int width); - /** - * Scales the image based on the maximum height and width given while - * preserving the aspect ratio. If the image is already larger in both - * dimensions, the image will not be scaled. - * - * @param renderedImage image to scale - * @param maxHeight maximum height allowed for image - * @param maxWidth maximum width allowed for image - * @return scaled image - */ public RenderedImage scale( RenderedImage renderedImage, int maxHeight, int maxWidth); diff --git a/portal-service/src/com/liferay/portal/kernel/image/ImageToolUtil.java b/portal-service/src/com/liferay/portal/kernel/image/ImageToolUtil.java index 264a7482f03927..77d885d90ea550 100644 --- a/portal-service/src/com/liferay/portal/kernel/image/ImageToolUtil.java +++ b/portal-service/src/com/liferay/portal/kernel/image/ImageToolUtil.java @@ -22,32 +22,93 @@ import java.io.OutputStream; /** + * The Image utility class. + * * @author Brian Wing Shun Chan + * @author Alexander Chow */ public class ImageToolUtil { + /** + * Converts a CMYK image to RGB using ImageMagick. This must be run against + * the original byte[] and not one extracted from a {@link + * java.awt.image.RenderedImage}. The latter may potentially have been + * already been read incorrectly. + * + * @param bytes the image to convert + * @param type the image type (e.g., "gif", "jpg", etc.) + * @param fork whether to fork the process + * @return the converted image or null if ImageMagick was + * disabled or if the conversion was not completed. The conversion + * may not complete if (1) the image was not in the CMYK colorspace + * to begin with or (2) there was an error in the conversion + * process. + */ + public static RenderedImage convertCMYKtoRGB( + byte[] bytes, String type, boolean fork) { + + return getImageTool().convertCMYKtoRGB(bytes, type, fork); + } + + /** + * Converts an image from one image type to another. + * + * @param sourceImage the image to convert + * @param type the image type to convert to (e.g., "gif", "jpg", etc.) + * @return converted image + */ public static BufferedImage convertImageType( BufferedImage sourceImage, int type) { return getImageTool().convertImageType(sourceImage, type); } + /** + * Encodes an image using the GIF format. + * + * @param renderedImage the image to encode + * @param os the stream to write to + * @throws IOException if an error occurred during writing + */ public static void encodeGIF(RenderedImage renderedImage, OutputStream os) throws IOException { getImageTool().encodeGIF(renderedImage, os); } + /** + * Encodes an image using the WBMP format. + * + * @param renderedImage the image to encode + * @param os the stream to write to + * @throws IOException if an error occurred during writing + */ public static void encodeWBMP(RenderedImage renderedImage, OutputStream os) - throws InterruptedException, IOException { + throws IOException { getImageTool().encodeWBMP(renderedImage, os); } + /** + * Returns a {@link java.awt.image.BufferedImage} from a given {@link + * java.awt.image.RenderedImage}. + * + * @param renderedImage the original image + * @return the converted image + */ public static BufferedImage getBufferedImage(RenderedImage renderedImage) { return getImageTool().getBufferedImage(renderedImage); } + /** + * Returns the byte[] from an image. + * + * @param renderedImage the image to read + * @param contentType the content type (e.g., "image/jpeg") or image type + * (e.g., "jpg") to use during encoding + * @return the encoded image + * @throws IOException if an error occurred during writing + */ public static byte[] getBytes( RenderedImage renderedImage, String contentType) throws IOException { @@ -59,24 +120,69 @@ public static ImageTool getImageTool() { return _imageTool; } + /** + * Detects the image format and creates an {@link + * com.liferay.portal.kernel.image.ImageBag} containing the {@link + * java.awt.image.RenderedImage} and image type. + * + * @param bytes the bytes to read + * @return the {@link com.liferay.portal.kernel.image.ImageBag} + * @throws IOException if an error occurred during writing + */ public static ImageBag read(byte[] bytes) throws IOException { return getImageTool().read(bytes); } + /** + * Detects the image format and creates an {@link + * com.liferay.portal.kernel.image.ImageBag} containing the {@link + * java.awt.image.RenderedImage} and image type. + * + * @param file the file to read + * @return the {@link com.liferay.portal.kernel.image.ImageBag} + * @throws IOException if an error occurred during writing + */ public static ImageBag read(File file) throws IOException { return getImageTool().read(file); } + /** + * Scales the image based on the given width with the height calculated to + * preserve aspect ratio. + * + * @param renderedImage the image to scale + * @param width the new width; also used to calculate the new height + * @return the scaled image + */ public static RenderedImage scale(RenderedImage renderedImage, int width) { return getImageTool().scale(renderedImage, width); } + /** + * Scales the image based on the maximum height and width given while + * preserving the aspect ratio. If the image is already larger in both + * dimensions, the image will not be scaled. + * + * @param renderedImage the image to scale + * @param maxHeight the maximum height allowed for image + * @param maxWidth the maximum width allowed for image + * @return the scaled image + */ public static RenderedImage scale( RenderedImage renderedImage, int maxHeight, int maxWidth) { return getImageTool().scale(renderedImage, maxHeight, maxWidth); } + /** + * Encodes an image using. + * + * @param renderedImage the image to encode + * @param contentType the content type (e.g., "image/jpeg") or image type + * (e.g., "jpg") to use during encoding + * @param os the stream to write to + * @throws IOException if an error occurred during writing + */ public static void write( RenderedImage renderedImage, String contentType, OutputStream os) throws IOException { diff --git a/portal-service/src/com/liferay/portlet/documentlibrary/util/PDFProcessor.java b/portal-service/src/com/liferay/portlet/documentlibrary/util/PDFProcessor.java index 6bc6f05371aa58..bc58985266367c 100644 --- a/portal-service/src/com/liferay/portlet/documentlibrary/util/PDFProcessor.java +++ b/portal-service/src/com/liferay/portlet/documentlibrary/util/PDFProcessor.java @@ -22,8 +22,6 @@ import java.io.InputStream; -import java.util.Properties; - /** * @author Sergio González */ @@ -41,8 +39,6 @@ public void exportGeneratedFiles( public void generateImages(FileVersion fileVersion) throws Exception; - public String getGlobalSearchPath() throws Exception; - public InputStream getPreviewAsStream(FileVersion fileVersion, int index) throws Exception; @@ -51,8 +47,6 @@ public InputStream getPreviewAsStream(FileVersion fileVersion, int index) public long getPreviewFileSize(FileVersion fileVersion, int index) throws Exception; - public Properties getResourceLimitsProperties() throws Exception; - public InputStream getThumbnailAsStream(FileVersion fileVersion, int index) throws Exception; @@ -70,12 +64,8 @@ public void importGeneratedFiles( public boolean isDocumentSupported(String mimeType); - public boolean isImageMagickEnabled() throws Exception; - public boolean isSupported(String mimeType); - public void reset() throws Exception; - public void trigger(FileVersion fileVersion); } \ No newline at end of file diff --git a/portal-service/src/com/liferay/portlet/documentlibrary/util/PDFProcessorUtil.java b/portal-service/src/com/liferay/portlet/documentlibrary/util/PDFProcessorUtil.java index 8a53b5f61afa21..cac65dac2b3780 100644 --- a/portal-service/src/com/liferay/portlet/documentlibrary/util/PDFProcessorUtil.java +++ b/portal-service/src/com/liferay/portlet/documentlibrary/util/PDFProcessorUtil.java @@ -18,8 +18,6 @@ import java.io.InputStream; -import java.util.Properties; - /** * @author Sergio González */ @@ -31,10 +29,6 @@ public static void generateImages(FileVersion fileVersion) getPDFProcessor().generateImages(fileVersion); } - public static String getGlobalSearchPath() throws Exception { - return getPDFProcessor().getGlobalSearchPath(); - } - public static PDFProcessor getPDFProcessor() { return _pdfProcessor; } @@ -56,10 +50,6 @@ public static long getPreviewFileSize(FileVersion fileVersion, int index) return getPDFProcessor().getPreviewFileSize(fileVersion, index); } - public static Properties getResourceLimitsProperties() throws Exception { - return getPDFProcessor().getResourceLimitsProperties(); - } - public static InputStream getThumbnailAsStream( FileVersion fileVersion, int index) throws Exception { @@ -85,18 +75,10 @@ public static boolean isDocumentSupported(String mimeType) { return getPDFProcessor().isDocumentSupported(mimeType); } - public static boolean isImageMagickEnabled() throws Exception { - return getPDFProcessor().isImageMagickEnabled(); - } - public static boolean isSupported(String mimeType) { return getPDFProcessor().isSupported(mimeType); } - public static void reset() throws Exception { - getPDFProcessor().reset(); - } - public static void trigger(FileVersion fileVersion) { getPDFProcessor().trigger(fileVersion); } diff --git a/portal-web/docroot/html/portlet/admin/init.jsp b/portal-web/docroot/html/portlet/admin/init.jsp index 81b89e16451cc7..81962ba07411ae 100644 --- a/portal-web/docroot/html/portlet/admin/init.jsp +++ b/portal-web/docroot/html/portlet/admin/init.jsp @@ -23,6 +23,7 @@ page import="com.liferay.portal.captcha.recaptcha.ReCaptchaImpl" %><%@ page import="com.liferay.portal.convert.ConvertProcess" %><%@ page import="com.liferay.portal.dao.shard.ManualShardSelector" %><%@ page import="com.liferay.portal.kernel.dao.shard.ShardUtil" %><%@ +page import="com.liferay.portal.kernel.image.ImageMagickUtil" %><%@ page import="com.liferay.portal.kernel.plugin.PluginPackage" %><%@ page import="com.liferay.portal.kernel.scripting.ScriptingUtil" %><%@ page import="com.liferay.portal.kernel.util.InstancePool" %><%@ @@ -30,7 +31,6 @@ page import="com.liferay.portal.util.PortalInstances" %><%@ page import="com.liferay.portlet.documentlibrary.model.DLFileEntry" %><%@ page import="com.liferay.portlet.documentlibrary.model.DLFileVersion" %><%@ page import="com.liferay.portlet.documentlibrary.service.DLFileEntryLocalServiceUtil" %><%@ -page import="com.liferay.portlet.documentlibrary.util.PDFProcessorUtil" %><%@ page import="com.liferay.portlet.expando.model.ExpandoBridge" %><%@ page import="com.liferay.portlet.expando.model.ExpandoColumnConstants" %> diff --git a/portal-web/docroot/html/portlet/admin/server.jspf b/portal-web/docroot/html/portlet/admin/server.jspf index e3f171471138a7..3778aa1b797034 100644 --- a/portal-web/docroot/html/portlet/admin/server.jspf +++ b/portal-web/docroot/html/portlet/admin/server.jspf @@ -548,14 +548,14 @@ numberFormat.setMinimumIntegerDigits(2); - + - + <% - Properties resourceLimitsProperties = PDFProcessorUtil.getResourceLimitsProperties(); + Properties resourceLimitsProperties = ImageMagickUtil.getResourceLimitsProperties(); for (String label : _IMAGEMAGICK_RESOURCE_LIMIT_LABELS) { String name = "imageMagickLimit" + StringUtil.upperCaseFirstLetter(label);