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.portal.image;
016    
017    import com.liferay.portal.kernel.concurrent.FutureConverter;
018    import com.liferay.portal.kernel.exception.ImageResolutionException;
019    import com.liferay.portal.kernel.image.ImageBag;
020    import com.liferay.portal.kernel.image.ImageMagick;
021    import com.liferay.portal.kernel.image.ImageTool;
022    import com.liferay.portal.kernel.io.unsync.UnsyncByteArrayOutputStream;
023    import com.liferay.portal.kernel.log.Log;
024    import com.liferay.portal.kernel.log.LogFactoryUtil;
025    import com.liferay.portal.kernel.model.Image;
026    import com.liferay.portal.kernel.security.pacl.DoPrivileged;
027    import com.liferay.portal.kernel.util.ArrayUtil;
028    import com.liferay.portal.kernel.util.PropsKeys;
029    import com.liferay.portal.kernel.util.StringBundler;
030    import com.liferay.portal.kernel.util.StringUtil;
031    import com.liferay.portal.model.impl.ImageImpl;
032    import com.liferay.portal.util.FileImpl;
033    import com.liferay.portal.util.PropsUtil;
034    import com.liferay.portal.util.PropsValues;
035    
036    import java.awt.AlphaComposite;
037    import java.awt.Graphics;
038    import java.awt.Graphics2D;
039    import java.awt.Rectangle;
040    import java.awt.color.ColorSpace;
041    import java.awt.image.BufferedImage;
042    import java.awt.image.ColorModel;
043    import java.awt.image.DataBuffer;
044    import java.awt.image.IndexColorModel;
045    import java.awt.image.RenderedImage;
046    import java.awt.image.SampleModel;
047    import java.awt.image.WritableRaster;
048    
049    import java.io.ByteArrayInputStream;
050    import java.io.File;
051    import java.io.IOException;
052    import java.io.InputStream;
053    import java.io.OutputStream;
054    
055    import java.util.Arrays;
056    import java.util.Hashtable;
057    import java.util.Iterator;
058    import java.util.LinkedList;
059    import java.util.Queue;
060    import java.util.concurrent.Future;
061    
062    import javax.imageio.ImageIO;
063    import javax.imageio.ImageReader;
064    import javax.imageio.spi.IIORegistry;
065    import javax.imageio.spi.ImageReaderSpi;
066    import javax.imageio.stream.ImageInputStream;
067    
068    import net.jmge.gif.Gif89Encoder;
069    
070    import org.im4java.core.IMOperation;
071    
072    import org.monte.media.jpeg.CMYKJPEGImageReaderSpi;
073    
074    /**
075     * @author Brian Wing Shun Chan
076     * @author Alexander Chow
077     * @author Shuyang Zhou
078     */
079    @DoPrivileged
080    public class ImageToolImpl implements ImageTool {
081    
082            public static ImageTool getInstance() {
083                    return _instance;
084            }
085    
086            public void afterPropertiesSet() {
087                    Class<?> clazz = getClass();
088    
089                    ClassLoader classLoader = clazz.getClassLoader();
090    
091                    try {
092                            InputStream is = classLoader.getResourceAsStream(
093                                    PropsUtil.get(PropsKeys.IMAGE_DEFAULT_SPACER));
094    
095                            if (is == null) {
096                                    _log.error("Default spacer is not available");
097                            }
098    
099                            _defaultSpacer = getImage(is);
100                    }
101                    catch (Exception e) {
102                            _log.error(
103                                    "Unable to configure the default spacer: " + e.getMessage());
104                    }
105    
106                    try {
107                            InputStream is = classLoader.getResourceAsStream(
108                                    PropsUtil.get(PropsKeys.IMAGE_DEFAULT_COMPANY_LOGO));
109    
110                            if (is == null) {
111                                    _log.error("Default company logo is not available");
112                            }
113    
114                            _defaultCompanyLogo = getImage(is);
115                    }
116                    catch (Exception e) {
117                            _log.error(
118                                    "Unable to configure the default company logo: " +
119                                            e.getMessage());
120                    }
121    
122                    try {
123                            InputStream is = classLoader.getResourceAsStream(
124                                    PropsUtil.get(PropsKeys.IMAGE_DEFAULT_ORGANIZATION_LOGO));
125    
126                            if (is == null) {
127                                    _log.error("Default organization logo is not available");
128                            }
129    
130                            _defaultOrganizationLogo = getImage(is);
131                    }
132                    catch (Exception e) {
133                            _log.error(
134                                    "Unable to configure the default organization logo: " +
135                                            e.getMessage());
136                    }
137    
138                    try {
139                            InputStream is = classLoader.getResourceAsStream(
140                                    PropsUtil.get(PropsKeys.IMAGE_DEFAULT_USER_FEMALE_PORTRAIT));
141    
142                            if (is == null) {
143                                    _log.error("Default user female portrait is not available");
144                            }
145    
146                            _defaultUserFemalePortrait = getImage(is);
147                    }
148                    catch (Exception e) {
149                            _log.error(
150                                    "Unable to configure the default user female portrait: " +
151                                            e.getMessage());
152                    }
153    
154                    try {
155                            InputStream is = classLoader.getResourceAsStream(
156                                    PropsUtil.get(PropsKeys.IMAGE_DEFAULT_USER_MALE_PORTRAIT));
157    
158                            if (is == null) {
159                                    _log.error("Default user male portrait is not available");
160                            }
161    
162                            _defaultUserMalePortrait = getImage(is);
163                    }
164                    catch (Exception e) {
165                            _log.error(
166                                    "Unable to configure the default user male portrait: " +
167                                            e.getMessage());
168                    }
169            }
170    
171            @Override
172            public Future<RenderedImage> convertCMYKtoRGB(
173                    byte[] bytes, final String type) {
174    
175                    ImageMagick imageMagick = getImageMagick();
176    
177                    if (!imageMagick.isEnabled()) {
178                            return null;
179                    }
180    
181                    File inputFile = _fileUtil.createTempFile(type);
182                    final File outputFile = _fileUtil.createTempFile(type);
183    
184                    try {
185                            _fileUtil.write(inputFile, bytes);
186    
187                            IMOperation imOperation = new IMOperation();
188    
189                            imOperation.addRawArgs("-format", "%[colorspace]");
190                            imOperation.addImage(inputFile.getPath());
191    
192                            String[] output = imageMagick.identify(imOperation.getCmdArgs());
193    
194                            if ((output.length == 1) &&
195                                    StringUtil.equalsIgnoreCase(output[0], "CMYK")) {
196    
197                                    if (_log.isInfoEnabled()) {
198                                            _log.info("The image is in the CMYK colorspace");
199                                    }
200    
201                                    imOperation = new IMOperation();
202    
203                                    imOperation.addRawArgs("-colorspace", "RGB");
204                                    imOperation.addImage(inputFile.getPath());
205                                    imOperation.addImage(outputFile.getPath());
206    
207                                    Future<Object> future = (Future<Object>)imageMagick.convert(
208                                            imOperation.getCmdArgs());
209    
210                                    return new FutureConverter<RenderedImage, Object>(future) {
211    
212                                            @Override
213                                            protected RenderedImage convert(Object obj) {
214                                                    RenderedImage renderedImage = null;
215    
216                                                    try {
217                                                            ImageBag imageBag = read(
218                                                                    _fileUtil.getBytes(outputFile));
219    
220                                                            renderedImage = imageBag.getRenderedImage();
221                                                    }
222                                                    catch (ImageResolutionException | IOException e) {
223                                                            if (_log.isDebugEnabled()) {
224                                                                    _log.debug("Unable to convert " + type, e);
225                                                            }
226                                                    }
227    
228                                                    return renderedImage;
229                                            }
230    
231                                    };
232                            }
233                    }
234                    catch (Exception e) {
235                            _log.error(e, e);
236                    }
237                    finally {
238                            _fileUtil.delete(inputFile);
239                            _fileUtil.delete(outputFile);
240                    }
241    
242                    return null;
243            }
244    
245            @Override
246            public BufferedImage convertImageType(BufferedImage sourceImage, int type) {
247                    BufferedImage targetImage = new BufferedImage(
248                            sourceImage.getWidth(), sourceImage.getHeight(), type);
249    
250                    Graphics2D graphics = targetImage.createGraphics();
251    
252                    graphics.drawRenderedImage(sourceImage, null);
253    
254                    graphics.dispose();
255    
256                    return targetImage;
257            }
258    
259            @Override
260            public RenderedImage crop(
261                    RenderedImage renderedImage, int height, int width, int x, int y) {
262    
263                    Rectangle rectangle = new Rectangle(x, y, width, height);
264    
265                    Rectangle croppedRectangle = rectangle.intersection(
266                            new Rectangle(renderedImage.getWidth(), renderedImage.getHeight()));
267    
268                    BufferedImage bufferedImage = getBufferedImage(renderedImage);
269    
270                    return bufferedImage.getSubimage(
271                            croppedRectangle.x, croppedRectangle.y, croppedRectangle.width,
272                            croppedRectangle.height);
273            }
274    
275            @Override
276            public void encodeGIF(RenderedImage renderedImage, OutputStream os)
277                    throws IOException {
278    
279                    BufferedImage bufferedImage = getBufferedImage(renderedImage);
280    
281                    if (!(bufferedImage.getColorModel() instanceof IndexColorModel)) {
282                            bufferedImage = convertImageType(
283                                    bufferedImage, BufferedImage.TYPE_BYTE_INDEXED);
284                    }
285    
286                    Gif89Encoder encoder = new Gif89Encoder(bufferedImage);
287    
288                    encoder.encode(os);
289            }
290    
291            @Override
292            public void encodeWBMP(RenderedImage renderedImage, OutputStream os)
293                    throws IOException {
294    
295                    BufferedImage bufferedImage = getBufferedImage(renderedImage);
296    
297                    SampleModel sampleModel = bufferedImage.getSampleModel();
298    
299                    int type = sampleModel.getDataType();
300    
301                    if ((bufferedImage.getType() != BufferedImage.TYPE_BYTE_BINARY) ||
302                            (type < DataBuffer.TYPE_BYTE) || (type > DataBuffer.TYPE_INT) ||
303                            (sampleModel.getNumBands() != 1) ||
304                            (sampleModel.getSampleSize(0) != 1)) {
305    
306                            BufferedImage binaryImage = new BufferedImage(
307                                    bufferedImage.getWidth(), bufferedImage.getHeight(),
308                                    BufferedImage.TYPE_BYTE_BINARY);
309    
310                            Graphics graphics = binaryImage.getGraphics();
311    
312                            graphics.drawImage(bufferedImage, 0, 0, null);
313    
314                            renderedImage = binaryImage;
315                    }
316    
317                    if (!ImageIO.write(renderedImage, "wbmp", os)) {
318    
319                            // See http://www.jguru.com/faq/view.jsp?EID=127723
320    
321                            os.write(0);
322                            os.write(0);
323                            os.write(toMultiByte(bufferedImage.getWidth()));
324                            os.write(toMultiByte(bufferedImage.getHeight()));
325    
326                            DataBuffer dataBuffer = bufferedImage.getData().getDataBuffer();
327    
328                            int size = dataBuffer.getSize();
329    
330                            for (int i = 0; i < size; i++) {
331                                    os.write((byte)dataBuffer.getElem(i));
332                            }
333                    }
334            }
335    
336            @Override
337            public BufferedImage getBufferedImage(RenderedImage renderedImage) {
338                    if (renderedImage instanceof BufferedImage) {
339                            return (BufferedImage)renderedImage;
340                    }
341    
342                    ColorModel colorModel = renderedImage.getColorModel();
343    
344                    WritableRaster writableRaster =
345                            colorModel.createCompatibleWritableRaster(
346                                    renderedImage.getWidth(), renderedImage.getHeight());
347    
348                    Hashtable<String, Object> properties = new Hashtable<>();
349    
350                    String[] keys = renderedImage.getPropertyNames();
351    
352                    if (!ArrayUtil.isEmpty(keys)) {
353                            for (String key : keys) {
354                                    properties.put(key, renderedImage.getProperty(key));
355                            }
356                    }
357    
358                    BufferedImage bufferedImage = new BufferedImage(
359                            colorModel, writableRaster, colorModel.isAlphaPremultiplied(),
360                            properties);
361    
362                    renderedImage.copyData(writableRaster);
363    
364                    return bufferedImage;
365            }
366    
367            @Override
368            public byte[] getBytes(RenderedImage renderedImage, String contentType)
369                    throws IOException {
370    
371                    UnsyncByteArrayOutputStream baos = new UnsyncByteArrayOutputStream();
372    
373                    write(renderedImage, contentType, baos);
374    
375                    return baos.toByteArray();
376            }
377    
378            @Override
379            public Image getDefaultCompanyLogo() {
380                    return _defaultCompanyLogo;
381            }
382    
383            @Override
384            public Image getDefaultOrganizationLogo() {
385                    return _defaultOrganizationLogo;
386            }
387    
388            @Override
389            public Image getDefaultSpacer() {
390                    return _defaultSpacer;
391            }
392    
393            @Override
394            public Image getDefaultUserFemalePortrait() {
395                    return _defaultUserFemalePortrait;
396            }
397    
398            @Override
399            public Image getDefaultUserMalePortrait() {
400                    return _defaultUserMalePortrait;
401            }
402    
403            @Override
404            public Image getImage(byte[] bytes)
405                    throws ImageResolutionException, IOException {
406    
407                    if (bytes == null) {
408                            return null;
409                    }
410    
411                    ImageBag imageBag = read(bytes);
412    
413                    RenderedImage renderedImage = imageBag.getRenderedImage();
414    
415                    if (renderedImage == null) {
416                            throw new IOException("Unable to decode image");
417                    }
418    
419                    String type = imageBag.getType();
420    
421                    int height = renderedImage.getHeight();
422                    int width = renderedImage.getWidth();
423                    int size = bytes.length;
424    
425                    Image image = new ImageImpl();
426    
427                    image.setTextObj(bytes);
428                    image.setType(type);
429                    image.setHeight(height);
430                    image.setWidth(width);
431                    image.setSize(size);
432    
433                    return image;
434            }
435    
436            @Override
437            public Image getImage(File file)
438                    throws ImageResolutionException, IOException {
439    
440                    byte[] bytes = _fileUtil.getBytes(file);
441    
442                    return getImage(bytes);
443            }
444    
445            @Override
446            public Image getImage(InputStream is)
447                    throws ImageResolutionException, IOException {
448    
449                    byte[] bytes = _fileUtil.getBytes(is, -1, true);
450    
451                    return getImage(bytes);
452            }
453    
454            @Override
455            public Image getImage(InputStream is, boolean cleanUpStream)
456                    throws ImageResolutionException, IOException {
457    
458                    byte[] bytes = _fileUtil.getBytes(is, -1, cleanUpStream);
459    
460                    return getImage(bytes);
461            }
462    
463            @Override
464            public boolean isNullOrDefaultSpacer(byte[] bytes) {
465                    if (ArrayUtil.isEmpty(bytes) ||
466                            Arrays.equals(bytes, getDefaultSpacer().getTextObj())) {
467    
468                            return true;
469                    }
470                    else {
471                            return false;
472                    }
473            }
474    
475            @Override
476            public ImageBag read(byte[] bytes)
477                    throws ImageResolutionException, IOException {
478    
479                    String formatName = null;
480                    ImageInputStream imageInputStream = null;
481                    Queue<ImageReader> imageReaders = new LinkedList<>();
482                    RenderedImage renderedImage = null;
483    
484                    try {
485                            imageInputStream = ImageIO.createImageInputStream(
486                                    new ByteArrayInputStream(bytes));
487    
488                            Iterator<ImageReader> iterator = ImageIO.getImageReaders(
489                                    imageInputStream);
490    
491                            while ((renderedImage == null) && iterator.hasNext()) {
492                                    ImageReader imageReader = iterator.next();
493    
494                                    imageReaders.offer(imageReader);
495    
496                                    try {
497                                            imageReader.setInput(imageInputStream);
498    
499                                            int height = imageReader.getHeight(0);
500                                            int width = imageReader.getWidth(0);
501    
502                                            if ((height > PropsValues.IMAGE_TOOL_IMAGE_MAX_HEIGHT) ||
503                                                    (width > PropsValues.IMAGE_TOOL_IMAGE_MAX_WIDTH)) {
504    
505                                                    StringBundler sb = new StringBundler(9);
506    
507                                                    sb.append("Image's dimensions (");
508                                                    sb.append(height);
509                                                    sb.append(" px high and ");
510                                                    sb.append(width);
511                                                    sb.append(" px wide) exceed max dimensions (");
512                                                    sb.append(PropsValues.IMAGE_TOOL_IMAGE_MAX_HEIGHT);
513                                                    sb.append(" px high and ");
514                                                    sb.append(PropsValues.IMAGE_TOOL_IMAGE_MAX_WIDTH);
515                                                    sb.append(" px wide)");
516    
517                                                    throw new ImageResolutionException(sb.toString());
518                                            }
519    
520                                            renderedImage = imageReader.read(0);
521                                    }
522                                    catch (IOException ioe) {
523                                            continue;
524                                    }
525    
526                                    formatName = StringUtil.toLowerCase(
527                                            imageReader.getFormatName());
528                            }
529    
530                            if (renderedImage == null) {
531                                    throw new IOException("Unsupported image type");
532                            }
533                    }
534                    finally {
535                            while (!imageReaders.isEmpty()) {
536                                    ImageReader imageReader = imageReaders.poll();
537    
538                                    imageReader.dispose();
539                            }
540    
541                            if (imageInputStream != null) {
542                                    imageInputStream.close();
543                            }
544                    }
545    
546                    String type = TYPE_JPEG;
547    
548                    if (formatName.contains(TYPE_BMP)) {
549                            type = TYPE_BMP;
550                    }
551                    else if (formatName.contains(TYPE_GIF)) {
552                            type = TYPE_GIF;
553                    }
554                    else if (formatName.contains("jpeg") ||
555                                     StringUtil.equalsIgnoreCase(type, "jpeg")) {
556    
557                            type = TYPE_JPEG;
558                    }
559                    else if (formatName.contains(TYPE_PNG)) {
560                            type = TYPE_PNG;
561                    }
562                    else if (formatName.contains(TYPE_TIFF)) {
563                            type = TYPE_TIFF;
564                    }
565                    else {
566                            throw new IllegalArgumentException(type + " is not supported");
567                    }
568    
569                    return new ImageBag(renderedImage, type);
570            }
571    
572            @Override
573            public ImageBag read(File file)
574                    throws ImageResolutionException, IOException {
575    
576                    return read(_fileUtil.getBytes(file));
577            }
578    
579            @Override
580            public ImageBag read(InputStream inputStream)
581                    throws ImageResolutionException, IOException {
582    
583                    return read(_fileUtil.getBytes(inputStream));
584            }
585    
586            @Override
587            public RenderedImage scale(RenderedImage renderedImage, int width) {
588                    if (width <= 0) {
589                            return renderedImage;
590                    }
591    
592                    int imageHeight = renderedImage.getHeight();
593                    int imageWidth = renderedImage.getWidth();
594    
595                    double factor = (double)width / imageWidth;
596    
597                    int scaledHeight = (int)Math.round(factor * imageHeight);
598                    int scaledWidth = width;
599    
600                    return doScale(renderedImage, scaledHeight, scaledWidth);
601            }
602    
603            @Override
604            public RenderedImage scale(
605                    RenderedImage renderedImage, int maxHeight, int maxWidth) {
606    
607                    int imageHeight = renderedImage.getHeight();
608                    int imageWidth = renderedImage.getWidth();
609    
610                    if (maxHeight == 0) {
611                            maxHeight = imageHeight;
612                    }
613    
614                    if (maxWidth == 0) {
615                            maxWidth = imageWidth;
616                    }
617    
618                    if ((imageHeight <= maxHeight) && (imageWidth <= maxWidth)) {
619                            return renderedImage;
620                    }
621    
622                    double factor = Math.min(
623                            (double)maxHeight / imageHeight, (double)maxWidth / imageWidth);
624    
625                    int scaledHeight = Math.max(1, (int)Math.round(factor * imageHeight));
626                    int scaledWidth = Math.max(1, (int)Math.round(factor * imageWidth));
627    
628                    return doScale(renderedImage, scaledHeight, scaledWidth);
629            }
630    
631            @Override
632            public void write(
633                            RenderedImage renderedImage, String contentType, OutputStream os)
634                    throws IOException {
635    
636                    if (contentType.contains(TYPE_BMP)) {
637                            ImageIO.write(renderedImage, "bmp", os);
638                    }
639                    else if (contentType.contains(TYPE_GIF)) {
640                            encodeGIF(renderedImage, os);
641                    }
642                    else if (contentType.contains(TYPE_JPEG) ||
643                                     contentType.contains("jpeg")) {
644    
645                            ImageIO.write(renderedImage, "jpeg", os);
646                    }
647                    else if (contentType.contains(TYPE_PNG)) {
648                            ImageIO.write(renderedImage, TYPE_PNG, os);
649                    }
650                    else if (contentType.contains(TYPE_TIFF) ||
651                                     contentType.contains("tif")) {
652    
653                            ImageIO.write(renderedImage, "tiff", os);
654                    }
655            }
656    
657            protected RenderedImage doScale(
658                    RenderedImage renderedImage, int scaledHeight, int scaledWidth) {
659    
660                    // See http://www.oracle.com/technetwork/java/index-137037.html
661    
662                    BufferedImage originalBufferedImage = getBufferedImage(renderedImage);
663    
664                    ColorModel originalColorModel = originalBufferedImage.getColorModel();
665    
666                    ColorSpace colorSpace = originalColorModel.getColorSpace();
667    
668                    BufferedImage scaledBufferedImage = new BufferedImage(
669                            scaledWidth, scaledHeight, colorSpace.getType());
670    
671                    Graphics2D scaledGraphics2D = scaledBufferedImage.createGraphics();
672    
673                    if (originalColorModel.hasAlpha()) {
674                            scaledGraphics2D.setComposite(AlphaComposite.Src);
675                    }
676    
677                    scaledGraphics2D.drawImage(
678                            originalBufferedImage, 0, 0, scaledWidth, scaledHeight, null);
679    
680                    scaledGraphics2D.dispose();
681    
682                    return scaledBufferedImage;
683            }
684    
685            protected ImageMagick getImageMagick() {
686                    if (_imageMagick == null) {
687                            _imageMagick = ImageMagickImpl.getInstance();
688    
689                            _imageMagick.reset();
690                    }
691    
692                    return _imageMagick;
693            }
694    
695            protected void orderImageReaderSpis() {
696                    IIORegistry defaultIIORegistry = IIORegistry.getDefaultInstance();
697    
698                    ImageReaderSpi firstImageReaderSpi = null;
699                    ImageReaderSpi secondImageReaderSpi = null;
700    
701                    Iterator<ImageReaderSpi> imageReaderSpis =
702                            defaultIIORegistry.getServiceProviders(ImageReaderSpi.class, true);
703    
704                    while (imageReaderSpis.hasNext()) {
705                            ImageReaderSpi imageReaderSpi = imageReaderSpis.next();
706    
707                            if (imageReaderSpi instanceof CMYKJPEGImageReaderSpi) {
708                                    secondImageReaderSpi = imageReaderSpi;
709                            }
710                            else {
711                                    String[] formatNames = imageReaderSpi.getFormatNames();
712    
713                                    if (ArrayUtil.contains(formatNames, TYPE_JPEG, true) ||
714                                            ArrayUtil.contains(formatNames, "jpeg", true)) {
715    
716                                            firstImageReaderSpi = imageReaderSpi;
717                                    }
718                            }
719                    }
720    
721                    if ((firstImageReaderSpi != null) && (secondImageReaderSpi != null)) {
722                            defaultIIORegistry.setOrdering(
723                                    ImageReaderSpi.class, firstImageReaderSpi,
724                                    secondImageReaderSpi);
725                    }
726            }
727    
728            protected byte[] toMultiByte(int intValue) {
729                    int numBits = 32;
730                    int mask = 0x80000000;
731    
732                    while ((mask != 0) && ((intValue & mask) == 0)) {
733                            numBits--;
734                            mask >>>= 1;
735                    }
736    
737                    int numBitsLeft = numBits;
738                    byte[] multiBytes = new byte[(numBitsLeft + 6) / 7];
739    
740                    int maxIndex = multiBytes.length - 1;
741    
742                    for (int b = 0; b <= maxIndex; b++) {
743                            multiBytes[b] = (byte)((intValue >>> ((maxIndex - b) * 7)) & 0x7f);
744    
745                            if (b != maxIndex) {
746                                    multiBytes[b] |= (byte)0x80;
747                            }
748                    }
749    
750                    return multiBytes;
751            }
752    
753            private ImageToolImpl() {
754                    ImageIO.setUseCache(PropsValues.IMAGE_IO_USE_DISK_CACHE);
755    
756                    orderImageReaderSpis();
757            }
758    
759            private static final Log _log = LogFactoryUtil.getLog(ImageToolImpl.class);
760    
761            private static final ImageTool _instance = new ImageToolImpl();
762    
763            private static final FileImpl _fileUtil = FileImpl.getInstance();
764            private static ImageMagick _imageMagick;
765    
766            private Image _defaultCompanyLogo;
767            private Image _defaultOrganizationLogo;
768            private Image _defaultSpacer;
769            private Image _defaultUserFemalePortrait;
770            private Image _defaultUserMalePortrait;
771    
772    }