001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
013     */
014    
015    package com.liferay.portal.image;
016    
017    import com.liferay.portal.kernel.image.ImageBag;
018    import com.liferay.portal.kernel.image.ImageMagick;
019    import com.liferay.portal.kernel.image.ImageTool;
020    import com.liferay.portal.kernel.io.unsync.UnsyncByteArrayInputStream;
021    import com.liferay.portal.kernel.io.unsync.UnsyncByteArrayOutputStream;
022    import com.liferay.portal.kernel.log.Log;
023    import com.liferay.portal.kernel.log.LogFactoryUtil;
024    import com.liferay.portal.kernel.security.pacl.DoPrivileged;
025    import com.liferay.portal.kernel.util.ArrayUtil;
026    import com.liferay.portal.kernel.util.JavaDetector;
027    import com.liferay.portal.kernel.util.PropsKeys;
028    import com.liferay.portal.kernel.util.StringUtil;
029    import com.liferay.portal.model.Image;
030    import com.liferay.portal.model.impl.ImageImpl;
031    import com.liferay.portal.util.FileImpl;
032    import com.liferay.portal.util.PropsUtil;
033    import com.liferay.portal.util.PropsValues;
034    
035    import com.sun.media.jai.codec.ImageCodec;
036    import com.sun.media.jai.codec.ImageDecoder;
037    import com.sun.media.jai.codec.ImageEncoder;
038    
039    import java.awt.AlphaComposite;
040    import java.awt.Graphics;
041    import java.awt.Graphics2D;
042    import java.awt.Rectangle;
043    import java.awt.RenderingHints;
044    import java.awt.image.BufferedImage;
045    import java.awt.image.ColorModel;
046    import java.awt.image.DataBuffer;
047    import java.awt.image.IndexColorModel;
048    import java.awt.image.RenderedImage;
049    import java.awt.image.SampleModel;
050    
051    import java.io.ByteArrayInputStream;
052    import java.io.File;
053    import java.io.IOException;
054    import java.io.InputStream;
055    import java.io.OutputStream;
056    
057    import java.util.Arrays;
058    import java.util.Enumeration;
059    import java.util.HashMap;
060    import java.util.Iterator;
061    import java.util.LinkedList;
062    import java.util.Map;
063    import java.util.Queue;
064    import java.util.concurrent.ExecutionException;
065    import java.util.concurrent.Future;
066    import java.util.concurrent.TimeUnit;
067    import java.util.concurrent.TimeoutException;
068    
069    import javax.imageio.ImageIO;
070    import javax.imageio.ImageReader;
071    import javax.imageio.spi.IIORegistry;
072    import javax.imageio.spi.ImageReaderSpi;
073    import javax.imageio.stream.ImageInputStream;
074    
075    import javax.media.jai.RenderedImageAdapter;
076    
077    import net.jmge.gif.Gif89Encoder;
078    
079    import org.im4java.core.IMOperation;
080    
081    import org.monte.media.jpeg.CMYKJPEGImageReaderSpi;
082    
083    /**
084     * @author Brian Wing Shun Chan
085     * @author Alexander Chow
086     * @author Shuyang Zhou
087     */
088    @DoPrivileged
089    public class ImageToolImpl implements ImageTool {
090    
091            public static ImageTool getInstance() {
092                    return _instance;
093            }
094    
095            public void afterPropertiesSet() {
096                    ClassLoader classLoader = getClass().getClassLoader();
097    
098                    try {
099                            InputStream is = classLoader.getResourceAsStream(
100                                    PropsUtil.get(PropsKeys.IMAGE_DEFAULT_SPACER));
101    
102                            if (is == null) {
103                                    _log.error("Default spacer is not available");
104                            }
105    
106                            _defaultSpacer = getImage(is);
107                    }
108                    catch (Exception e) {
109                            _log.error(
110                                    "Unable to configure the default spacer: " + e.getMessage());
111                    }
112    
113                    try {
114                            InputStream is = classLoader.getResourceAsStream(
115                                    PropsUtil.get(PropsKeys.IMAGE_DEFAULT_COMPANY_LOGO));
116    
117                            if (is == null) {
118                                    _log.error("Default company logo is not available");
119                            }
120    
121                            _defaultCompanyLogo = getImage(is);
122                    }
123                    catch (Exception e) {
124                            _log.error(
125                                    "Unable to configure the default company logo: " +
126                                            e.getMessage());
127                    }
128    
129                    try {
130                            InputStream is = classLoader.getResourceAsStream(
131                                    PropsUtil.get(PropsKeys.IMAGE_DEFAULT_ORGANIZATION_LOGO));
132    
133                            if (is == null) {
134                                    _log.error("Default organization logo is not available");
135                            }
136    
137                            _defaultOrganizationLogo = getImage(is);
138                    }
139                    catch (Exception e) {
140                            _log.error(
141                                    "Unable to configure the default organization logo: " +
142                                            e.getMessage());
143                    }
144    
145                    try {
146                            InputStream is = classLoader.getResourceAsStream(
147                                    PropsUtil.get(PropsKeys.IMAGE_DEFAULT_USER_FEMALE_PORTRAIT));
148    
149                            if (is == null) {
150                                    _log.error("Default user female portrait is not available");
151                            }
152    
153                            _defaultUserFemalePortrait = getImage(is);
154                    }
155                    catch (Exception e) {
156                            _log.error(
157                                    "Unable to configure the default user female portrait: " +
158                                            e.getMessage());
159                    }
160    
161                    try {
162                            InputStream is = classLoader.getResourceAsStream(
163                                    PropsUtil.get(PropsKeys.IMAGE_DEFAULT_USER_MALE_PORTRAIT));
164    
165                            if (is == null) {
166                                    _log.error("Default user male portrait is not available");
167                            }
168    
169                            _defaultUserMalePortrait = getImage(is);
170                    }
171                    catch (Exception e) {
172                            _log.error(
173                                    "Unable to configure the default user male portrait: " +
174                                            e.getMessage());
175                    }
176            }
177    
178            @Override
179            public Future<RenderedImage> convertCMYKtoRGB(byte[] bytes, String type) {
180                    ImageMagick imageMagick = getImageMagick();
181    
182                    if (!imageMagick.isEnabled()) {
183                            return null;
184                    }
185    
186                    File inputFile = _fileUtil.createTempFile(type);
187                    File outputFile = _fileUtil.createTempFile(type);
188    
189                    try {
190                            _fileUtil.write(inputFile, bytes);
191    
192                            IMOperation imOperation = new IMOperation();
193    
194                            imOperation.addRawArgs("-format", "%[colorspace]");
195                            imOperation.addImage(inputFile.getPath());
196    
197                            String[] output = imageMagick.identify(imOperation.getCmdArgs());
198    
199                            if ((output.length == 1) &&
200                                    StringUtil.equalsIgnoreCase(output[0], "CMYK")) {
201    
202                                    if (_log.isInfoEnabled()) {
203                                            _log.info("The image is in the CMYK colorspace");
204                                    }
205    
206                                    imOperation = new IMOperation();
207    
208                                    imOperation.addRawArgs("-colorspace", "RGB");
209                                    imOperation.addImage(inputFile.getPath());
210                                    imOperation.addImage(outputFile.getPath());
211    
212                                    Future<?> future = imageMagick.convert(
213                                            imOperation.getCmdArgs());
214    
215                                    return new RenderedImageFuture(future, outputFile, type);
216                            }
217                    }
218                    catch (Exception e) {
219                            if (_log.isErrorEnabled()) {
220                                    _log.error(e, e);
221                            }
222                    }
223                    finally {
224                            _fileUtil.delete(inputFile);
225                            _fileUtil.delete(outputFile);
226                    }
227    
228                    return null;
229            }
230    
231            @Override
232            public BufferedImage convertImageType(BufferedImage sourceImage, int type) {
233                    BufferedImage targetImage = new BufferedImage(
234                            sourceImage.getWidth(), sourceImage.getHeight(), type);
235    
236                    Graphics2D graphics = targetImage.createGraphics();
237    
238                    graphics.drawRenderedImage(sourceImage, null);
239    
240                    graphics.dispose();
241    
242                    return targetImage;
243            }
244    
245            @Override
246            public RenderedImage crop(
247                    RenderedImage renderedImage, int height, int width, int x, int y) {
248    
249                    Rectangle rectangle = new Rectangle(x, y, width, height);
250    
251                    Rectangle croppedRectangle = rectangle.intersection(
252                            new Rectangle(renderedImage.getWidth(), renderedImage.getHeight()));
253    
254                    BufferedImage bufferedImage = getBufferedImage(renderedImage);
255    
256                    return bufferedImage.getSubimage(
257                            croppedRectangle.x, croppedRectangle.y, croppedRectangle.width,
258                            croppedRectangle.height);
259            }
260    
261            @Override
262            public void encodeGIF(RenderedImage renderedImage, OutputStream os)
263                    throws IOException {
264    
265                    if (JavaDetector.isJDK6()) {
266                            ImageIO.write(renderedImage, TYPE_GIF, os);
267                    }
268                    else {
269                            BufferedImage bufferedImage = getBufferedImage(renderedImage);
270    
271                            if (!(bufferedImage.getColorModel() instanceof IndexColorModel)) {
272                                    bufferedImage = convertImageType(
273                                            bufferedImage, BufferedImage.TYPE_BYTE_INDEXED);
274                            }
275    
276                            Gif89Encoder encoder = new Gif89Encoder(bufferedImage);
277    
278                            encoder.encode(os);
279                    }
280            }
281    
282            @Override
283            public void encodeWBMP(RenderedImage renderedImage, OutputStream os)
284                    throws IOException {
285    
286                    BufferedImage bufferedImage = getBufferedImage(renderedImage);
287    
288                    SampleModel sampleModel = bufferedImage.getSampleModel();
289    
290                    int type = sampleModel.getDataType();
291    
292                    if ((bufferedImage.getType() != BufferedImage.TYPE_BYTE_BINARY) ||
293                            (type < DataBuffer.TYPE_BYTE) || (type > DataBuffer.TYPE_INT) ||
294                            (sampleModel.getNumBands() != 1) ||
295                            (sampleModel.getSampleSize(0) != 1)) {
296    
297                            BufferedImage binaryImage = new BufferedImage(
298                                    bufferedImage.getWidth(), bufferedImage.getHeight(),
299                                    BufferedImage.TYPE_BYTE_BINARY);
300    
301                            Graphics graphics = binaryImage.getGraphics();
302    
303                            graphics.drawImage(bufferedImage, 0, 0, null);
304    
305                            renderedImage = binaryImage;
306                    }
307    
308                    if (!ImageIO.write(renderedImage, "wbmp", os)) {
309    
310                            // See http://www.jguru.com/faq/view.jsp?EID=127723
311    
312                            os.write(0);
313                            os.write(0);
314                            os.write(toMultiByte(bufferedImage.getWidth()));
315                            os.write(toMultiByte(bufferedImage.getHeight()));
316    
317                            DataBuffer dataBuffer = bufferedImage.getData().getDataBuffer();
318    
319                            int size = dataBuffer.getSize();
320    
321                            for (int i = 0; i < size; i++) {
322                                    os.write((byte)dataBuffer.getElem(i));
323                            }
324                    }
325            }
326    
327            @Override
328            public BufferedImage getBufferedImage(RenderedImage renderedImage) {
329                    if (renderedImage instanceof BufferedImage) {
330                            return (BufferedImage)renderedImage;
331                    }
332    
333                    RenderedImageAdapter adapter = new RenderedImageAdapter(renderedImage);
334    
335                    return adapter.getAsBufferedImage();
336            }
337    
338            @Override
339            public byte[] getBytes(RenderedImage renderedImage, String contentType)
340                    throws IOException {
341    
342                    UnsyncByteArrayOutputStream baos = new UnsyncByteArrayOutputStream();
343    
344                    write(renderedImage, contentType, baos);
345    
346                    return baos.toByteArray();
347            }
348    
349            @Override
350            public Image getDefaultCompanyLogo() {
351                    return _defaultCompanyLogo;
352            }
353    
354            @Override
355            public Image getDefaultOrganizationLogo() {
356                    return _defaultOrganizationLogo;
357            }
358    
359            @Override
360            public Image getDefaultSpacer() {
361                    return _defaultSpacer;
362            }
363    
364            @Override
365            public Image getDefaultUserFemalePortrait() {
366                    return _defaultUserFemalePortrait;
367            }
368    
369            @Override
370            public Image getDefaultUserMalePortrait() {
371                    return _defaultUserMalePortrait;
372            }
373    
374            @Override
375            public Image getImage(byte[] bytes) throws IOException {
376                    if (bytes == null) {
377                            return null;
378                    }
379    
380                    ImageBag imageBag = read(bytes);
381    
382                    RenderedImage renderedImage = imageBag.getRenderedImage();
383    
384                    if (renderedImage == null) {
385                            throw new IOException("Unable to decode image");
386                    }
387    
388                    String type = imageBag.getType();
389    
390                    int height = renderedImage.getHeight();
391                    int width = renderedImage.getWidth();
392                    int size = bytes.length;
393    
394                    Image image = new ImageImpl();
395    
396                    image.setTextObj(bytes);
397                    image.setType(type);
398                    image.setHeight(height);
399                    image.setWidth(width);
400                    image.setSize(size);
401    
402                    return image;
403            }
404    
405            @Override
406            public Image getImage(File file) throws IOException {
407                    byte[] bytes = _fileUtil.getBytes(file);
408    
409                    return getImage(bytes);
410            }
411    
412            @Override
413            public Image getImage(InputStream is) throws IOException {
414                    byte[] bytes = _fileUtil.getBytes(is, -1, true);
415    
416                    return getImage(bytes);
417            }
418    
419            @Override
420            public Image getImage(InputStream is, boolean cleanUpStream)
421                    throws IOException {
422    
423                    byte[] bytes = _fileUtil.getBytes(is, -1, cleanUpStream);
424    
425                    return getImage(bytes);
426            }
427    
428            @Override
429            public boolean isNullOrDefaultSpacer(byte[] bytes) {
430                    if (ArrayUtil.isEmpty(bytes) ||
431                            Arrays.equals(bytes, getDefaultSpacer().getTextObj())) {
432    
433                            return true;
434                    }
435                    else {
436                            return false;
437                    }
438            }
439    
440            @Override
441            public ImageBag read(byte[] bytes) throws IOException {
442                    String formatName = null;
443                    ImageInputStream imageInputStream = null;
444                    Queue<ImageReader> imageReaders = new LinkedList<ImageReader>();
445                    RenderedImage renderedImage = null;
446    
447                    try {
448                            imageInputStream = ImageIO.createImageInputStream(
449                                    new ByteArrayInputStream(bytes));
450    
451                            Enumeration<ImageCodec> enumeration = ImageCodec.getCodecs();
452    
453                            Map<String, ImageCodec> imageCodecs =
454                                    new HashMap<String, ImageCodec>();
455    
456                            while (enumeration.hasMoreElements()) {
457                                    ImageCodec imageCodec = enumeration.nextElement();
458    
459                                    imageCodecs.put(imageCodec.getFormatName(), imageCodec);
460                            }
461    
462                            Iterator<ImageReader> iterator = ImageIO.getImageReaders(
463                                    imageInputStream);
464    
465                            while ((renderedImage == null) && iterator.hasNext()) {
466                                    ImageReader imageReader = iterator.next();
467    
468                                    imageReaders.offer(imageReader);
469    
470                                    formatName = StringUtil.toLowerCase(
471                                            imageReader.getFormatName());
472    
473                                    if (formatName.contains("jpeg")) {
474                                            try {
475                                                    imageReader.setInput(imageInputStream);
476    
477                                                    renderedImage = imageReader.read(0);
478                                            }
479                                            catch (IOException ioe) {
480                                                    continue;
481                                            }
482                                    }
483                                    else {
484                                            ImageCodec imageCodec = imageCodecs.get(formatName);
485    
486                                            if (imageCodec.isFormatRecognized(bytes)) {
487                                                    renderedImage = read(bytes, formatName);
488                                            }
489                                    }
490                            }
491    
492                            if (renderedImage == null) {
493                                    throw new IOException("Unsupported image type");
494                            }
495                    }
496                    finally {
497                            while (!imageReaders.isEmpty()) {
498                                    ImageReader imageReader = imageReaders.poll();
499    
500                                    imageReader.dispose();
501                            }
502    
503                            if (imageInputStream != null) {
504                                    imageInputStream.close();
505                            }
506                    }
507    
508                    String type = TYPE_JPEG;
509    
510                    if (formatName.contains(TYPE_BMP)) {
511                            type = TYPE_BMP;
512                    }
513                    else if (formatName.contains(TYPE_GIF)) {
514                            type = TYPE_GIF;
515                    }
516                    else if (formatName.contains("jpeg") ||
517                                     StringUtil.equalsIgnoreCase(type, "jpeg")) {
518    
519                            type = TYPE_JPEG;
520                    }
521                    else if (formatName.contains(TYPE_PNG)) {
522                            type = TYPE_PNG;
523                    }
524                    else if (formatName.contains(TYPE_TIFF)) {
525                            type = TYPE_TIFF;
526                    }
527                    else {
528                            throw new IllegalArgumentException(type + " is not supported");
529                    }
530    
531                    return new ImageBag(renderedImage, type);
532            }
533    
534            @Override
535            public ImageBag read(File file) throws IOException {
536                    return read(_fileUtil.getBytes(file));
537            }
538    
539            @Override
540            public ImageBag read(InputStream inputStream) throws IOException {
541                    return read(_fileUtil.getBytes(inputStream));
542            }
543    
544            @Override
545            public RenderedImage scale(RenderedImage renderedImage, int width) {
546                    if (width <= 0) {
547                            return renderedImage;
548                    }
549    
550                    int imageHeight = renderedImage.getHeight();
551                    int imageWidth = renderedImage.getWidth();
552    
553                    double factor = (double)width / imageWidth;
554    
555                    int scaledHeight = (int)Math.round(factor * imageHeight);
556                    int scaledWidth = width;
557    
558                    return doScale(renderedImage, scaledHeight, scaledWidth);
559            }
560    
561            @Override
562            public RenderedImage scale(
563                    RenderedImage renderedImage, int maxHeight, int maxWidth) {
564    
565                    int imageHeight = renderedImage.getHeight();
566                    int imageWidth = renderedImage.getWidth();
567    
568                    if (maxHeight == 0) {
569                            maxHeight = imageHeight;
570                    }
571    
572                    if (maxWidth == 0) {
573                            maxWidth = imageWidth;
574                    }
575    
576                    if ((imageHeight <= maxHeight) && (imageWidth <= maxWidth)) {
577                            return renderedImage;
578                    }
579    
580                    double factor = Math.min(
581                            (double)maxHeight / imageHeight, (double)maxWidth / imageWidth);
582    
583                    int scaledHeight = Math.max(1, (int)Math.round(factor * imageHeight));
584                    int scaledWidth = Math.max(1, (int)Math.round(factor * imageWidth));
585    
586                    return doScale(renderedImage, scaledHeight, scaledWidth);
587            }
588    
589            @Override
590            public void write(
591                            RenderedImage renderedImage, String contentType, OutputStream os)
592                    throws IOException {
593    
594                    if (contentType.contains(TYPE_BMP)) {
595                            ImageEncoder imageEncoder = ImageCodec.createImageEncoder(
596                                    TYPE_BMP, os, null);
597    
598                            imageEncoder.encode(renderedImage);
599                    }
600                    else if (contentType.contains(TYPE_GIF)) {
601                            encodeGIF(renderedImage, os);
602                    }
603                    else if (contentType.contains(TYPE_JPEG) ||
604                                     contentType.contains("jpeg")) {
605    
606                            ImageIO.write(renderedImage, "jpeg", os);
607                    }
608                    else if (contentType.contains(TYPE_PNG)) {
609                            ImageIO.write(renderedImage, TYPE_PNG, os);
610                    }
611                    else if (contentType.contains(TYPE_TIFF) ||
612                                     contentType.contains("tif")) {
613    
614                            ImageEncoder imageEncoder = ImageCodec.createImageEncoder(
615                                    TYPE_TIFF, os, null);
616    
617                            imageEncoder.encode(renderedImage);
618                    }
619            }
620    
621            protected RenderedImage doScale(
622                    RenderedImage renderedImage, int scaledHeight, int scaledWidth) {
623    
624                    // See http://www.oracle.com/technetwork/java/index-137037.html
625    
626                    BufferedImage originalBufferedImage = getBufferedImage(renderedImage);
627    
628                    BufferedImage scaledBufferedImage = new BufferedImage(
629                            scaledWidth, scaledHeight, originalBufferedImage.getType());
630    
631                    int originalHeight = originalBufferedImage.getHeight();
632                    int originalWidth = originalBufferedImage.getWidth();
633    
634                    if (((scaledHeight * 2) >= originalHeight) &&
635                            ((scaledWidth * 2) >= originalWidth)) {
636    
637                            Graphics2D scaledGraphics2D = scaledBufferedImage.createGraphics();
638    
639                            scaledGraphics2D.drawImage(
640                                    originalBufferedImage, 0, 0, scaledWidth, scaledHeight, null);
641    
642                            scaledGraphics2D.dispose();
643    
644                            return scaledBufferedImage;
645                    }
646    
647                    BufferedImage tempBufferedImage = new BufferedImage(
648                            originalWidth, originalHeight, scaledBufferedImage.getType());
649    
650                    Graphics2D tempGraphics2D = tempBufferedImage.createGraphics();
651    
652                    RenderingHints renderingHints = new RenderingHints(
653                            RenderingHints.KEY_INTERPOLATION,
654                            RenderingHints.VALUE_INTERPOLATION_BILINEAR);
655    
656                    tempGraphics2D.setRenderingHints(renderingHints);
657    
658                    ColorModel originalColorModel = originalBufferedImage.getColorModel();
659    
660                    if (originalColorModel.hasAlpha()) {
661                            tempGraphics2D.setComposite(AlphaComposite.Src);
662                    }
663    
664                    int startHeight = scaledHeight;
665                    int startWidth = scaledWidth;
666    
667                    while ((startHeight < originalHeight) && (startWidth < originalWidth)) {
668                            startHeight *= 2;
669                            startWidth *= 2;
670                    }
671    
672                    originalHeight = startHeight / 2;
673                    originalWidth = startWidth / 2;
674    
675                    tempGraphics2D.drawImage(
676                            originalBufferedImage, 0, 0, originalWidth, originalHeight, null);
677    
678                    while ((originalHeight >= (scaledHeight * 2)) &&
679                               (originalWidth >= (scaledWidth * 2))) {
680    
681                            originalHeight /= 2;
682    
683                            if (originalHeight < scaledHeight) {
684                                    originalHeight = scaledHeight;
685                            }
686    
687                            originalWidth /= 2;
688    
689                            if (originalWidth < scaledWidth) {
690                                    originalWidth = scaledWidth;
691                            }
692    
693                            tempGraphics2D.drawImage(
694                                    tempBufferedImage, 0, 0, originalWidth, originalHeight, 0, 0,
695                                    originalWidth * 2, originalHeight * 2, null);
696                    }
697    
698                    tempGraphics2D.dispose();
699    
700                    Graphics2D scaledGraphics2D = scaledBufferedImage.createGraphics();
701    
702                    scaledGraphics2D.drawImage(
703                            tempBufferedImage, 0, 0, scaledWidth, scaledHeight, 0, 0,
704                            originalWidth, originalHeight, null);
705    
706                    scaledGraphics2D.dispose();
707    
708                    return scaledBufferedImage;
709            }
710    
711            protected ImageMagick getImageMagick() {
712                    if (_imageMagick == null) {
713                            _imageMagick = ImageMagickImpl.getInstance();
714    
715                            _imageMagick.reset();
716                    }
717    
718                    return _imageMagick;
719            }
720    
721            protected RenderedImage read(byte[] bytes, String type) {
722                    RenderedImage renderedImage = null;
723    
724                    try {
725                            if (type.equals(TYPE_JPEG)) {
726                                    type = "jpeg";
727                            }
728    
729                            ImageDecoder imageDecoder = ImageCodec.createImageDecoder(
730                                    type, new UnsyncByteArrayInputStream(bytes), null);
731    
732                            renderedImage = imageDecoder.decodeAsRenderedImage();
733                    }
734                    catch (IllegalArgumentException iae) {
735                            if (_log.isDebugEnabled()) {
736                                    _log.debug(iae, iae);
737                            }
738                    }
739                    catch (IOException ioe) {
740                            if (_log.isDebugEnabled()) {
741                                    _log.debug(type + ": " + ioe.getMessage());
742                            }
743                    }
744    
745                    return renderedImage;
746            }
747    
748            protected void orderImageReaderSpis() {
749                    IIORegistry defaultIIORegistry = IIORegistry.getDefaultInstance();
750    
751                    ImageReaderSpi firstImageReaderSpi = null;
752                    ImageReaderSpi secondImageReaderSpi = null;
753    
754                    Iterator<ImageReaderSpi> imageReaderSpis =
755                            defaultIIORegistry.getServiceProviders(ImageReaderSpi.class, true);
756    
757                    while (imageReaderSpis.hasNext()) {
758                            ImageReaderSpi imageReaderSpi = imageReaderSpis.next();
759    
760                            if (imageReaderSpi instanceof CMYKJPEGImageReaderSpi) {
761                                    secondImageReaderSpi = imageReaderSpi;
762                            }
763                            else {
764                                    String[] formatNames = imageReaderSpi.getFormatNames();
765    
766                                    if (ArrayUtil.contains(formatNames, TYPE_JPEG, true) ||
767                                            ArrayUtil.contains(formatNames, "jpeg", true)) {
768    
769                                            firstImageReaderSpi = imageReaderSpi;
770                                    }
771                            }
772                    }
773    
774                    if ((firstImageReaderSpi != null) && (secondImageReaderSpi != null)) {
775                            defaultIIORegistry.setOrdering(
776                                    ImageReaderSpi.class, firstImageReaderSpi,
777                                    secondImageReaderSpi);
778                    }
779            }
780    
781            protected byte[] toMultiByte(int intValue) {
782                    int numBits = 32;
783                    int mask = 0x80000000;
784    
785                    while ((mask != 0) && ((intValue & mask) == 0)) {
786                            numBits--;
787                            mask >>>= 1;
788                    }
789    
790                    int numBitsLeft = numBits;
791                    byte[] multiBytes = new byte[(numBitsLeft + 6) / 7];
792    
793                    int maxIndex = multiBytes.length - 1;
794    
795                    for (int b = 0; b <= maxIndex; b++) {
796                            multiBytes[b] = (byte)((intValue >>> ((maxIndex - b) * 7)) & 0x7f);
797    
798                            if (b != maxIndex) {
799                                    multiBytes[b] |= (byte)0x80;
800                            }
801                    }
802    
803                    return multiBytes;
804            }
805    
806            private ImageToolImpl() {
807                    ImageIO.setUseCache(PropsValues.IMAGE_IO_USE_DISK_CACHE);
808    
809                    orderImageReaderSpis();
810            }
811    
812            private static Log _log = LogFactoryUtil.getLog(ImageToolImpl.class);
813    
814            private static ImageTool _instance = new ImageToolImpl();
815    
816            private static FileImpl _fileUtil = FileImpl.getInstance();
817            private static ImageMagick _imageMagick;
818    
819            private Image _defaultCompanyLogo;
820            private Image _defaultOrganizationLogo;
821            private Image _defaultSpacer;
822            private Image _defaultUserFemalePortrait;
823            private Image _defaultUserMalePortrait;
824    
825            private class RenderedImageFuture implements Future<RenderedImage> {
826    
827                    public RenderedImageFuture(
828                            Future<?> future, File outputFile, String type) {
829    
830                            _future = future;
831                            _outputFile = outputFile;
832                            _type = type;
833                    }
834    
835                    @Override
836                    public boolean cancel(boolean mayInterruptIfRunning) {
837                            if (_future.isCancelled() || _future.isDone()) {
838                                    return false;
839                            }
840    
841                            _future.cancel(true);
842    
843                            return true;
844                    }
845    
846                    @Override
847                    public RenderedImage get()
848                            throws ExecutionException, InterruptedException {
849    
850                            _future.get();
851    
852                            byte[] bytes = new byte[0];
853    
854                            try {
855                                    bytes = _fileUtil.getBytes(_outputFile);
856    
857                                    ImageBag imageBag = read(bytes);
858    
859                                    return imageBag.getRenderedImage();
860                            }
861                            catch (IOException ioe) {
862                                    throw new ExecutionException(ioe);
863                            }
864                    }
865    
866                    @Override
867                    public RenderedImage get(long timeout, TimeUnit timeUnit)
868                            throws ExecutionException, InterruptedException, TimeoutException {
869    
870                            _future.get(timeout, timeUnit);
871    
872                            byte[] bytes = new byte[0];
873    
874                            try {
875                                    bytes = _fileUtil.getBytes(_outputFile);
876    
877                                    ImageBag imageBag = read(bytes);
878    
879                                    return imageBag.getRenderedImage();
880                            }
881                            catch (IOException ioe) {
882                                    throw new ExecutionException(ioe);
883                            }
884                    }
885    
886                    @Override
887                    public boolean isCancelled() {
888                            return _future.isCancelled();
889                    }
890    
891                    @Override
892                    public boolean isDone() {
893                            return _future.isDone();
894                    }
895    
896                    private final Future<?> _future;
897                    private final File _outputFile;
898                    private final String _type;
899    
900            }
901    
902    }