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