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                    if (Validator.isNull(mimeType)) {
177                            return false;
178                    }
179    
180                    return _imageMimeTypes.contains(mimeType);
181            }
182    
183            @Override
184            public void storeThumbnail(
185                            long companyId, long groupId, long fileEntryId, long fileVersionId,
186                            long custom1ImageId, long custom2ImageId, InputStream is,
187                            String type)
188                    throws Exception {
189    
190                    _storeThumbnail(
191                            companyId, groupId, fileEntryId, fileVersionId, custom1ImageId,
192                            custom2ImageId, is, type);
193            }
194    
195            @Override
196            public void trigger(
197                    FileVersion sourceFileVersion, FileVersion destinationFileVersion) {
198    
199                    super.trigger(sourceFileVersion, destinationFileVersion);
200    
201                    _queueGeneration(sourceFileVersion, destinationFileVersion);
202            }
203    
204            @Override
205            protected void doExportGeneratedFiles(
206                            PortletDataContext portletDataContext, FileEntry fileEntry,
207                            Element fileEntryElement)
208                    throws Exception {
209    
210                    exportThumbnails(
211                            portletDataContext, fileEntry, fileEntryElement, "image");
212    
213                    exportPreview(portletDataContext, fileEntry, fileEntryElement);
214            }
215    
216            @Override
217            protected void doImportGeneratedFiles(
218                            PortletDataContext portletDataContext, FileEntry fileEntry,
219                            FileEntry importedFileEntry, Element fileEntryElement)
220                    throws Exception {
221    
222                    importThumbnails(
223                            portletDataContext, fileEntry, importedFileEntry, fileEntryElement,
224                            "image");
225    
226                    FileVersion importedFileVersion = importedFileEntry.getFileVersion();
227    
228                    if (!_previewGenerationRequired(importedFileVersion)) {
229                            return;
230                    }
231    
232                    importPreview(
233                            portletDataContext, fileEntry, importedFileEntry, fileEntryElement,
234                            "image", getPreviewType(importedFileVersion));
235            }
236    
237            protected void exportPreview(
238                            PortletDataContext portletDataContext, FileEntry fileEntry,
239                            Element fileEntryElement)
240                    throws Exception {
241    
242                    FileVersion fileVersion = fileEntry.getFileVersion();
243    
244                    if (!isSupported(fileVersion) ||
245                            !_previewGenerationRequired(fileVersion) ||
246                            !_hasPreview(fileVersion)) {
247    
248                            return;
249                    }
250    
251                    exportPreview(
252                            portletDataContext, fileEntry, fileEntryElement, "image",
253                            getPreviewType(fileVersion));
254            }
255    
256            @Override
257            protected List<Long> getFileVersionIds() {
258                    return _fileVersionIds;
259            }
260    
261            private void _generateImages(
262                            FileVersion sourceFileVersion, FileVersion destinationFileVersion)
263                    throws Exception {
264    
265                    InputStream inputStream = null;
266    
267                    try {
268                            if (sourceFileVersion != null) {
269                                    copy(sourceFileVersion, destinationFileVersion);
270    
271                                    return;
272                            }
273    
274                            if (!PropsValues.DL_FILE_ENTRY_THUMBNAIL_ENABLED &&
275                                    !PropsValues.DL_FILE_ENTRY_PREVIEW_ENABLED) {
276    
277                                    return;
278                            }
279    
280                            inputStream = destinationFileVersion.getContentStream(false);
281    
282                            byte[] bytes = FileUtil.getBytes(inputStream);
283    
284                            ImageBag imageBag = ImageToolUtil.read(bytes);
285    
286                            RenderedImage renderedImage = imageBag.getRenderedImage();
287    
288                            if (renderedImage == null) {
289                                    return;
290                            }
291    
292                            ColorModel colorModel = renderedImage.getColorModel();
293    
294                            if (colorModel.getNumColorComponents() == 4) {
295                                    Future<RenderedImage> future = ImageToolUtil.convertCMYKtoRGB(
296                                            bytes, imageBag.getType());
297    
298                                    if (future == null) {
299                                            return;
300                                    }
301    
302                                    String processIdentity = String.valueOf(
303                                            destinationFileVersion.getFileVersionId());
304    
305                                    futures.put(processIdentity, future);
306    
307                                    RenderedImage convertedRenderedImage = future.get();
308    
309                                    if (convertedRenderedImage != null) {
310                                            renderedImage = convertedRenderedImage;
311                                    }
312                            }
313    
314                            if (!_hasPreview(destinationFileVersion)) {
315                                    _storePreviewImage(destinationFileVersion, renderedImage);
316                            }
317    
318                            if (!hasThumbnails(destinationFileVersion)) {
319                                    storeThumbnailImages(destinationFileVersion, renderedImage);
320                            }
321                    }
322                    catch (NoSuchFileEntryException nsfee) {
323                    }
324                    finally {
325                            StreamUtil.cleanUp(inputStream);
326    
327                            _fileVersionIds.remove(destinationFileVersion.getFileVersionId());
328                    }
329            }
330    
331            private String _getType(FileVersion fileVersion) {
332                    String type = "png";
333    
334                    if (fileVersion == null) {
335                            return type;
336                    }
337    
338                    String extension = fileVersion.getExtension();
339    
340                    if (extension.equals("jpeg")) {
341                            type = "jpg";
342                    }
343                    else if (!_previewGenerationRequired(fileVersion)) {
344                            type = extension;
345                    }
346    
347                    return type;
348            }
349    
350            private boolean _hasPreview(FileVersion fileVersion)
351                    throws PortalException {
352    
353                    if (PropsValues.DL_FILE_ENTRY_PREVIEW_ENABLED &&
354                            _previewGenerationRequired(fileVersion)) {
355    
356                            String type = getPreviewType(fileVersion);
357    
358                            String previewFilePath = getPreviewFilePath(fileVersion, type);
359    
360                            if (!DLStoreUtil.hasFile(
361                                            fileVersion.getCompanyId(), REPOSITORY_ID,
362                                            previewFilePath)) {
363    
364                                    return false;
365                            }
366                    }
367    
368                    return true;
369            }
370    
371            private boolean _previewGenerationRequired(FileVersion fileVersion) {
372                    String type = fileVersion.getExtension();
373    
374                    if (type.equals("tiff") || type.equals("tif")) {
375                            return true;
376                    }
377                    else {
378                            return false;
379                    }
380            }
381    
382            private void _queueGeneration(
383                    FileVersion sourceFileVersion, FileVersion destinationFileVersion) {
384    
385                    if (_fileVersionIds.contains(
386                                    destinationFileVersion.getFileVersionId()) ||
387                            !isSupported(destinationFileVersion)) {
388    
389                            return;
390                    }
391    
392                    _fileVersionIds.add(destinationFileVersion.getFileVersionId());
393    
394                    sendGenerationMessage(
395                            DestinationNames.DOCUMENT_LIBRARY_IMAGE_PROCESSOR,
396                            sourceFileVersion, destinationFileVersion);
397            }
398    
399            private void _storePreviewImage(
400                            FileVersion fileVersion, RenderedImage renderedImage)
401                    throws Exception {
402    
403                    String type = getPreviewType(fileVersion);
404    
405                    File file = null;
406    
407                    try {
408                            file = FileUtil.createTempFile(type);
409    
410                            try (FileOutputStream fos = new FileOutputStream(file)) {
411                                    ImageToolUtil.write(renderedImage, type, fos);
412                            }
413    
414                            addFileToStore(
415                                    fileVersion.getCompanyId(), PREVIEW_PATH,
416                                    getPreviewFilePath(fileVersion, type), file);
417                    }
418                    finally {
419                            FileUtil.delete(file);
420                    }
421            }
422    
423            private void _storeThumbnail(
424                            long companyId, long groupId, long fileEntryId, long fileVersionId,
425                            long custom1ImageId, long custom2ImageId, InputStream is,
426                            String type)
427                    throws Exception {
428    
429                    StringBundler sb = new StringBundler(5);
430    
431                    sb.append(getPathSegment(groupId, fileEntryId, fileVersionId, false));
432    
433                    if (custom1ImageId != 0) {
434                            sb.append(StringPool.DASH);
435                            sb.append(1);
436                    }
437                    else if (custom2ImageId != 0) {
438                            sb.append(StringPool.DASH);
439                            sb.append(2);
440                    }
441    
442                    if (Validator.isNotNull(type)) {
443                            sb.append(StringPool.PERIOD);
444                            sb.append(type);
445                    }
446    
447                    String filePath = sb.toString();
448    
449                    File file = null;
450    
451                    try {
452                            file = FileUtil.createTempFile(is);
453    
454                            addFileToStore(companyId, THUMBNAIL_PATH, filePath, file);
455                    }
456                    finally {
457                            FileUtil.delete(file);
458                    }
459            }
460    
461            private static final Log _log = LogFactoryUtil.getLog(
462                    ImageProcessorImpl.class);
463    
464            private final List<Long> _fileVersionIds = new Vector<Long>();
465            private final Set<String> _imageMimeTypes = SetUtil.fromArray(
466                    PropsValues.DL_FILE_ENTRY_PREVIEW_IMAGE_MIME_TYPES);
467    
468    }