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