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