Package org.apache.sanselan.formats.psd

Source Code of org.apache.sanselan.formats.psd.PsdImageParser

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.sanselan.formats.psd;

import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Map;

import org.apache.sanselan.ImageFormat;
import org.apache.sanselan.ImageInfo;
import org.apache.sanselan.ImageParser;
import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.common.IImageMetadata;
import org.apache.sanselan.common.byteSources.ByteSource;
import org.apache.sanselan.formats.psd.dataparsers.DataParser;
import org.apache.sanselan.formats.psd.dataparsers.DataParserBitmap;
import org.apache.sanselan.formats.psd.dataparsers.DataParserCMYK;
import org.apache.sanselan.formats.psd.dataparsers.DataParserGrayscale;
import org.apache.sanselan.formats.psd.dataparsers.DataParserIndexed;
import org.apache.sanselan.formats.psd.dataparsers.DataParserLab;
import org.apache.sanselan.formats.psd.dataparsers.DataParserRGB;
import org.apache.sanselan.formats.psd.datareaders.CompressedDataReader;
import org.apache.sanselan.formats.psd.datareaders.DataReader;
import org.apache.sanselan.formats.psd.datareaders.UncompressedDataReader;
import org.apache.sanselan.util.Debug;

public class PsdImageParser extends ImageParser {

  public PsdImageParser() {
    super.setByteOrder(BYTE_ORDER_MSB);
    // setDebug(true);
  }

  public String getName() {
    return "PSD-Custom";
  }

  public String getDefaultExtension() {
    return DEFAULT_EXTENSION;
  }

  private static final String DEFAULT_EXTENSION = ".psd";

  private static final String ACCEPTED_EXTENSIONS[] = { DEFAULT_EXTENSION, };

  protected String[] getAcceptedExtensions() {
    return ACCEPTED_EXTENSIONS;
  }

  protected ImageFormat[] getAcceptedTypes() {
    return new ImageFormat[] { ImageFormat.IMAGE_FORMAT_PSD, //
    };
  }

  private PSDHeaderInfo readHeader(ByteSource byteSource)
      throws ImageReadException, IOException {
    InputStream is = null;

    try {
      is = byteSource.getInputStream();

      return readHeader(is);
    } finally {
      try {
        is.close();
      } catch (Exception e) {
        Debug.debug(e);
      }

    }
  }

  private PSDHeaderInfo readHeader(InputStream is) throws ImageReadException,
      IOException {
    readAndVerifyBytes(is, new byte[] { 56, 66, 80, 83 },
        "Not a Valid PSD File");

    int Version = read2Bytes("Version", is, "Not a Valid PSD File");

    byte Reserved[] = readByteArray("Reserved", 6, is,
        "Not a Valid PSD File");

    int Channels = read2Bytes("Channels", is, "Not a Valid PSD File");
    int Rows = read4Bytes("Rows", is, "Not a Valid PSD File");
    int Columns = read4Bytes("Columns", is, "Not a Valid PSD File");
    int Depth = read2Bytes("Depth", is, "Not a Valid PSD File");
    int Mode = read2Bytes("Mode", is, "Not a Valid PSD File");

    PSDHeaderInfo result = new PSDHeaderInfo(Version, Reserved, Channels,
        Rows, Columns, Depth, Mode);

    return result;
  }

  private ImageContents readImageContents(InputStream is)
      throws ImageReadException, IOException {
    PSDHeaderInfo header = readHeader(is);

    int ColorModeDataLength = read4Bytes("ColorModeDataLength", is,
        "Not a Valid PSD File");
    skipBytes(is, ColorModeDataLength);
    // is.skip(ColorModeDataLength);
    // byte ColorModeData[] = readByteArray("ColorModeData",
    // ColorModeDataLength, is, "Not a Valid PSD File");

    int ImageResourcesLength = read4Bytes("ImageResourcesLength", is,
        "Not a Valid PSD File");
    skipBytes(is, ImageResourcesLength);
    // long skipped = is.skip(ImageResourcesLength);
    // byte ImageResources[] = readByteArray("ImageResources",
    // ImageResourcesLength, is, "Not a Valid PSD File");

    int LayerAndMaskDataLength = read4Bytes("LayerAndMaskDataLength", is,
        "Not a Valid PSD File");
    skipBytes(is, LayerAndMaskDataLength);
    // is.skip(LayerAndMaskDataLength);
    // byte LayerAndMaskData[] = readByteArray("LayerAndMaskData",
    // LayerAndMaskDataLength, is, "Not a Valid PSD File");

    int Compression = read2Bytes("Compression", is, "Not a Valid PSD File");

    // skip_bytes(is, LayerAndMaskDataLength);
    // byte ImageData[] = readByteArray("ImageData", LayerAndMaskDataLength,
    // is, "Not a Valid PSD File");

    // System.out.println("Compression: " + Compression);

    ImageContents result = new ImageContents(header, ColorModeDataLength,
    // ColorModeData,
        ImageResourcesLength,
        // ImageResources,
        LayerAndMaskDataLength,
        // LayerAndMaskData,
        Compression);

    return result;
  }

  private ArrayList readImageResourceBlocks(byte bytes[],
      int fImageResourceIDs[], int max_blocks_to_read)
      throws ImageReadException, IOException {
    return readImageResourceBlocks(new ByteArrayInputStream(bytes),
        fImageResourceIDs, max_blocks_to_read, bytes.length);
  }

  private boolean keepImageResourceBlock(int ID, int fImageResourceIDs[]) {
    if (fImageResourceIDs == null)
      return true;

    for (int i = 0; i < fImageResourceIDs.length; i++)
      if (ID == fImageResourceIDs[i])
        return true;

    return false;
  }

  private ArrayList readImageResourceBlocks(InputStream is,
      int fImageResourceIDs[], int max_blocks_to_read, int available)
      throws ImageReadException, IOException {
    ArrayList result = new ArrayList();

    while (available > 0) {
      // if (debug)
      // System.out.println("available: " + available);

      readAndVerifyBytes(is, new byte[] { 56, 66, 73, 77 },
          "Not a Valid PSD File");
      available -= 4;

      int ID = read2Bytes("ID", is, "Not a Valid PSD File");
      available -= 2;

      int NameLength = readByte("NameLength", is, "Not a Valid PSD File");

      available -= 1;
      // int ActualNameLength = (((NameLength + 1) % 2) == 0)
      // ? NameLength
      // : NameLength + 1; // pad to make even
      byte NameData[] = readByteArray("NameData", NameLength, is,
          "Not a Valid PSD File");
      available -= NameLength;
      if (((NameLength + 1) % 2) != 0) {
        int NameDiscard = readByte("NameDiscard", is,
            "Not a Valid PSD File");
        available -= 1;
      }
      // String Name = readPString("Name", 6, is, "Not a Valid PSD File");
      int DataSize = read4Bytes("Size", is, "Not a Valid PSD File");
      available -= 4;
      // int ActualDataSize = ((DataSize % 2) == 0)
      // ? DataSize
      // : DataSize + 1; // pad to make even

      byte Data[] = readByteArray("Data", DataSize, is,
          "Not a Valid PSD File");
      available -= DataSize;

      if ((DataSize % 2) != 0) {
        int DataDiscard = readByte("DataDiscard", is,
            "Not a Valid PSD File");
        available -= 1;
      }

      if (keepImageResourceBlock(ID, fImageResourceIDs)) {
        result.add(new ImageResourceBlock(ID, NameData, Data));

        if ((max_blocks_to_read >= 0)
            && (result.size() >= max_blocks_to_read))
          return result;
      }
      // debugNumber("ID", ID, 2);

    }

    return result;
  }

  private ArrayList readImageResourceBlocks(ByteSource byteSource,
      int fImageResourceIDs[], int max_blocks_to_read)
      throws ImageReadException, IOException {
    InputStream is = null;

    try {
      is = byteSource.getInputStream();

      ImageContents imageContents = readImageContents(is);

      is.close();

      is = this.getInputStream(byteSource, PSD_SECTION_IMAGE_RESOURCES);
      byte ImageResources[] = readByteArray("ImageResources",
          imageContents.ImageResourcesLength, is,
          "Not a Valid PSD File");

      return readImageResourceBlocks(ImageResources, fImageResourceIDs,
          max_blocks_to_read);
    }
    // catch (Exception e)
    // {
    // // System.out.println("Error: " + file.getAbsolutePath());
    // Debug.debug(e); }
    finally {
      try {
        is.close();
      } catch (Exception e) {
        Debug.debug(e);
      }

    }
    // is.reset();
    // if (debug)
    // System.out.println("");
    //
    // return null;
  }

  // InputStream is = getInputStream(byteSource, kPSD_SECTION_IMAGE_DATA);

  private static final int PSD_SECTION_HEADER = 0;
  private static final int PSD_SECTION_COLOR_MODE = 1;
  private static final int PSD_SECTION_IMAGE_RESOURCES = 2;
  private static final int PSD_SECTION_LAYER_AND_MASK_DATA = 3;
  private static final int PSD_SECTION_IMAGE_DATA = 4;

  private static final int PSD_HEADER_LENGTH = 26;

  private InputStream getInputStream(ByteSource byteSource, int section)
      throws ImageReadException, IOException {
    InputStream is = byteSource.getInputStream();

    if (section == PSD_SECTION_HEADER)
      return is;

    skipBytes(is, PSD_HEADER_LENGTH);
    // is.skip(kHeaderLength);

    int ColorModeDataLength = read4Bytes("ColorModeDataLength", is,
        "Not a Valid PSD File");

    if (section == PSD_SECTION_COLOR_MODE)
      return is;

    skipBytes(is, ColorModeDataLength);
    // byte ColorModeData[] = readByteArray("ColorModeData",
    // ColorModeDataLength, is, "Not a Valid PSD File");

    int ImageResourcesLength = read4Bytes("ImageResourcesLength", is,
        "Not a Valid PSD File");

    if (section == PSD_SECTION_IMAGE_RESOURCES)
      return is;

    skipBytes(is, ImageResourcesLength);
    // byte ImageResources[] = readByteArray("ImageResources",
    // ImageResourcesLength, is, "Not a Valid PSD File");

    int LayerAndMaskDataLength = read4Bytes("LayerAndMaskDataLength", is,
        "Not a Valid PSD File");

    if (section == PSD_SECTION_LAYER_AND_MASK_DATA)
      return is;

    skipBytes(is, LayerAndMaskDataLength);
    // byte LayerAndMaskData[] = readByteArray("LayerAndMaskData",
    // LayerAndMaskDataLength, is, "Not a Valid PSD File");

    int Compression = read2Bytes("Compression", is, "Not a Valid PSD File");

    // byte ImageData[] = readByteArray("ImageData",
    // LayerAndMaskDataLength, is, "Not a Valid PSD File");

    if (section == PSD_SECTION_IMAGE_DATA)
      return is;

    throw new ImageReadException("getInputStream: Unknown Section: "
        + section);
  }

  private byte[] getData(ByteSource byteSource, int section)
      throws ImageReadException, IOException {
    InputStream is = null;

    try {
      is = byteSource.getInputStream();

      // PSDHeaderInfo header = readHeader(is);
      if (section == PSD_SECTION_HEADER)
        return readByteArray("Header", PSD_HEADER_LENGTH, is,
            "Not a Valid PSD File");
      skipBytes(is, PSD_HEADER_LENGTH);

      int ColorModeDataLength = read4Bytes("ColorModeDataLength", is,
          "Not a Valid PSD File");

      if (section == PSD_SECTION_COLOR_MODE)
        return readByteArray("ColorModeData", ColorModeDataLength, is,
            "Not a Valid PSD File");

      skipBytes(is, ColorModeDataLength);
      // byte ColorModeData[] = readByteArray("ColorModeData",
      // ColorModeDataLength, is, "Not a Valid PSD File");

      int ImageResourcesLength = read4Bytes("ImageResourcesLength", is,
          "Not a Valid PSD File");

      if (section == PSD_SECTION_IMAGE_RESOURCES)
        return readByteArray("ImageResources", ImageResourcesLength,
            is, "Not a Valid PSD File");

      skipBytes(is, ImageResourcesLength);
      // byte ImageResources[] = readByteArray("ImageResources",
      // ImageResourcesLength, is, "Not a Valid PSD File");

      int LayerAndMaskDataLength = read4Bytes("LayerAndMaskDataLength",
          is, "Not a Valid PSD File");

      if (section == PSD_SECTION_LAYER_AND_MASK_DATA)
        return readByteArray("LayerAndMaskData",
            LayerAndMaskDataLength, is, "Not a Valid PSD File");

      skipBytes(is, LayerAndMaskDataLength);
      // byte LayerAndMaskData[] = readByteArray("LayerAndMaskData",
      // LayerAndMaskDataLength, is, "Not a Valid PSD File");

      int Compression = read2Bytes("Compression", is,
          "Not a Valid PSD File");

      // byte ImageData[] = readByteArray("ImageData",
      // LayerAndMaskDataLength, is, "Not a Valid PSD File");

      // if (section == kPSD_SECTION_IMAGE_DATA)
      // return readByteArray("LayerAndMaskData", LayerAndMaskDataLength,
      // is,
      // "Not a Valid PSD File");

    } finally {
      try {
        is.close();
      } catch (Exception e) {
        Debug.debug(e);
      }

    }
    throw new ImageReadException("getInputStream: Unknown Section: "
        + section);
  }

  private ImageContents readImageContents(ByteSource byteSource)
      throws ImageReadException, IOException {
    InputStream is = null;

    try {
      is = byteSource.getInputStream();

      ImageContents imageContents = readImageContents(is);
      return imageContents;
    } finally {
      try {
        is.close();
      } catch (Exception e) {
        Debug.debug(e);
      }

    }

  }

  public final static int IMAGE_RESOURCE_Id_ICC_PROFILE = 0x040F;

  public byte[] getICCProfileBytes(ByteSource byteSource, Map params)
      throws ImageReadException, IOException {
    ArrayList segments = readImageResourceBlocks(byteSource,
        new int[] { IMAGE_RESOURCE_Id_ICC_PROFILE, }, 1);

    if ((segments == null) || (segments.size() < 1))
      return null;

    ImageResourceBlock irb = (ImageResourceBlock) segments.get(0);
    byte bytes[] = irb.Data;
    if ((bytes == null) || (bytes.length < 1))
      return null;
    return bytes;
  }

  public Dimension getImageSize(ByteSource byteSource, Map params)
      throws ImageReadException, IOException {
    PSDHeaderInfo bhi = readHeader(byteSource);
    if (bhi == null)
      throw new ImageReadException("PSD: couldn't read header");

    return new Dimension(bhi.Columns, bhi.Rows);

  }

  public byte[] embedICCProfile(byte image[], byte profile[]) {
    return null;
  }

  public boolean embedICCProfile(File src, File dst, byte profile[]) {
    return false;
  }

  public IImageMetadata getMetadata(ByteSource byteSource, Map params)
      throws ImageReadException, IOException {
    return null;
  }

  private int getChannelsPerMode(int mode) {
    switch (mode) {
    case 0: // Bitmap
      return 1;
    case 1: // Grayscale
      return 1;
    case 2: // Indexed
      return -1;
    case 3: // RGB
      return 3;
    case 4: // CMYK
      return 4;
    case 7: // Multichannel
      return -1;
    case 8: // Duotone
      return -1;
    case 9: // Lab
      return 4;
    default:
      return -1;

    }
  }

  public ImageInfo getImageInfo(ByteSource byteSource, Map params)
      throws ImageReadException, IOException {
    ImageContents imageContents = readImageContents(byteSource);
    // ImageContents imageContents = readImage(byteSource, false);

    if (imageContents == null)
      throw new ImageReadException("PSD: Couldn't read blocks");

    PSDHeaderInfo header = imageContents.header;
    if (header == null)
      throw new ImageReadException("PSD: Couldn't read Header");

    int Width = header.Columns;
    int Height = header.Rows;

    ArrayList Comments = new ArrayList();
    // TODO: comments...

    int BitsPerPixel = header.Depth * getChannelsPerMode(header.Mode);
    // System.out.println("header.Depth: " + header.Depth);
    // System.out.println("header.Mode: " + header.Mode);
    // System.out.println("getChannelsPerMode(header.Mode): " +
    // getChannelsPerMode(header.Mode));
    if (BitsPerPixel < 0)
      BitsPerPixel = 0;
    ImageFormat Format = ImageFormat.IMAGE_FORMAT_PSD;
    String FormatName = "Photoshop";
    String MimeType = "image/x-photoshop";
    // we ought to count images, but don't yet.
    int NumberOfImages = -1;
    // not accurate ... only reflects first
    boolean isProgressive = false;

    int PhysicalWidthDpi = 72;
    float PhysicalWidthInch = (float) ((double) Width / (double) PhysicalWidthDpi);
    int PhysicalHeightDpi = 72;
    float PhysicalHeightInch = (float) ((double) Height / (double) PhysicalHeightDpi);

    String FormatDetails = "Psd";

    boolean isTransparent = false; // TODO: inaccurate.
    boolean usesPalette = header.Mode == COLOR_MODE_INDEXED;
    int ColorType = ImageInfo.COLOR_TYPE_UNKNOWN;

    String compressionAlgorithm;
    switch (imageContents.Compression) {
    case 0:
      compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_NONE;
      break;
    case 1:
      compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_PSD;
      break;
    default:
      compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_UNKNOWN;
    }

    ImageInfo result = new ImageInfo(FormatDetails, BitsPerPixel, Comments,
        Format, FormatName, Height, MimeType, NumberOfImages,
        PhysicalHeightDpi, PhysicalHeightInch, PhysicalWidthDpi,
        PhysicalWidthInch, Width, isProgressive, isTransparent,
        usesPalette, ColorType, compressionAlgorithm);

    return result;
  }

  private ImageResourceBlock findImageResourceBlock(ArrayList blocks, int ID)
      throws ImageReadException, IOException {
    for (int i = 0; i < blocks.size(); i++) {
      ImageResourceBlock block = (ImageResourceBlock) blocks.get(i);

      if (block.ID == ID)
        return block;
    }
    return null;
  }

  public boolean dumpImageFile(PrintWriter pw, ByteSource byteSource)
      throws ImageReadException, IOException {
    pw.println("gif.dumpImageFile");

    {
      ImageInfo fImageData = getImageInfo(byteSource);
      if (fImageData == null)
        return false;

      fImageData.toString(pw, "");
    }
    {
      ImageContents imageContents = readImageContents(byteSource);

      imageContents.dump(pw);
      imageContents.header.dump(pw);

      ArrayList blocks = readImageResourceBlocks(byteSource,
      // fImageContents.ImageResources,
          null, -1);

      pw.println("blocks.size(): " + blocks.size());

      // System.out.println("gif.blocks: " + blocks.blocks.size());
      for (int i = 0; i < blocks.size(); i++) {
        ImageResourceBlock block = (ImageResourceBlock) blocks.get(i);
        pw.println("\t" + i + " (" + Integer.toHexString(block.ID)
            + ", " + "'"
            + new String(block.NameData)
            + "' ("
            + block.NameData.length
            + "), "
            // + block.getClass().getName()
            // + ", "
            + " data: " + block.Data.length + " type: '"
            + new PSDConstants().getDescription(block.ID) + "' "
            + ")");

      }

    }

    pw.println("");

    return true;
  }

  private static final int COLOR_MODE_INDEXED = 2;

  public BufferedImage getBufferedImage(ByteSource byteSource, Map params)
      throws ImageReadException, IOException {
    ImageContents imageContents = readImageContents(byteSource);
    // ImageContents imageContents = readImage(byteSource, false);

    if (imageContents == null)
      throw new ImageReadException("PSD: Couldn't read blocks");

    PSDHeaderInfo header = imageContents.header;
    if (header == null)
      throw new ImageReadException("PSD: Couldn't read Header");

    // ImageDescriptor id = (ImageDescriptor)
    // findBlock(fImageContents.blocks,
    // kImageSeperator);
    // if (id == null)
    // throw new ImageReadException("PSD: Couldn't read Image Descriptor");
    // GraphicControlExtension gce = (GraphicControlExtension) findBlock(
    // fImageContents.blocks, kGraphicControlExtension);

    ArrayList blocks = readImageResourceBlocks(byteSource,
    // fImageContents.ImageResources,
        null, -1);

    int width = header.Columns;
    int height = header.Rows;
    // int height = header.Columns;

    // int transfer_type;

    // transfer_type = DataBuffer.TYPE_BYTE;

    boolean hasAlpha = false;
    BufferedImage result = getBufferedImageFactory(params)
        .getColorBufferedImage(width, height, hasAlpha);

    DataParser dataParser;
    switch (imageContents.header.Mode) {
    case 0: // bitmap
      dataParser = new DataParserBitmap();
      break;
    case 1:
    case 8: // Duotone=8;
      dataParser = new DataParserGrayscale();
      break;
    case 3:
      dataParser = new DataParserRGB();
      break;
    case 4:
      dataParser = new DataParserCMYK();
      break;
    case 9:
      dataParser = new DataParserLab();
      break;
    case COLOR_MODE_INDEXED:
      // case 2 : // Indexed=2;
    {

      byte ColorModeData[] = getData(byteSource, PSD_SECTION_COLOR_MODE);

      // ImageResourceBlock block = findImageResourceBlock(blocks,
      // 0x03EB);
      // if (block == null)
      // throw new ImageReadException(
      // "Missing: Indexed Color Image Resource Block");

      dataParser = new DataParserIndexed(ColorModeData);
      break;
    }
    case 7: // Multichannel=7;
      // fDataParser = new DataParserStub();
      // break;

      // case 1 :
      // fDataReader = new CompressedDataReader();
      // break;
    default:
      throw new ImageReadException("Unknown Mode: "
          + imageContents.header.Mode);
    }
    DataReader fDataReader;
    switch (imageContents.Compression) {
    case 0:
      fDataReader = new UncompressedDataReader(dataParser);
      break;
    case 1:
      fDataReader = new CompressedDataReader(dataParser);
      break;
    default:
      throw new ImageReadException("Unknown Compression: "
          + imageContents.Compression);
    }
    {
      InputStream is = null;

      try {
        is = getInputStream(byteSource, PSD_SECTION_IMAGE_DATA);
        fDataReader.readData(is, result, imageContents, this);

        fDataReader.dump();
        // is.
        // ImageContents imageContents = readImageContents(is);
        // return imageContents;
      } finally {
        try {
          if (is != null)
            is.close();
        } catch (Exception e) {
          Debug.debug(e);
        }

      }

    }

    return result;

  }

}
TOP

Related Classes of org.apache.sanselan.formats.psd.PsdImageParser

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.