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