Package org.apache.sanselan.formats.tiff

Source Code of org.apache.sanselan.formats.tiff.TiffReader

/*
* 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.tiff;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.sanselan.FormatCompliance;
import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.common.BinaryFileParser;
import org.apache.sanselan.common.byteSources.ByteSource;
import org.apache.sanselan.formats.tiff.TiffDirectory.ImageDataElement;
import org.apache.sanselan.formats.tiff.constants.TiffConstants;
import org.apache.sanselan.util.Debug;

public class TiffReader extends BinaryFileParser implements TiffConstants {

  private final boolean strict;

  public TiffReader(boolean strict) {
    this.strict = strict;
  }

  private TiffHeader readTiffHeader(ByteSource byteSource,
      FormatCompliance formatCompliance) throws ImageReadException,
      IOException {
    InputStream is = null;
    try {
      is = byteSource.getInputStream();
      return readTiffHeader(is, formatCompliance);
    } finally {
      try {
        if (is != null)
          is.close();
      } catch (Exception e) {
        Debug.debug(e);
      }
    }
  }

  private TiffHeader readTiffHeader(InputStream is,
      FormatCompliance formatCompliance) throws ImageReadException,
      IOException {
    int BYTE_ORDER_1 = readByte("BYTE_ORDER_1", is, "Not a Valid TIFF File");
    int BYTE_ORDER_2 = readByte("BYTE_ORDER_2", is, "Not a Valid TIFF File");
    setByteOrder(BYTE_ORDER_1, BYTE_ORDER_2);

    int tiffVersion = read2Bytes("tiffVersion", is, "Not a Valid TIFF File");
    if (tiffVersion != 42)
      throw new ImageReadException("Unknown Tiff Version: " + tiffVersion);

    int offsetToFirstIFD = read4Bytes("offsetToFirstIFD", is,
        "Not a Valid TIFF File");

    skipBytes(is, offsetToFirstIFD - 8,
        "Not a Valid TIFF File: couldn't find IFDs");

    if (debug)
      System.out.println("");

    return new TiffHeader(BYTE_ORDER_1, tiffVersion, offsetToFirstIFD);
  }

  private void readDirectories(ByteSource byteSource,
      FormatCompliance formatCompliance, Listener listener)
      throws ImageReadException, IOException {
    TiffHeader tiffHeader = readTiffHeader(byteSource, formatCompliance);
    if (!listener.setTiffHeader(tiffHeader))
      return;

    int offset = tiffHeader.offsetToFirstIFD;
    int dirType = TiffDirectory.DIRECTORY_TYPE_ROOT;

    List visited = new ArrayList();
    readDirectory(byteSource, offset, dirType, formatCompliance, listener,
        visited);
  }

  private boolean readDirectory(ByteSource byteSource, int offset,
      int dirType, FormatCompliance formatCompliance, Listener listener,
      List visited) throws ImageReadException, IOException {
    boolean ignoreNextDirectory = false;
    return readDirectory(byteSource, offset, dirType, formatCompliance,
        listener, ignoreNextDirectory, visited);
  }

  private boolean readDirectory(ByteSource byteSource, int offset,
      int dirType, FormatCompliance formatCompliance, Listener listener,
      boolean ignoreNextDirectory, List visited)
      throws ImageReadException, IOException {
    Number key = new Integer(offset);

//    Debug.debug();
//    Debug.debug("dir offset", offset + " (0x" + Integer.toHexString(offset)
//        + ")");
//    Debug.debug("dir key", key);
//    Debug.debug("dir visited", visited);
//    Debug.debug("dirType", dirType);
//    Debug.debug();

    if (visited.contains(key))
      return false;
    visited.add(key);

    InputStream is = null;
    try {
      is = byteSource.getInputStream();
      if (offset > 0)
        is.skip(offset);

      ArrayList fields = new ArrayList();


      if (offset >= byteSource.getLength()) {
//        Debug.debug("skipping invalid directory!");
        return true;
      }

      int entryCount;
      try {
        entryCount = read2Bytes("DirectoryEntryCount", is,
            "Not a Valid TIFF File");
      } catch (IOException e) {
        if (strict)
          throw e;
        else
          return true;
      }

//      Debug.debug("entryCount", entryCount);

      for (int i = 0; i < entryCount; i++) {
        int tag = read2Bytes("Tag", is, "Not a Valid TIFF File");
        int type = read2Bytes("Type", is, "Not a Valid TIFF File");
        int length = read4Bytes("Length", is, "Not a Valid TIFF File");

//        Debug.debug("tag*", tag + " (0x" + Integer.toHexString(tag)
//            + ")");

        byte valueOffsetBytes[] = readByteArray("ValueOffset", 4, is,
            "Not a Valid TIFF File");
        int valueOffset = convertByteArrayToInt("ValueOffset",
            valueOffsetBytes);

        if (tag == 0) {
          // skip invalid fields.
          // These are seen very rarely, but can have invalid value
          // lengths,
          // which can cause OOM problems.
          continue;
        }

        // if (keepField(tag, tags))
        // {
        TiffField field = new TiffField(tag, dirType, type, length,
            valueOffset, valueOffsetBytes, getByteOrder());
        field.setSortHint(i);

//        Debug.debug("tagInfo", field.tagInfo);

        field.fillInValue(byteSource);

//        Debug.debug("\t" + "value", field.getValueDescription());

        fields.add(field);

        if (!listener.addField(field))
          return true;
      }

      int nextDirectoryOffset = read4Bytes("nextDirectoryOffset", is,
          "Not a Valid TIFF File");
      // Debug.debug("nextDirectoryOffset", nextDirectoryOffset);

      TiffDirectory directory = new TiffDirectory(dirType, fields,
          offset, nextDirectoryOffset);

      if (listener.readImageData()) {
        if (directory.hasTiffImageData()) {
          TiffImageData rawImageData = getTiffRawImageData(
              byteSource, directory);
          directory.setTiffImageData(rawImageData);
        }
        if (directory.hasJpegImageData()) {
          JpegImageData rawJpegImageData = getJpegRawImageData(
              byteSource, directory);
          directory.setJpegImageData(rawJpegImageData);
        }
      }

      if (!listener.addDirectory(directory))
        return true;

      if (listener.readOffsetDirectories()) {
        List fieldsToRemove = new ArrayList();
        for (int j = 0; j < fields.size(); j++) {
          TiffField entry = (TiffField) fields.get(j);

          if (entry.tag == TiffConstants.EXIF_TAG_EXIF_OFFSET.tag
              || entry.tag == TiffConstants.EXIF_TAG_GPSINFO.tag
              || entry.tag == TiffConstants.EXIF_TAG_INTEROP_OFFSET.tag)
            ;
          else
            continue;

          int subDirectoryOffset = ((Number) entry.getValue())
              .intValue();
          int subDirectoryType;
          if (entry.tag == TiffConstants.EXIF_TAG_EXIF_OFFSET.tag)
            subDirectoryType = TiffDirectory.DIRECTORY_TYPE_EXIF;
          else if (entry.tag == TiffConstants.EXIF_TAG_GPSINFO.tag)
            subDirectoryType = TiffDirectory.DIRECTORY_TYPE_GPS;
          else if (entry.tag == TiffConstants.EXIF_TAG_INTEROP_OFFSET.tag)
            subDirectoryType = TiffDirectory.DIRECTORY_TYPE_INTEROPERABILITY;
          else
            throw new ImageReadException(
                "Unknown subdirectory type.");

          // Debug.debug("sub dir", subDirectoryOffset);
          boolean subDirectoryRead = readDirectory(byteSource,
              subDirectoryOffset, subDirectoryType,
              formatCompliance, listener, true, visited);

          if (!subDirectoryRead) {
            // Offset field pointed to invalid location.
            // This is a bug in certain cameras. Ignore offset
            // field.
            fieldsToRemove.add(entry);
          }

        }
        fields.removeAll(fieldsToRemove);
      }

      if (!ignoreNextDirectory && directory.nextDirectoryOffset > 0) {
        // Debug.debug("next dir", directory.nextDirectoryOffset );
        readDirectory(byteSource, directory.nextDirectoryOffset,
            dirType + 1, formatCompliance, listener, visited);
      }

      return true;
    } finally {
      try {
        if (is != null)
          is.close();
      } catch (Exception e) {
        Debug.debug(e);
      }
    }
  }

  public static interface Listener {
    public boolean setTiffHeader(TiffHeader tiffHeader);

    public boolean addDirectory(TiffDirectory directory);

    public boolean addField(TiffField field);

    public boolean readImageData();

    public boolean readOffsetDirectories();
  }

  private static class Collector implements Listener {
    private TiffHeader tiffHeader = null;
    private ArrayList directories = new ArrayList();
    private ArrayList fields = new ArrayList();
    private final boolean readThumbnails;

    public Collector() {
      this(null);
    }

    public Collector(Map params) {
      boolean readThumbnails = true;
      if (params != null && params.containsKey(PARAM_KEY_READ_THUMBNAILS))
        readThumbnails = Boolean.TRUE.equals(params
            .get(PARAM_KEY_READ_THUMBNAILS));
      this.readThumbnails = readThumbnails;
    }

    public boolean setTiffHeader(TiffHeader tiffHeader) {
      this.tiffHeader = tiffHeader;
      return true;
    }

    public boolean addDirectory(TiffDirectory directory) {
      directories.add(directory);
      return true;
    }

    public boolean addField(TiffField field) {
      fields.add(field);
      return true;
    }

    public boolean readImageData() {
      return readThumbnails;
    }

    public boolean readOffsetDirectories() {
      return true;
    }

    public TiffContents getContents() {
      return new TiffContents(tiffHeader, directories);
    }
  }

  private static class FirstDirectoryCollector extends Collector {
    private final boolean readImageData;

    public FirstDirectoryCollector(final boolean readImageData) {
      this.readImageData = readImageData;
    }

    public boolean addDirectory(TiffDirectory directory) {
      super.addDirectory(directory);
      return false;
    }

    public boolean readImageData() {
      return readImageData;
    }
  }

  private static class DirectoryCollector extends Collector {
    private final boolean readImageData;

    public DirectoryCollector(final boolean readImageData) {
      this.readImageData = readImageData;
    }

    public boolean addDirectory(TiffDirectory directory) {
      super.addDirectory(directory);
      return false;
    }

    public boolean readImageData() {
      return readImageData;
    }
  }

  public TiffContents readFirstDirectory(ByteSource byteSource, Map params,
      boolean readImageData, FormatCompliance formatCompliance)
      throws ImageReadException, IOException {
    Collector collector = new FirstDirectoryCollector(readImageData);
    read(byteSource, params, formatCompliance, collector);
    TiffContents contents = collector.getContents();
    if (contents.directories.size() < 1)
      throw new ImageReadException(
          "Image did not contain any directories.");
    return contents;
  }

  public TiffContents readDirectories(ByteSource byteSource,
      boolean readImageData, FormatCompliance formatCompliance)
      throws ImageReadException, IOException {
    Collector collector = new FirstDirectoryCollector(readImageData);
    readDirectories(byteSource, formatCompliance, collector);
    TiffContents contents = collector.getContents();
    if (contents.directories.size() < 1)
      throw new ImageReadException(
          "Image did not contain any directories.");
    return contents;
  }

  public TiffContents readContents(ByteSource byteSource, Map params,
      FormatCompliance formatCompliance) throws ImageReadException,
      IOException {

    Collector collector = new Collector(params);
    read(byteSource, params, formatCompliance, collector);
    TiffContents contents = collector.getContents();
    return contents;
  }

  public void read(ByteSource byteSource, Map params,
      FormatCompliance formatCompliance, Listener listener)
      throws ImageReadException, IOException {
    // TiffContents contents =
    readDirectories(byteSource, formatCompliance, listener);
  }

  private TiffImageData getTiffRawImageData(ByteSource byteSource,
      TiffDirectory directory) throws ImageReadException, IOException {
    ArrayList elements = directory.getTiffRawImageDataElements();
    TiffImageData.Data data[] = new TiffImageData.Data[elements.size()];
    for (int i = 0; i < elements.size(); i++) {
      TiffDirectory.ImageDataElement element = (TiffDirectory.ImageDataElement) elements
          .get(i);
      byte bytes[] = byteSource.getBlock(element.offset, element.length);
      data[i] = new TiffImageData.Data(element.offset, element.length,
          bytes);
    }

    if (directory.imageDataInStrips()) {
      TiffField rowsPerStripField = directory
          .findField(TIFF_TAG_ROWS_PER_STRIP);
      if (null == rowsPerStripField)
        throw new ImageReadException("Can't find rows per strip field.");
      int rowsPerStrip = rowsPerStripField.getIntValue();

      return new TiffImageData.Strips(data, rowsPerStrip);
    } else {
      TiffField tileWidthField = directory.findField(TIFF_TAG_TILE_WIDTH);
      if (null == tileWidthField)
        throw new ImageReadException("Can't find tile width field.");
      int tileWidth = tileWidthField.getIntValue();

      TiffField tileLengthField = directory
          .findField(TIFF_TAG_TILE_LENGTH);
      if (null == tileLengthField)
        throw new ImageReadException("Can't find tile length field.");
      int tileLength = tileLengthField.getIntValue();

      return new TiffImageData.Tiles(data, tileWidth, tileLength);
    }
  }

  private JpegImageData getJpegRawImageData(ByteSource byteSource,
      TiffDirectory directory) throws ImageReadException, IOException {
    ImageDataElement element = directory.getJpegRawImageDataElement();
    int offset = element.offset;
    int length = element.length;
    // Sony DCR-PC110 has an off-by-one error.
    if (offset + length == byteSource.getLength() + 1)
      length--;
    byte data[] = byteSource.getBlock(offset, length);
    return new JpegImageData(offset, length, data);
  }

}
TOP

Related Classes of org.apache.sanselan.formats.tiff.TiffReader

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.