Package org.apache.sanselan.formats.tiff

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

/*
* 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.awt.Dimension;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.sanselan.FormatCompliance;
import org.apache.sanselan.ImageFormat;
import org.apache.sanselan.ImageInfo;
import org.apache.sanselan.ImageParser;
import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.ImageWriteException;
import org.apache.sanselan.common.IImageMetadata;
import org.apache.sanselan.common.byteSources.ByteSource;
import org.apache.sanselan.formats.tiff.constants.TiffConstants;
import org.apache.sanselan.formats.tiff.datareaders.DataReader;
import org.apache.sanselan.formats.tiff.photometricinterpreters.PhotometricInterpreter;
import org.apache.sanselan.formats.tiff.photometricinterpreters.PhotometricInterpreterBiLevel;
import org.apache.sanselan.formats.tiff.photometricinterpreters.PhotometricInterpreterCIELAB;
import org.apache.sanselan.formats.tiff.photometricinterpreters.PhotometricInterpreterCMYK;
import org.apache.sanselan.formats.tiff.photometricinterpreters.PhotometricInterpreterLogLUV;
import org.apache.sanselan.formats.tiff.photometricinterpreters.PhotometricInterpreterPalette;
import org.apache.sanselan.formats.tiff.photometricinterpreters.PhotometricInterpreterRGB;
import org.apache.sanselan.formats.tiff.photometricinterpreters.PhotometricInterpreterYCbCr;
import org.apache.sanselan.formats.tiff.write.TiffImageWriterLossy;

public class TiffImageParser extends ImageParser implements TiffConstants
{
  public TiffImageParser()
  {
    // setDebug(true);
  }

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

  public String getDefaultExtension()
  {
    return DEFAULT_EXTENSION;
  }

  private static final String DEFAULT_EXTENSION = ".tif";

  private static final String ACCEPTED_EXTENSIONS[] = { ".tif", ".tiff", };

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

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

  public byte[] getICCProfileBytes(ByteSource byteSource, Map params)
      throws ImageReadException, IOException
  {
    FormatCompliance formatCompliance = FormatCompliance.getDefault();
    TiffContents contents = new TiffReader(isStrict(params))
        .readFirstDirectory(byteSource, params, false, formatCompliance);
    TiffDirectory directory = (TiffDirectory) contents.directories.get(0);

    TiffField field = directory.findField(EXIF_TAG_ICC_PROFILE);
    if (null == field)
      return null;
    return field.oversizeValue;
  }

  public Dimension getImageSize(ByteSource byteSource, Map params)
      throws ImageReadException, IOException
  {
    FormatCompliance formatCompliance = FormatCompliance.getDefault();
    TiffContents contents = new TiffReader(isStrict(params))
        .readFirstDirectory(byteSource, params, false, formatCompliance);
    TiffDirectory directory = (TiffDirectory) contents.directories.get(0);

    int width = directory.findField(TIFF_TAG_IMAGE_WIDTH).getIntValue();
    int height = directory.findField(TIFF_TAG_IMAGE_LENGTH).getIntValue();

    return new Dimension(width, height);
  }

  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
  {
    FormatCompliance formatCompliance = FormatCompliance.getDefault();
    TiffContents contents = new TiffReader(isStrict(params)).readContents(
        byteSource, params, formatCompliance);

    ArrayList directories = contents.directories;

    TiffImageMetadata result = new TiffImageMetadata(contents);

    for (int i = 0; i < directories.size(); i++)
    {
      TiffDirectory dir = (TiffDirectory) directories.get(i);

      TiffImageMetadata.Directory metadataDirectory = new TiffImageMetadata.Directory(
          dir);

      ArrayList entries = dir.getDirectoryEntrys();

      for (int j = 0; j < entries.size(); j++)
      {
        TiffField entry = (TiffField) entries.get(j);
        metadataDirectory.add(entry);
      }

      result.add(metadataDirectory);
    }

    return result;
  }

  public ImageInfo getImageInfo(ByteSource byteSource, Map params)
      throws ImageReadException, IOException
  {
    FormatCompliance formatCompliance = FormatCompliance.getDefault();
    TiffContents contents = new TiffReader(isStrict(params))
        .readDirectories(byteSource, false, formatCompliance);
    TiffDirectory directory = (TiffDirectory) contents.directories.get(0);

    TiffField widthField = directory.findField(TIFF_TAG_IMAGE_WIDTH, true);
    TiffField heightField = directory
        .findField(TIFF_TAG_IMAGE_LENGTH, true);

    if ((widthField == null) || (heightField == null))
      throw new ImageReadException("TIFF image missing size info.");

    int height = heightField.getIntValue();
    int width = widthField.getIntValue();

    // -------------------

    TiffField resolutionUnitField = directory
        .findField(TIFF_TAG_RESOLUTION_UNIT);
    int resolutionUnit = 2; // Inch
    if ((resolutionUnitField != null)
        && (resolutionUnitField.getValue() != null))
      resolutionUnit = resolutionUnitField.getIntValue();

    double unitsPerInch = -1;
    switch (resolutionUnit)
    {
    case 1:
      break;
    case 2: // Inch
      unitsPerInch = 1.0;
      break;
    case 3: // Meter
      unitsPerInch = 0.0254;
      break;
    default:
      break;

    }
    TiffField xResolutionField = directory.findField(TIFF_TAG_XRESOLUTION);
    TiffField yResolutionField = directory.findField(TIFF_TAG_YRESOLUTION);

    int physicalWidthDpi = -1;
    float physicalWidthInch = -1;
    int physicalHeightDpi = -1;
    float physicalHeightInch = -1;

    if (unitsPerInch > 0)
    {
      if ((xResolutionField != null)
          && (xResolutionField.getValue() != null))
      {
        double XResolutionPixelsPerUnit = xResolutionField
            .getDoubleValue();
        physicalWidthDpi = (int) (XResolutionPixelsPerUnit / unitsPerInch);
        physicalWidthInch = (float) (width / (XResolutionPixelsPerUnit * unitsPerInch));
      }
      if ((yResolutionField != null)
          && (yResolutionField.getValue() != null))
      {
        double YResolutionPixelsPerUnit = yResolutionField
            .getDoubleValue();
        physicalHeightDpi = (int) (YResolutionPixelsPerUnit / unitsPerInch);
        physicalHeightInch = (float) (height / (YResolutionPixelsPerUnit * unitsPerInch));
      }
    }

    // -------------------

    TiffField bitsPerSampleField = directory
        .findField(TIFF_TAG_BITS_PER_SAMPLE);

    int bitsPerSample = -1;
    if ((bitsPerSampleField != null)
        && (bitsPerSampleField.getValue() != null))
      bitsPerSample = bitsPerSampleField.getIntValueOrArraySum();

    int bitsPerPixel = bitsPerSample; // assume grayscale;
    // dunno if this handles colormapped images correctly.

    // -------------------

    ArrayList comments = new ArrayList();
    ArrayList entries = directory.entries;
    for (int i = 0; i < entries.size(); i++)
    {
      TiffField field = (TiffField) entries.get(i);
      String comment = field.toString();
      comments.add(comment);
    }

    ImageFormat format = ImageFormat.IMAGE_FORMAT_TIFF;
    String formatName = "TIFF Tag-based Image File Format";
    String mimeType = "image/tiff";
    int numberOfImages = contents.directories.size();
    // not accurate ... only reflects first
    boolean isProgressive = false;
    // is TIFF ever interlaced/progressive?

    String formatDetails = "Tiff v." + contents.header.tiffVersion;

    boolean isTransparent = false; // TODO: wrong
    boolean usesPalette = false;
    TiffField colorMapField = directory.findField(TIFF_TAG_COLOR_MAP);
    if (colorMapField != null)
      usesPalette = true;

    int colorType = ImageInfo.COLOR_TYPE_RGB;

    int compression = directory.findField(TIFF_TAG_COMPRESSION)
        .getIntValue();
    String compressionAlgorithm;

    switch (compression)
    {
    case TIFF_COMPRESSION_UNCOMPRESSED_1:
      compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_NONE;
      break;
    case TIFF_COMPRESSION_CCITT_1D:
      compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_CCITT_1D;
      break;
    case TIFF_COMPRESSION_CCITT_GROUP_3:
      compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_CCITT_GROUP_3;
      break;
    case TIFF_COMPRESSION_CCITT_GROUP_4:
      compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_CCITT_GROUP_4;
      break;
    case TIFF_COMPRESSION_LZW:
      compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_LZW;
      break;
    case TIFF_COMPRESSION_JPEG:
      compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_JPEG;
      break;
    case TIFF_COMPRESSION_UNCOMPRESSED_2:
      compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_NONE;
      break;
    case TIFF_COMPRESSION_PACKBITS:
      compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_PACKBITS;
      break;
    default:
      compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_UNKNOWN;
      break;
    }

    ImageInfo result = new ImageInfo(formatDetails, bitsPerPixel, comments,
        format, formatName, height, mimeType, numberOfImages,
        physicalHeightDpi, physicalHeightInch, physicalWidthDpi,
        physicalWidthInch, width, isProgressive, isTransparent,
        usesPalette, colorType, compressionAlgorithm);

    return result;
  }

  public String getXmpXml(ByteSource byteSource, Map params)
      throws ImageReadException, IOException
  {
    FormatCompliance formatCompliance = FormatCompliance.getDefault();
    TiffContents contents = new TiffReader(isStrict(params))
        .readDirectories(byteSource, false, formatCompliance);
    TiffDirectory directory = (TiffDirectory) contents.directories.get(0);

    TiffField xmpField = directory.findField(TIFF_TAG_XMP, false);
    if (xmpField == null)
      return null;

    byte bytes[] = xmpField.getByteArrayValue();

    try
    {
      // segment data is UTF-8 encoded xml.
      String xml = new String(bytes, "utf-8");
      return xml;
    } catch (UnsupportedEncodingException e)
    {
      throw new ImageReadException("Invalid JPEG XMP Segment.");
    }
  }

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

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

        imageData.toString(pw, "");
      }

      pw.println("");

      // try
      {
        FormatCompliance formatCompliance = FormatCompliance
            .getDefault();
        Map params = null;
        TiffContents contents = new TiffReader(true).readContents(
            byteSource, params, formatCompliance);

        ArrayList directories = contents.directories;

        if (directories == null)
          return false;

        for (int d = 0; d < directories.size(); d++)
        {
          TiffDirectory directory = (TiffDirectory) directories
              .get(d);

          ArrayList entries = directory.entries;

          if (entries == null)
            return false;

          // Debug.debug("directory offset", directory.offset);

          for (int i = 0; i < entries.size(); i++)
          {
            TiffField field = (TiffField) entries.get(i);

            field.dump(pw, d + "");
          }
        }

        pw.println("");
      }
      // catch (Exception e)
      // {
      // Debug.debug(e);
      // pw.println("");
      // return false;
      // }

      return true;
    } finally
    {
      pw.println("");
    }
  }

  public FormatCompliance getFormatCompliance(ByteSource byteSource)
      throws ImageReadException, IOException
  {
    FormatCompliance formatCompliance = FormatCompliance.getDefault();
    Map params = null;
    new TiffReader(isStrict(params)).readContents(byteSource, params,
        formatCompliance);
    return formatCompliance;
  }

  public List collectRawImageData(ByteSource byteSource, Map params)
      throws ImageReadException, IOException
  {
    FormatCompliance formatCompliance = FormatCompliance.getDefault();
    TiffContents contents = new TiffReader(isStrict(params))
        .readDirectories(byteSource, true, formatCompliance);

    List result = new ArrayList();
    for (int i = 0; i < contents.directories.size(); i++)
    {
      TiffDirectory directory = (TiffDirectory) contents.directories
          .get(i);
      List dataElements = directory.getTiffRawImageDataElements();
      for (int j = 0; j < dataElements.size(); j++)
      {
        TiffDirectory.ImageDataElement element = (TiffDirectory.ImageDataElement) dataElements
            .get(j);
        byte bytes[] = byteSource.getBlock(element.offset,
            element.length);
        result.add(bytes);
      }
    }
    return result;
  }

  public BufferedImage getBufferedImage(ByteSource byteSource, Map params)
      throws ImageReadException, IOException
  {
    FormatCompliance formatCompliance = FormatCompliance.getDefault();
    TiffContents contents = new TiffReader(isStrict(params))
        .readFirstDirectory(byteSource, params, true, formatCompliance);
    TiffDirectory directory = (TiffDirectory) contents.directories.get(0);
    BufferedImage result = directory.getTiffImage(params);
    if (null == result)
      throw new ImageReadException("TIFF does not contain an image.");
    return result;
  }

  protected BufferedImage getBufferedImage(TiffDirectory directory, Map params)
      throws ImageReadException, IOException
  {
    ArrayList entries = directory.entries;

    if (entries == null)
      throw new ImageReadException("TIFF missing entries");

    int photometricInterpretation = directory.findField(
        TIFF_TAG_PHOTOMETRIC_INTERPRETATION, true).getIntValue();
    int compression = directory.findField(TIFF_TAG_COMPRESSION, true)
        .getIntValue();
    int width = directory.findField(TIFF_TAG_IMAGE_WIDTH, true)
        .getIntValue();
    int height = directory.findField(TIFF_TAG_IMAGE_LENGTH, true)
        .getIntValue();
    int samplesPerPixel = directory.findField(TIFF_TAG_SAMPLES_PER_PIXEL,
        true).getIntValue();
    int bitsPerSample[] = directory.findField(TIFF_TAG_BITS_PER_SAMPLE,
        true).getIntArrayValue();
    // TODO: why are we using bits per sample twice? because one is a sum.
    int bitsPerPixel = directory.findField(TIFF_TAG_BITS_PER_SAMPLE, true)
        .getIntValueOrArraySum();

    // int bitsPerPixel = getTagAsValueOrArraySum(entries,
    // TIFF_TAG_BITS_PER_SAMPLE);

    int predictor = -1;
    {
      // dumpOptionalNumberTag(entries, TIFF_TAG_FILL_ORDER);
      // dumpOptionalNumberTag(entries, TIFF_TAG_FREE_BYTE_COUNTS);
      // dumpOptionalNumberTag(entries, TIFF_TAG_FREE_OFFSETS);
      // dumpOptionalNumberTag(entries, TIFF_TAG_ORIENTATION);
      // dumpOptionalNumberTag(entries, TIFF_TAG_PLANAR_CONFIGURATION);
      TiffField predictorField = directory.findField(TIFF_TAG_PREDICTOR);
      if (null != predictorField)
        predictor = predictorField.getIntValueOrArraySum();
    }

    if (samplesPerPixel != bitsPerSample.length)
      throw new ImageReadException("Tiff: samplesPerPixel ("
          + samplesPerPixel + ")!=fBitsPerSample.length ("
          + bitsPerSample.length + ")");

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

    PhotometricInterpreter photometricInterpreter = getPhotometricInterpreter(
        directory, photometricInterpretation, bitsPerPixel,
        bitsPerSample, predictor, samplesPerPixel, width, height);

    TiffImageData imageData = directory.getTiffImageData();

    DataReader dataReader = imageData.getDataReader(entries,
        photometricInterpreter, bitsPerPixel, bitsPerSample, predictor,
        samplesPerPixel, width, height, compression);

    dataReader.readImageData(result);

    photometricInterpreter.dumpstats();

    return result;
  }

  private PhotometricInterpreter getPhotometricInterpreter(
      TiffDirectory directory, int photometricInterpretation,
      int bitsPerPixel, int bitsPerSample[], int predictor,
      int samplesPerPixel, int width, int height) throws IOException,
      ImageReadException
  {
    switch (photometricInterpretation)
    {
    case 0:
    case 1:
      boolean invert = photometricInterpretation == 0;

      return new PhotometricInterpreterBiLevel(bitsPerPixel,
          samplesPerPixel, bitsPerSample, predictor, width, height,
          invert);
    case 3: // Palette
    {
      int colorMap[] = directory.findField(TIFF_TAG_COLOR_MAP, true)
          .getIntArrayValue();

      int expected_colormap_size = 3 * (1 << bitsPerPixel);

      if (colorMap.length != expected_colormap_size)
        throw new ImageReadException("Tiff: fColorMap.length ("
            + colorMap.length + ")!=expected_colormap_size ("
            + expected_colormap_size + ")");

      return new PhotometricInterpreterPalette(samplesPerPixel,
          bitsPerSample, predictor, width, height, colorMap);
    }
    case 2: // RGB
      return new PhotometricInterpreterRGB(samplesPerPixel,
          bitsPerSample, predictor, width, height);
    case 5: // CMYK
      return new PhotometricInterpreterCMYK(samplesPerPixel,
          bitsPerSample, predictor, width, height);
    case 6: //
    {
      double yCbCrCoefficients[] = directory.findField(
          TIFF_TAG_YCBCR_COEFFICIENTS, true).getDoubleArrayValue();

      int yCbCrPositioning[] = directory.findField(
          TIFF_TAG_YCBCR_POSITIONING, true).getIntArrayValue();
      int yCbCrSubSampling[] = directory.findField(
          TIFF_TAG_YCBCR_SUB_SAMPLING, true).getIntArrayValue();

      double referenceBlackWhite[] = directory.findField(
          TIFF_TAG_REFERENCE_BLACK_WHITE, true).getDoubleArrayValue();

      return new PhotometricInterpreterYCbCr(yCbCrCoefficients,
          yCbCrPositioning, yCbCrSubSampling, referenceBlackWhite,
          samplesPerPixel, bitsPerSample, predictor, width, height);
    }

    case 8:
      return new PhotometricInterpreterCIELAB(samplesPerPixel,
          bitsPerSample, predictor, width, height);

    case 32844:
    case 32845: {
      boolean yonly = (photometricInterpretation == 32844);
      return new PhotometricInterpreterLogLUV(samplesPerPixel,
          bitsPerSample, predictor, width, height, yonly);
    }

    default:
      throw new ImageReadException(
          "TIFF: Unknown fPhotometricInterpretation: "
              + photometricInterpretation);
    }
  }

  public void writeImage(BufferedImage src, OutputStream os, Map params)
      throws ImageWriteException, IOException
  {
    new TiffImageWriterLossy().writeImage(src, os, params);
  }

}
TOP

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

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.