001    /**
002     * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portlet.documentlibrary.util;
016    
017    import com.liferay.portal.kernel.exception.PortalException;
018    import com.liferay.portal.kernel.exception.SystemException;
019    import com.liferay.portal.kernel.image.ImageBag;
020    import com.liferay.portal.kernel.image.ImageToolUtil;
021    import com.liferay.portal.kernel.lar.PortletDataContext;
022    import com.liferay.portal.kernel.log.Log;
023    import com.liferay.portal.kernel.log.LogFactoryUtil;
024    import com.liferay.portal.kernel.messaging.DestinationNames;
025    import com.liferay.portal.kernel.repository.model.FileEntry;
026    import com.liferay.portal.kernel.repository.model.FileVersion;
027    import com.liferay.portal.kernel.util.FileUtil;
028    import com.liferay.portal.kernel.util.SetUtil;
029    import com.liferay.portal.kernel.util.StreamUtil;
030    import com.liferay.portal.kernel.util.StringBundler;
031    import com.liferay.portal.kernel.util.StringPool;
032    import com.liferay.portal.kernel.util.Validator;
033    import com.liferay.portal.kernel.xml.Element;
034    import com.liferay.portal.util.PropsValues;
035    import com.liferay.portlet.documentlibrary.NoSuchFileEntryException;
036    import com.liferay.portlet.documentlibrary.store.DLStoreUtil;
037    
038    import java.awt.image.RenderedImage;
039    
040    import java.io.File;
041    import java.io.FileOutputStream;
042    import java.io.InputStream;
043    
044    import java.util.List;
045    import java.util.Set;
046    import java.util.Vector;
047    import java.util.concurrent.Future;
048    
049    /**
050     * @author Sergio González
051     * @author Alexander Chow
052     * @author Ivica Cardic
053     */
054    public class ImageProcessorImpl
055            extends DLPreviewableProcessor implements ImageProcessor {
056    
057            public void afterPropertiesSet() {
058            }
059    
060            @Override
061            public void cleanUp(FileEntry fileEntry) {
062                    deleteFiles(fileEntry, null);
063            }
064    
065            @Override
066            public void cleanUp(FileVersion fileVersion) {
067                    String type = getThumbnailType(fileVersion);
068    
069                    deleteFiles(fileVersion, type);
070            }
071    
072            public void generateImages(
073                            FileVersion sourceFileVersion, FileVersion destinationFileVersion)
074                    throws Exception {
075    
076                    _generateImages(sourceFileVersion, destinationFileVersion);
077            }
078    
079            public Set<String> getImageMimeTypes() {
080                    return _imageMimeTypes;
081            }
082    
083            public InputStream getPreviewAsStream(FileVersion fileVersion)
084                    throws Exception {
085    
086                    if (_previewGenerationRequired(fileVersion)) {
087                            String type = getPreviewType(fileVersion);
088    
089                            return doGetPreviewAsStream(fileVersion, type);
090                    }
091    
092                    return fileVersion.getContentStream(false);
093            }
094    
095            public long getPreviewFileSize(FileVersion fileVersion) throws Exception {
096                    if (_previewGenerationRequired(fileVersion)) {
097                            String type = getPreviewType(fileVersion);
098    
099                            return doGetPreviewFileSize(fileVersion, type);
100                    }
101    
102                    return fileVersion.getSize();
103            }
104    
105            @Override
106            public String getPreviewType(FileVersion fileVersion) {
107                    return _getType(fileVersion);
108            }
109    
110            public InputStream getThumbnailAsStream(FileVersion fileVersion, int index)
111                    throws Exception {
112    
113                    return doGetThumbnailAsStream(fileVersion, index);
114            }
115    
116            public long getThumbnailFileSize(FileVersion fileVersion, int index)
117                    throws Exception {
118    
119                    return doGetThumbnailFileSize(fileVersion, index);
120            }
121    
122            @Override
123            public String getThumbnailType(FileVersion fileVersion) {
124                    return _getType(fileVersion);
125            }
126    
127            public boolean hasImages(FileVersion fileVersion) {
128                    if (!PropsValues.DL_FILE_ENTRY_PREVIEW_ENABLED &&
129                            !PropsValues.DL_FILE_ENTRY_THUMBNAIL_ENABLED) {
130    
131                            return false;
132                    }
133    
134                    boolean hasImages = false;
135    
136                    try {
137                            if (_hasPreview(fileVersion) && hasThumbnails(fileVersion)) {
138    
139                                    hasImages = true;
140                            }
141    
142                            if (!hasImages && isSupported(fileVersion)) {
143                                    _queueGeneration(null, fileVersion);
144                            }
145                    }
146                    catch (Exception e) {
147                            _log.error(e, e);
148                    }
149    
150                    return hasImages;
151            }
152    
153            public boolean isImageSupported(FileVersion fileVersion) {
154                    return isSupported(fileVersion);
155            }
156    
157            public boolean isImageSupported(String mimeType) {
158                    return isSupported(mimeType);
159            }
160    
161            public boolean isSupported(String mimeType) {
162                    if (Validator.isNull(mimeType)) {
163                            return false;
164                    }
165    
166                    return _imageMimeTypes.contains(mimeType);
167            }
168    
169            public void storeThumbnail(
170                            long companyId, long groupId, long fileEntryId, long fileVersionId,
171                            long custom1ImageId, long custom2ImageId, InputStream is,
172                            String type)
173                    throws Exception {
174    
175                    _storeThumbnail(
176                            companyId, groupId, fileEntryId, fileVersionId, custom1ImageId,
177                            custom2ImageId, is, type);
178            }
179    
180            @Override
181            public void trigger(
182                    FileVersion sourceFileVersion, FileVersion destinationFileVersion) {
183    
184                    super.trigger(sourceFileVersion, destinationFileVersion);
185    
186                    _queueGeneration(sourceFileVersion, destinationFileVersion);
187            }
188    
189            @Override
190            protected void doExportGeneratedFiles(
191                            PortletDataContext portletDataContext, FileEntry fileEntry,
192                            Element fileEntryElement)
193                    throws Exception {
194    
195                    exportThumbnails(
196                            portletDataContext, fileEntry, fileEntryElement, "image");
197    
198                    exportPreview(portletDataContext, fileEntry, fileEntryElement);
199            }
200    
201            @Override
202            protected void doImportGeneratedFiles(
203                            PortletDataContext portletDataContext, FileEntry fileEntry,
204                            FileEntry importedFileEntry, Element fileEntryElement)
205                    throws Exception {
206    
207                    importThumbnails(
208                            portletDataContext, fileEntry, importedFileEntry, fileEntryElement,
209                            "image");
210    
211                    FileVersion importedFileVersion = importedFileEntry.getFileVersion();
212    
213                    if (!_previewGenerationRequired(importedFileVersion)) {
214                            return;
215                    }
216    
217                    importPreview(
218                            portletDataContext, fileEntry, importedFileEntry, fileEntryElement,
219                            "image", getPreviewType(importedFileVersion));
220            }
221    
222            protected void exportPreview(
223                            PortletDataContext portletDataContext, FileEntry fileEntry,
224                            Element fileEntryElement)
225                    throws Exception {
226    
227                    FileVersion fileVersion = fileEntry.getFileVersion();
228    
229                    if (!isSupported(fileVersion) ||
230                            !_previewGenerationRequired(fileVersion) ||
231                            !_hasPreview(fileVersion)) {
232    
233                            return;
234                    }
235    
236                    exportPreview(
237                            portletDataContext, fileEntry, fileEntryElement, "image",
238                            getPreviewType(fileVersion));
239            }
240    
241            @Override
242            protected List<Long> getFileVersionIds() {
243                    return _fileVersionIds;
244            }
245    
246            private void _generateImages(
247                            FileVersion sourceFileVersion, FileVersion destinationFileVersion)
248                    throws Exception {
249    
250                    InputStream inputStream = null;
251    
252                    try {
253                            if (sourceFileVersion != null) {
254                                    copy(sourceFileVersion, destinationFileVersion);
255    
256                                    return;
257                            }
258    
259                            if (!PropsValues.DL_FILE_ENTRY_THUMBNAIL_ENABLED &&
260                                    !PropsValues.DL_FILE_ENTRY_PREVIEW_ENABLED) {
261    
262                                    return;
263                            }
264    
265                            inputStream = destinationFileVersion.getContentStream(false);
266    
267                            byte[] bytes = FileUtil.getBytes(inputStream);
268    
269                            ImageBag imageBag = ImageToolUtil.read(bytes);
270    
271                            RenderedImage renderedImage = imageBag.getRenderedImage();
272    
273                            if (renderedImage == null) {
274                                    return;
275                            }
276    
277                            if (renderedImage.getColorModel().getNumComponents() == 4) {
278                                    Future<RenderedImage> future = ImageToolUtil.convertCMYKtoRGB(
279                                            bytes, imageBag.getType());
280    
281                                    if (future == null) {
282                                            return;
283                                    }
284    
285                                    String processIdentity = String.valueOf(
286                                            destinationFileVersion.getFileVersionId());
287    
288                                    futures.put(processIdentity, future);
289    
290                                    RenderedImage convertedRenderedImage = future.get();
291    
292                                    if (convertedRenderedImage != null) {
293                                            renderedImage = convertedRenderedImage;
294                                    }
295                            }
296    
297                            if (!_hasPreview(destinationFileVersion)) {
298                                    _storePreviewImage(destinationFileVersion, renderedImage);
299                            }
300    
301                            if (!hasThumbnails(destinationFileVersion)) {
302                                    storeThumbnailImages(destinationFileVersion, renderedImage);
303                            }
304                    }
305                    catch (NoSuchFileEntryException nsfee) {
306                    }
307                    finally {
308                            StreamUtil.cleanUp(inputStream);
309    
310                            _fileVersionIds.remove(destinationFileVersion.getFileVersionId());
311                    }
312            }
313    
314            private String _getType(FileVersion fileVersion) {
315                    String type = "png";
316    
317                    if (fileVersion == null) {
318                            return type;
319                    }
320    
321                    String extension = fileVersion.getExtension();
322    
323                    if (extension.equals("jpeg")) {
324                            type = "jpg";
325                    }
326                    else if (!_previewGenerationRequired(fileVersion)) {
327                            type = extension;
328                    }
329    
330                    return type;
331            }
332    
333            private boolean _hasPreview(FileVersion fileVersion)
334                    throws PortalException, SystemException {
335    
336                    if (PropsValues.DL_FILE_ENTRY_PREVIEW_ENABLED &&
337                            _previewGenerationRequired(fileVersion)) {
338    
339                            String type = getPreviewType(fileVersion);
340    
341                            String previewFilePath = getPreviewFilePath(fileVersion, type);
342    
343                            if (!DLStoreUtil.hasFile(
344                                            fileVersion.getCompanyId(), REPOSITORY_ID,
345                                            previewFilePath)) {
346    
347                                    return false;
348                            }
349                    }
350    
351                    return true;
352            }
353    
354            private boolean _previewGenerationRequired(FileVersion fileVersion) {
355                    String type = fileVersion.getExtension();
356    
357                    if (type.equals("tiff") || type.equals("tif")) {
358                            return true;
359                    }
360                    else {
361                            return false;
362                    }
363            }
364    
365            private void _queueGeneration(
366                    FileVersion sourceFileVersion, FileVersion destinationFileVersion) {
367    
368                    if (_fileVersionIds.contains(
369                                    destinationFileVersion.getFileVersionId()) ||
370                            !isSupported(destinationFileVersion)) {
371    
372                            return;
373                    }
374    
375                    _fileVersionIds.add(destinationFileVersion.getFileVersionId());
376    
377                    sendGenerationMessage(
378                            DestinationNames.DOCUMENT_LIBRARY_IMAGE_PROCESSOR,
379                            PropsValues.DL_FILE_ENTRY_PROCESSORS_TRIGGER_SYNCHRONOUSLY,
380                            sourceFileVersion, destinationFileVersion);
381            }
382    
383            private void _storePreviewImage(
384                            FileVersion fileVersion, RenderedImage renderedImage)
385                    throws Exception {
386    
387                    String type = getPreviewType(fileVersion);
388    
389                    File file = null;
390    
391                    try {
392                            file = FileUtil.createTempFile(type);
393    
394                            FileOutputStream fos = new FileOutputStream(file);
395    
396                            try {
397                                    ImageToolUtil.write(renderedImage, type, fos);
398                            }
399                            finally {
400                                    fos.close();
401                            }
402    
403                            addFileToStore(
404                                    fileVersion.getCompanyId(), PREVIEW_PATH,
405                                    getPreviewFilePath(fileVersion, type), file);
406                    }
407                    finally {
408                            FileUtil.delete(file);
409                    }
410            }
411    
412            private void _storeThumbnail(
413                            long companyId, long groupId, long fileEntryId, long fileVersionId,
414                            long custom1ImageId, long custom2ImageId, InputStream is,
415                            String type)
416                    throws Exception {
417    
418                    StringBundler sb = new StringBundler(5);
419    
420                    sb.append(getPathSegment(groupId, fileEntryId, fileVersionId, false));
421    
422                    if (custom1ImageId != 0) {
423                            sb.append(StringPool.DASH);
424                            sb.append(1);
425                    }
426                    else if (custom2ImageId != 0) {
427                            sb.append(StringPool.DASH);
428                            sb.append(2);
429                    }
430    
431                    sb.append(StringPool.PERIOD);
432                    sb.append(type);
433    
434                    String filePath = sb.toString();
435    
436                    File file = null;
437    
438                    try {
439                            file = FileUtil.createTempFile(is);
440    
441                            addFileToStore(companyId, THUMBNAIL_PATH, filePath, file);
442                    }
443                    finally {
444                            FileUtil.delete(file);
445                    }
446            }
447    
448            private static Log _log = LogFactoryUtil.getLog(ImageProcessorImpl.class);
449    
450            private List<Long> _fileVersionIds = new Vector<Long>();
451            private Set<String> _imageMimeTypes = SetUtil.fromArray(
452                    PropsValues.DL_FILE_ENTRY_PREVIEW_IMAGE_MIME_TYPES);
453    
454    }