View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.imaging.formats.psd;
18  
19  import static org.apache.commons.imaging.common.BinaryFunctions.read2Bytes;
20  import static org.apache.commons.imaging.common.BinaryFunctions.read4Bytes;
21  import static org.apache.commons.imaging.common.BinaryFunctions.readAndVerifyBytes;
22  import static org.apache.commons.imaging.common.BinaryFunctions.readByte;
23  import static org.apache.commons.imaging.common.BinaryFunctions.readBytes;
24  import static org.apache.commons.imaging.common.BinaryFunctions.skipBytes;
25  
26  import java.awt.Dimension;
27  import java.awt.image.BufferedImage;
28  import java.io.ByteArrayInputStream;
29  import java.io.IOException;
30  import java.io.InputStream;
31  import java.io.PrintWriter;
32  import java.io.UnsupportedEncodingException;
33  import java.nio.ByteOrder;
34  import java.util.ArrayList;
35  import java.util.List;
36  import java.util.Map;
37  
38  import org.apache.commons.imaging.ImageFormat;
39  import org.apache.commons.imaging.ImageFormats;
40  import org.apache.commons.imaging.ImageInfo;
41  import org.apache.commons.imaging.ImageParser;
42  import org.apache.commons.imaging.ImageReadException;
43  import org.apache.commons.imaging.common.ImageMetadata;
44  import org.apache.commons.imaging.common.bytesource.ByteSource;
45  import org.apache.commons.imaging.formats.psd.dataparsers.DataParser;
46  import org.apache.commons.imaging.formats.psd.dataparsers.DataParserBitmap;
47  import org.apache.commons.imaging.formats.psd.dataparsers.DataParserCmyk;
48  import org.apache.commons.imaging.formats.psd.dataparsers.DataParserGrayscale;
49  import org.apache.commons.imaging.formats.psd.dataparsers.DataParserIndexed;
50  import org.apache.commons.imaging.formats.psd.dataparsers.DataParserLab;
51  import org.apache.commons.imaging.formats.psd.dataparsers.DataParserRgb;
52  import org.apache.commons.imaging.formats.psd.datareaders.CompressedDataReader;
53  import org.apache.commons.imaging.formats.psd.datareaders.DataReader;
54  import org.apache.commons.imaging.formats.psd.datareaders.UncompressedDataReader;
55  
56  public class PsdImageParser extends ImageParser {
57      private static final String DEFAULT_EXTENSION = ".psd";
58      private static final String[] ACCEPTED_EXTENSIONS = { DEFAULT_EXTENSION, };
59      private static final int PSD_SECTION_HEADER = 0;
60      private static final int PSD_SECTION_COLOR_MODE = 1;
61      private static final int PSD_SECTION_IMAGE_RESOURCES = 2;
62      private static final int PSD_SECTION_LAYER_AND_MASK_DATA = 3;
63      private static final int PSD_SECTION_IMAGE_DATA = 4;
64      private static final int PSD_HEADER_LENGTH = 26;
65      private static final int COLOR_MODE_INDEXED = 2;
66      public static final int IMAGE_RESOURCE_ID_ICC_PROFILE = 0x040F;
67      public static final int IMAGE_RESOURCE_ID_XMP = 0x0424;
68      public static final String BLOCK_NAME_XMP = "XMP";
69  
70      public PsdImageParser() {
71          super.setByteOrder(ByteOrder.BIG_ENDIAN);
72          // setDebug(true);
73      }
74  
75      @Override
76      public String getName() {
77          return "PSD-Custom";
78      }
79  
80      @Override
81      public String getDefaultExtension() {
82          return DEFAULT_EXTENSION;
83      }
84  
85      @Override
86      protected String[] getAcceptedExtensions() {
87          return ACCEPTED_EXTENSIONS.clone();
88      }
89  
90      @Override
91      protected ImageFormat[] getAcceptedTypes() {
92          return new ImageFormat[] { ImageFormats.PSD, //
93          };
94      }
95  
96      private PsdHeaderInfo readHeader(final ByteSource byteSource)
97              throws ImageReadException, IOException {
98          try (InputStream is = byteSource.getInputStream()) {
99              final PsdHeaderInfo ret = readHeader(is);
100             return ret;
101         }
102     }
103 
104     private PsdHeaderInfo readHeader(final InputStream is) throws ImageReadException, IOException {
105         readAndVerifyBytes(is, new byte[] { 56, 66, 80, 83 }, "Not a Valid PSD File");
106 
107         final int version = read2Bytes("Version", is, "Not a Valid PSD File", getByteOrder());
108         final byte[] reserved = readBytes("Reserved", is, 6, "Not a Valid PSD File");
109         final int channels = read2Bytes("Channels", is, "Not a Valid PSD File", getByteOrder());
110         final int rows = read4Bytes("Rows", is, "Not a Valid PSD File", getByteOrder());
111         final int columns = read4Bytes("Columns", is, "Not a Valid PSD File", getByteOrder());
112         final int depth = read2Bytes("Depth", is, "Not a Valid PSD File", getByteOrder());
113         final int mode = read2Bytes("Mode", is, "Not a Valid PSD File", getByteOrder());
114 
115         return new PsdHeaderInfo(version, reserved, channels, rows, columns, depth, mode);
116     }
117 
118     private PsdImageContents readImageContents(final InputStream is)
119             throws ImageReadException, IOException {
120         final PsdHeaderInfo header = readHeader(is);
121 
122         final int ColorModeDataLength = read4Bytes("ColorModeDataLength", is,
123                 "Not a Valid PSD File", getByteOrder());
124         skipBytes(is, ColorModeDataLength);
125         // is.skip(ColorModeDataLength);
126         // byte ColorModeData[] = readByteArray("ColorModeData",
127         // ColorModeDataLength, is, "Not a Valid PSD File");
128 
129         final int ImageResourcesLength = read4Bytes("ImageResourcesLength", is,
130                 "Not a Valid PSD File", getByteOrder());
131         skipBytes(is, ImageResourcesLength);
132         // long skipped = is.skip(ImageResourcesLength);
133         // byte ImageResources[] = readByteArray("ImageResources",
134         // ImageResourcesLength, is, "Not a Valid PSD File");
135 
136         final int LayerAndMaskDataLength = read4Bytes("LayerAndMaskDataLength", is,
137                 "Not a Valid PSD File", getByteOrder());
138         skipBytes(is, LayerAndMaskDataLength);
139         // is.skip(LayerAndMaskDataLength);
140         // byte LayerAndMaskData[] = readByteArray("LayerAndMaskData",
141         // LayerAndMaskDataLength, is, "Not a Valid PSD File");
142 
143         final int Compression = read2Bytes("Compression", is, "Not a Valid PSD File", getByteOrder());
144 
145         // skip_bytes(is, LayerAndMaskDataLength);
146         // byte ImageData[] = readByteArray("ImageData", LayerAndMaskDataLength,
147         // is, "Not a Valid PSD File");
148 
149         // System.out.println("Compression: " + Compression);
150 
151         return new PsdImageContents(header, ColorModeDataLength,
152         // ColorModeData,
153                 ImageResourcesLength,
154                 // ImageResources,
155                 LayerAndMaskDataLength,
156                 // LayerAndMaskData,
157                 Compression);
158     }
159 
160     private List<ImageResourceBlock> readImageResourceBlocks(final byte[] bytes,
161             final int[] imageResourceIDs, final int maxBlocksToRead)
162             throws ImageReadException, IOException {
163         return readImageResourceBlocks(new ByteArrayInputStream(bytes),
164                 imageResourceIDs, maxBlocksToRead, bytes.length);
165     }
166 
167     private boolean keepImageResourceBlock(final int ID, final int[] imageResourceIDs) {
168         if (imageResourceIDs == null) {
169             return true;
170         }
171 
172         for (final int imageResourceID : imageResourceIDs) {
173             if (ID == imageResourceID) {
174                 return true;
175             }
176         }
177 
178         return false;
179     }
180 
181     private List<ImageResourceBlock> readImageResourceBlocks(final InputStream is,
182             final int[] imageResourceIDs, final int maxBlocksToRead, int available)
183             throws ImageReadException, IOException {
184         final List<ImageResourceBlock> result = new ArrayList<>();
185 
186         while (available > 0) {
187             readAndVerifyBytes(is, new byte[] { 56, 66, 73, 77 },
188                     "Not a Valid PSD File");
189             available -= 4;
190 
191             final int id = read2Bytes("ID", is, "Not a Valid PSD File", getByteOrder());
192             available -= 2;
193 
194             final int nameLength = readByte("NameLength", is, "Not a Valid PSD File");
195 
196             available -= 1;
197             final byte[] nameBytes = readBytes("NameData", is, nameLength,
198                     "Not a Valid PSD File");
199             available -= nameLength;
200             if (((nameLength + 1) % 2) != 0) {
201                 //final int NameDiscard = 
202                 readByte("NameDiscard", is,
203                         "Not a Valid PSD File");
204                 available -= 1;
205             }
206             // String Name = readPString("Name", 6, is, "Not a Valid PSD File");
207             final int dataSize = read4Bytes("Size", is, "Not a Valid PSD File", getByteOrder());
208             available -= 4;
209             // int ActualDataSize = ((DataSize % 2) == 0)
210             // ? DataSize
211             // : DataSize + 1; // pad to make even
212 
213             final byte[] data = readBytes("Data", is, dataSize, "Not a Valid PSD File");
214             available -= dataSize;
215 
216             if ((dataSize % 2) != 0) {
217                 //final int DataDiscard =
218                 readByte("DataDiscard", is, "Not a Valid PSD File");
219                 available -= 1;
220             }
221 
222             if (keepImageResourceBlock(id, imageResourceIDs)) {
223                 result.add(new ImageResourceBlock(id, nameBytes, data));
224 
225                 if ((maxBlocksToRead >= 0)
226                         && (result.size() >= maxBlocksToRead)) {
227                     return result;
228                 }
229             }
230             // debugNumber("ID", ID, 2);
231 
232         }
233 
234         return result;
235     }
236 
237     private List<ImageResourceBlock> readImageResourceBlocks(
238             final ByteSource byteSource, final int[] imageResourceIDs, final int maxBlocksToRead)
239             throws ImageReadException, IOException {
240         try (InputStream imageStream = byteSource.getInputStream();
241                 InputStream resourceStream = this.getInputStream(byteSource, PSD_SECTION_IMAGE_RESOURCES)) {
242 
243             final PsdImageContents imageContents = readImageContents(imageStream);
244 
245             final byte[] ImageResources = readBytes("ImageResources",
246                     resourceStream, imageContents.ImageResourcesLength,
247                     "Not a Valid PSD File");
248 
249             final List<ImageResourceBlock> ret = readImageResourceBlocks(ImageResources, imageResourceIDs,
250                     maxBlocksToRead);
251             return ret;
252         }
253     }
254 
255     private InputStream getInputStream(final ByteSource byteSource, final int section)
256             throws ImageReadException, IOException {
257         InputStream is = null;
258         boolean notFound = false;
259         try {
260             is = byteSource.getInputStream();
261 
262             if (section == PSD_SECTION_HEADER) {
263                 return is;
264             }
265 
266             skipBytes(is, PSD_HEADER_LENGTH);
267             // is.skip(kHeaderLength);
268 
269             final int colorModeDataLength = read4Bytes("ColorModeDataLength", is, "Not a Valid PSD File", getByteOrder());
270 
271             if (section == PSD_SECTION_COLOR_MODE) {
272                 return is;
273             }
274 
275             skipBytes(is, colorModeDataLength);
276             // byte ColorModeData[] = readByteArray("ColorModeData",
277             // ColorModeDataLength, is, "Not a Valid PSD File");
278 
279             final int imageResourcesLength = read4Bytes("ImageResourcesLength", is, "Not a Valid PSD File", getByteOrder());
280 
281             if (section == PSD_SECTION_IMAGE_RESOURCES) {
282                 return is;
283             }
284 
285             skipBytes(is, imageResourcesLength);
286             // byte ImageResources[] = readByteArray("ImageResources",
287             // ImageResourcesLength, is, "Not a Valid PSD File");
288 
289             final int layerAndMaskDataLength = read4Bytes("LayerAndMaskDataLength", is, "Not a Valid PSD File", getByteOrder());
290 
291             if (section == PSD_SECTION_LAYER_AND_MASK_DATA) {
292                 return is;
293             }
294 
295             skipBytes(is, layerAndMaskDataLength);
296             // byte LayerAndMaskData[] = readByteArray("LayerAndMaskData",
297             // LayerAndMaskDataLength, is, "Not a Valid PSD File");
298 
299             read2Bytes("Compression", is, "Not a Valid PSD File", getByteOrder());
300 
301             // byte ImageData[] = readByteArray("ImageData",
302             // LayerAndMaskDataLength, is, "Not a Valid PSD File");
303 
304             if (section == PSD_SECTION_IMAGE_DATA) {
305                 return is;
306             }
307             notFound = true;
308         } finally {
309             if (notFound && is != null) {
310                 is.close();
311             }
312         }
313         throw new ImageReadException("getInputStream: Unknown Section: "
314                 + section);
315     }
316 
317     private byte[] getData(final ByteSource byteSource, final int section)
318             throws ImageReadException, IOException {
319         try (InputStream is = byteSource.getInputStream()) {
320             // PsdHeaderInfo header = readHeader(is);
321             if (section == PSD_SECTION_HEADER) {
322                 return readBytes("Header", is, PSD_HEADER_LENGTH,
323                         "Not a Valid PSD File");
324             }
325             skipBytes(is, PSD_HEADER_LENGTH);
326 
327             final int ColorModeDataLength = read4Bytes("ColorModeDataLength", is,
328                     "Not a Valid PSD File", getByteOrder());
329 
330             if (section == PSD_SECTION_COLOR_MODE) {
331                 return readBytes("ColorModeData", is, ColorModeDataLength,
332                         "Not a Valid PSD File");
333             }
334 
335             skipBytes(is, ColorModeDataLength);
336             // byte ColorModeData[] = readByteArray("ColorModeData",
337             // ColorModeDataLength, is, "Not a Valid PSD File");
338 
339             final int ImageResourcesLength = read4Bytes("ImageResourcesLength", is,
340                     "Not a Valid PSD File", getByteOrder());
341 
342             if (section == PSD_SECTION_IMAGE_RESOURCES) {
343                 return readBytes("ImageResources", is,
344                         ImageResourcesLength, "Not a Valid PSD File");
345             }
346 
347             skipBytes(is, ImageResourcesLength);
348             // byte ImageResources[] = readByteArray("ImageResources",
349             // ImageResourcesLength, is, "Not a Valid PSD File");
350 
351             final int LayerAndMaskDataLength = read4Bytes("LayerAndMaskDataLength",
352                     is, "Not a Valid PSD File", getByteOrder());
353 
354             if (section == PSD_SECTION_LAYER_AND_MASK_DATA) {
355                 return readBytes("LayerAndMaskData",
356                         is, LayerAndMaskDataLength, "Not a Valid PSD File");
357             }
358 
359             skipBytes(is, LayerAndMaskDataLength);
360             // byte LayerAndMaskData[] = readByteArray("LayerAndMaskData",
361             // LayerAndMaskDataLength, is, "Not a Valid PSD File");
362 
363             read2Bytes("Compression", is, "Not a Valid PSD File", getByteOrder());
364 
365             // byte ImageData[] = readByteArray("ImageData",
366             // LayerAndMaskDataLength, is, "Not a Valid PSD File");
367 
368             // if (section == kPSD_SECTION_IMAGE_DATA)
369             // return readByteArray("LayerAndMaskData", LayerAndMaskDataLength,
370             // is,
371             // "Not a Valid PSD File");
372         }
373         throw new ImageReadException("getInputStream: Unknown Section: "
374                 + section);
375     }
376 
377     private PsdImageContents readImageContents(final ByteSource byteSource)
378             throws ImageReadException, IOException {
379         try (InputStream is = byteSource.getInputStream()) {
380             return readImageContents(is);
381         }
382     }
383 
384     @Override
385     public byte[] getICCProfileBytes(final ByteSource byteSource, final Map<String, Object> params)
386             throws ImageReadException, IOException {
387         final List<ImageResourceBlock> blocks = readImageResourceBlocks(byteSource,
388                 new int[] { IMAGE_RESOURCE_ID_ICC_PROFILE, }, 1);
389 
390         if ((blocks == null) || (blocks.size() < 1)) {
391             return null;
392         }
393 
394         final ImageResourceBlock irb = blocks.get(0);
395         final byte[] bytes = irb.data;
396         if ((bytes == null) || (bytes.length < 1)) {
397             return null;
398         }
399         return bytes; // TODO clone?
400     }
401 
402     @Override
403     public Dimension getImageSize(final ByteSource byteSource, final Map<String, Object> params)
404             throws ImageReadException, IOException {
405         final PsdHeaderInfo bhi = readHeader(byteSource);
406         if (bhi == null) {
407             throw new ImageReadException("PSD: couldn't read header");
408         }
409 
410         return new Dimension(bhi.columns, bhi.rows);
411 
412     }
413 
414     @Override
415     public ImageMetadata getMetadata(final ByteSource byteSource, final Map<String, Object> params)
416             throws ImageReadException, IOException {
417         return null;
418     }
419 
420     private int getChannelsPerMode(final int mode) {
421         switch (mode) {
422         case 0: // Bitmap
423             return 1;
424         case 1: // Grayscale
425             return 1;
426         case 2: // Indexed
427             return -1;
428         case 3: // RGB
429             return 3;
430         case 4: // CMYK
431             return 4;
432         case 7: // Multichannel
433             return -1;
434         case 8: // Duotone
435             return -1;
436         case 9: // Lab
437             return 4;
438         default:
439             return -1;
440 
441         }
442     }
443 
444     @Override
445     public ImageInfo getImageInfo(final ByteSource byteSource, final Map<String, Object> params)
446             throws ImageReadException, IOException {
447         final PsdImageContents imageContents = readImageContents(byteSource);
448         // ImageContents imageContents = readImage(byteSource, false);
449 
450         if (imageContents == null) {
451             throw new ImageReadException("PSD: Couldn't read blocks");
452         }
453 
454         final PsdHeaderInfo header = imageContents.header;
455         if (header == null) {
456             throw new ImageReadException("PSD: Couldn't read Header");
457         }
458 
459         final int width = header.columns;
460         final int height = header.rows;
461 
462         final List<String> comments = new ArrayList<>();
463         // TODO: comments...
464 
465         int BitsPerPixel = header.depth * getChannelsPerMode(header.mode);
466         // System.out.println("header.Depth: " + header.Depth);
467         // System.out.println("header.Mode: " + header.Mode);
468         // System.out.println("getChannelsPerMode(header.Mode): " +
469         // getChannelsPerMode(header.Mode));
470         if (BitsPerPixel < 0) {
471             BitsPerPixel = 0;
472         }
473         final ImageFormat format = ImageFormats.PSD;
474         final String formatName = "Photoshop";
475         final String mimeType = "image/x-photoshop";
476         // we ought to count images, but don't yet.
477         final int numberOfImages = -1;
478         // not accurate ... only reflects first
479         final boolean progressive = false;
480 
481         final int physicalWidthDpi = 72;
482         final float physicalWidthInch = (float) ((double) width / (double) physicalWidthDpi);
483         final int physicalHeightDpi = 72;
484         final float physicalHeightInch = (float) ((double) height / (double) physicalHeightDpi);
485 
486         final String formatDetails = "Psd";
487 
488         final boolean transparent = false; // TODO: inaccurate.
489         final boolean usesPalette = header.mode == COLOR_MODE_INDEXED;
490         final ImageInfo.ColorType colorType = ImageInfo.ColorType.UNKNOWN;
491 
492         ImageInfo.CompressionAlgorithm compressionAlgorithm;
493         switch (imageContents.Compression) {
494         case 0:
495             compressionAlgorithm = ImageInfo.CompressionAlgorithm.NONE;
496             break;
497         case 1:
498             compressionAlgorithm = ImageInfo.CompressionAlgorithm.PSD;
499             break;
500         default:
501             compressionAlgorithm = ImageInfo.CompressionAlgorithm.UNKNOWN;
502         }
503 
504         return new ImageInfo(formatDetails, BitsPerPixel, comments,
505                 format, formatName, height, mimeType, numberOfImages,
506                 physicalHeightDpi, physicalHeightInch, physicalWidthDpi,
507                 physicalWidthInch, width, progressive, transparent,
508                 usesPalette, colorType, compressionAlgorithm);
509     }
510 
511 //    // TODO not used
512 //    private ImageResourceBlock findImageResourceBlock(
513 //            final List<ImageResourceBlock> blocks, final int ID) {
514 //        for (int i = 0; i < blocks.size(); i++) {
515 //            final ImageResourceBlock block = blocks.get(i);
516 //
517 //            if (block.id == ID) {
518 //                return block;
519 //            }
520 //        }
521 //        return null;
522 //    }
523 
524     @Override
525     public boolean dumpImageFile(final PrintWriter pw, final ByteSource byteSource)
526             throws ImageReadException, IOException {
527         pw.println("gif.dumpImageFile");
528 
529         final ImageInfo fImageData = getImageInfo(byteSource);
530         if (fImageData == null) {
531             return false;
532         }
533 
534         fImageData.toString(pw, "");
535         final PsdImageContents imageContents = readImageContents(byteSource);
536 
537         imageContents.dump(pw);
538         imageContents.header.dump(pw);
539 
540         final List<ImageResourceBlock> blocks = readImageResourceBlocks(
541                 byteSource,
542                 // fImageContents.ImageResources,
543                 null, -1);
544 
545         pw.println("blocks.size(): " + blocks.size());
546 
547         // System.out.println("gif.blocks: " + blocks.blocks.size());
548         for (int i = 0; i < blocks.size(); i++) {
549             final ImageResourceBlock block = blocks.get(i);
550             pw.println("\t" + i + " (" + Integer.toHexString(block.id)
551                     + ", " + "'"
552                     + new String(block.nameData, "ISO-8859-1")
553                     + "' ("
554                     + block.nameData.length
555                     + "), "
556                     // + block.getClass().getName()
557                     // + ", "
558                     + " data: " + block.data.length + " type: '"
559                     + ImageResourceType.getDescription(block.id) + "' "
560                     + ")");
561         }
562 
563         pw.println("");
564 
565         return true;
566     }
567 
568     @Override
569     public BufferedImage getBufferedImage(final ByteSource byteSource, final Map<String, Object> params)
570             throws ImageReadException, IOException {
571         final PsdImageContents imageContents = readImageContents(byteSource);
572         // ImageContents imageContents = readImage(byteSource, false);
573 
574         if (imageContents == null) {
575             throw new ImageReadException("PSD: Couldn't read blocks");
576         }
577 
578         final PsdHeaderInfo header = imageContents.header;
579         if (header == null) {
580             throw new ImageReadException("PSD: Couldn't read Header");
581         }
582 
583         // ImageDescriptor id = (ImageDescriptor)
584         // findBlock(fImageContents.blocks,
585         // kImageSeperator);
586         // if (id == null)
587         // throw new ImageReadException("PSD: Couldn't read Image Descriptor");
588         // GraphicControlExtension gce = (GraphicControlExtension) findBlock(
589         // fImageContents.blocks, kGraphicControlExtension);
590 
591         readImageResourceBlocks(byteSource,
592         // fImageContents.ImageResources,
593                 null, -1);
594 
595         final int width = header.columns;
596         final int height = header.rows;
597         // int height = header.Columns;
598 
599         // int transfer_type;
600 
601         // transfer_type = DataBuffer.TYPE_BYTE;
602 
603         final boolean hasAlpha = false;
604         final BufferedImage result = getBufferedImageFactory(params).getColorBufferedImage(
605                 width, height, hasAlpha);
606 
607         DataParser dataParser;
608         switch (imageContents.header.mode) {
609         case 0: // bitmap
610             dataParser = new DataParserBitmap();
611             break;
612         case 1:
613         case 8: // Duotone=8;
614             dataParser = new DataParserGrayscale();
615             break;
616         case 3:
617             dataParser = new DataParserRgb();
618             break;
619         case 4:
620             dataParser = new DataParserCmyk();
621             break;
622         case 9:
623             dataParser = new DataParserLab();
624             break;
625         case COLOR_MODE_INDEXED:
626         // case 2 : // Indexed=2;
627         {
628 
629             final byte[] ColorModeData = getData(byteSource, PSD_SECTION_COLOR_MODE);
630 
631             // ImageResourceBlock block = findImageResourceBlock(blocks,
632             // 0x03EB);
633             // if (block == null)
634             // throw new ImageReadException(
635             // "Missing: Indexed Color Image Resource Block");
636 
637             dataParser = new DataParserIndexed(ColorModeData);
638             break;
639         }
640         case 7: // Multichannel=7;
641             // fDataParser = new DataParserStub();
642             // break;
643 
644             // case 1 :
645             // fDataReader = new CompressedDataReader();
646             // break;
647         default:
648             throw new ImageReadException("Unknown Mode: "
649                     + imageContents.header.mode);
650         }
651         DataReader fDataReader;
652         switch (imageContents.Compression) {
653         case 0:
654             fDataReader = new UncompressedDataReader(dataParser);
655             break;
656         case 1:
657             fDataReader = new CompressedDataReader(dataParser);
658             break;
659         default:
660             throw new ImageReadException("Unknown Compression: "
661                     + imageContents.Compression);
662         }
663         
664         try (InputStream is = getInputStream(byteSource, PSD_SECTION_IMAGE_DATA)) {
665             fDataReader.readData(is, result, imageContents, this);
666 
667             // is.
668             // ImageContents imageContents = readImageContents(is);
669             // return imageContents;
670         }
671 
672         return result;
673 
674     }
675 
676     /**
677      * Extracts embedded XML metadata as XML string.
678      * <p>
679      * 
680      * @param byteSource
681      *            File containing image data.
682      * @param params
683      *            Map of optional parameters, defined in ImagingConstants.
684      * @return Xmp Xml as String, if present. Otherwise, returns null.
685      */
686     @Override
687     public String getXmpXml(final ByteSource byteSource, final Map<String, Object> params)
688             throws ImageReadException, IOException {
689 
690         final PsdImageContents imageContents = readImageContents(byteSource);
691 
692         if (imageContents == null) {
693             throw new ImageReadException("PSD: Couldn't read blocks");
694         }
695 
696         final PsdHeaderInfo header = imageContents.header;
697         if (header == null) {
698             throw new ImageReadException("PSD: Couldn't read Header");
699         }
700 
701         final List<ImageResourceBlock> blocks = readImageResourceBlocks(byteSource,
702                 new int[] { IMAGE_RESOURCE_ID_XMP, }, -1);
703 
704         if ((blocks == null) || (blocks.size() < 1)) {
705             return null;
706         }
707 
708         final List<ImageResourceBlock> xmpBlocks = new ArrayList<>();
709 //        if (false) {
710 //            // TODO: for PSD 7 and later, verify "XMP" name.
711 //            for (int i = 0; i < blocks.size(); i++) {
712 //                final ImageResourceBlock block = blocks.get(i);
713 //                if (!block.getName().equals(BLOCK_NAME_XMP)) {
714 //                    continue;
715 //                }
716 //                xmpBlocks.add(block);
717 //            }
718 //        } else {
719             xmpBlocks.addAll(blocks);
720 //        }
721 
722         if (xmpBlocks.size() < 1) {
723             return null;
724         }
725         if (xmpBlocks.size() > 1) {
726             throw new ImageReadException(
727                     "PSD contains more than one XMP block.");
728         }
729 
730         final ImageResourceBlock block = xmpBlocks.get(0);
731 
732         try {
733             // segment data is UTF-8 encoded xml.
734             return new String(block.data, 0, block.data.length, "utf-8");
735         } catch (final UnsupportedEncodingException e) {
736             throw new ImageReadException("Invalid JPEG XMP Segment.", e);
737         }
738     }
739 
740 }