Package org.apache.sanselan.formats.jpeg.xmp

Source Code of org.apache.sanselan.formats.jpeg.xmp.JpegRewriter$JFIFPiece

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

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.ImageWriteException;
import org.apache.sanselan.common.BinaryFileParser;
import org.apache.sanselan.common.byteSources.ByteSource;
import org.apache.sanselan.formats.jpeg.JpegConstants;
import org.apache.sanselan.formats.jpeg.JpegUtils;
import org.apache.sanselan.formats.jpeg.iptc.IPTCParser;

/**
* Interface for Exif write/update/remove functionality for Jpeg/JFIF images.
* <p>
* <p>
* See the source of the XmpXmlUpdateExample class for example usage.
*
* @see org.apache.sanselan.sampleUsage.WriteXmpXmlExample
*/
public class JpegRewriter extends BinaryFileParser implements JpegConstants
{
  private static final int JPEG_BYTE_ORDER = BYTE_ORDER_NETWORK;

  /**
   * Constructor. to guess whether a file contains an image based on its file
   * extension.
   */
  public JpegRewriter()
  {
    setByteOrder(JPEG_BYTE_ORDER);
  }

  protected static class JFIFPieces
  {
    public final List pieces;
    public final List segmentPieces;

    public JFIFPieces(final List pieces, final List segmentPieces)
    {
      this.pieces = pieces;
      this.segmentPieces = segmentPieces;
    }

  }

  protected abstract static class JFIFPiece
  {
    protected abstract void write(OutputStream os) throws IOException;
   
    public String toString()
    {
      return "[" + this.getClass().getName() + "]";
    }
  }

  protected static class JFIFPieceSegment extends JFIFPiece
  {
    public final int marker;
    public final byte markerBytes[];
    public final byte segmentLengthBytes[];
    public final byte segmentData[];

    public JFIFPieceSegment(final int marker, final byte[] segmentData)
    {
      this(marker, int2ToByteArray(marker, JPEG_BYTE_ORDER),
          int2ToByteArray(segmentData.length + 2, JPEG_BYTE_ORDER),
          segmentData);
    }

    public JFIFPieceSegment(final int marker, final byte[] markerBytes,
        final byte[] segmentLengthBytes, final byte[] segmentData)
    {
      this.marker = marker;
      this.markerBytes = markerBytes;
      this.segmentLengthBytes = segmentLengthBytes;
      this.segmentData = segmentData;
    }

    public String toString()
    {
      return "[" + this.getClass().getName() + " (0x" + Integer.toHexString(marker) + ")]";
    }

    protected void write(OutputStream os) throws IOException
    {
      os.write(markerBytes);
      os.write(segmentLengthBytes);
      os.write(segmentData);
    }

    public boolean isApp1Segment()
    {
      return marker == JPEG_APP1_Marker;
    }

    public boolean isAppSegment()
    {
      return marker >= JPEG_APP0_Marker && marker <= JPEG_APP15_Marker;
    }

    public boolean isExifSegment()
    {
      if (marker != JPEG_APP1_Marker)
        return false;
      if (!byteArrayHasPrefix(segmentData, EXIF_IDENTIFIER_CODE))
        return false;
      return true;
    }

    public boolean isPhotoshopApp13Segment()
    {
      if (marker != JPEG_APP13_Marker)
        return false;
      if (!new IPTCParser().isPhotoshopJpegSegment(segmentData))
        return false;
      return true;
    }

    public boolean isXmpSegment()
    {
      if (marker != JPEG_APP1_Marker)
        return false;
      if (!byteArrayHasPrefix(segmentData, XMP_IDENTIFIER))
        return false;
      return true;
    }

  }

  protected static class JFIFPieceImageData extends JFIFPiece
  {
    public final byte markerBytes[];
    public final byte imageData[];

    public JFIFPieceImageData(final byte[] markerBytes,
        final byte[] imageData)
    {
      super();
      this.markerBytes = markerBytes;
      this.imageData = imageData;
    }

    protected void write(OutputStream os) throws IOException
    {
      os.write(markerBytes);
      os.write(imageData);
    }
  }

  protected JFIFPieces analyzeJFIF(ByteSource byteSource)
      throws ImageReadException, IOException
  // , ImageWriteException
  {
    final ArrayList pieces = new ArrayList();
    final List segmentPieces = new ArrayList();

    JpegUtils.Visitor visitor = new JpegUtils.Visitor() {
      // return false to exit before reading image data.
      public boolean beginSOS()
      {
        return true;
      }

      public void visitSOS(int marker, byte markerBytes[],
          byte imageData[])
      {
        pieces.add(new JFIFPieceImageData(markerBytes, imageData));
      }

      // return false to exit traversal.
      public boolean visitSegment(int marker, byte markerBytes[],
          int segmentLength, byte segmentLengthBytes[],
          byte segmentData[]) throws ImageReadException, IOException
      {
        JFIFPiece piece = new JFIFPieceSegment(marker, markerBytes,
            segmentLengthBytes, segmentData);
        pieces.add(piece);
        segmentPieces.add(piece);

        return true;
      }
    };

    new JpegUtils().traverseJFIF(byteSource, visitor);

    return new JFIFPieces(pieces, segmentPieces);
  }

  private static interface SegmentFilter
  {
    public boolean filter(JFIFPieceSegment segment);
  }

  private static final SegmentFilter EXIF_SEGMENT_FILTER = new SegmentFilter() {
    public boolean filter(JFIFPieceSegment segment)
    {
      return segment.isExifSegment();
    }
  };

  private static final SegmentFilter XMP_SEGMENT_FILTER = new SegmentFilter() {
    public boolean filter(JFIFPieceSegment segment)
    {
      return segment.isXmpSegment();
    }
  };

  private static final SegmentFilter PHOTOSHOP_APP13_SEGMENT_FILTER = new SegmentFilter() {
    public boolean filter(JFIFPieceSegment segment)
    {
      return segment.isPhotoshopApp13Segment();
    }
  };

  protected List removeXmpSegments(List segments)
  {
    return filterSegments(segments, XMP_SEGMENT_FILTER);
  }

  protected List removePhotoshopApp13Segments(List segments)
  {
    return filterSegments(segments, PHOTOSHOP_APP13_SEGMENT_FILTER);
  }

  protected List findPhotoshopApp13Segments(List segments)
  {
    return filterSegments(segments, PHOTOSHOP_APP13_SEGMENT_FILTER, true);
  }

  protected List removeExifSegments(List segments)
  {
    return filterSegments(segments, EXIF_SEGMENT_FILTER);
  }

  protected List filterSegments(List segments, SegmentFilter filter)
  {
    return filterSegments(segments, filter, false);
  }

  protected List filterSegments(List segments, SegmentFilter filter,
      boolean reverse)
  {
    List result = new ArrayList();

    for (int i = 0; i < segments.size(); i++)
    {
      JFIFPiece piece = (JFIFPiece) segments.get(i);
      if (piece instanceof JFIFPieceSegment)
      {
        if (filter.filter((JFIFPieceSegment) piece) ^ !reverse)
          result.add(piece);
      } else if(!reverse)
        result.add(piece);
    }

    return result;
  }

  protected List insertBeforeFirstAppSegments(List segments, List newSegments)
      throws ImageWriteException
  {
    int firstAppIndex = -1;
    for (int i = 0; i < segments.size(); i++)
    {
      JFIFPiece piece = (JFIFPiece) segments.get(i);
      if (!(piece instanceof JFIFPieceSegment))
        continue;

      JFIFPieceSegment segment = (JFIFPieceSegment) piece;
      if (segment.isAppSegment())
      {
        if (firstAppIndex == -1)
          firstAppIndex = i;
      }
    }

    List result = new ArrayList(segments);
    if (firstAppIndex == -1)
      throw new ImageWriteException("JPEG file has no APP segments.");
    result.addAll(firstAppIndex, newSegments);
    return result;
  }

  protected List insertAfterLastAppSegments(List segments, List newSegments)
      throws ImageWriteException
  {
    int lastAppIndex = -1;
    for (int i = 0; i < segments.size(); i++)
    {
      JFIFPiece piece = (JFIFPiece) segments.get(i);
      if (!(piece instanceof JFIFPieceSegment))
        continue;

      JFIFPieceSegment segment = (JFIFPieceSegment) piece;
      if (segment.isAppSegment())
        lastAppIndex = i;
    }

    List result = new ArrayList(segments);
    if (lastAppIndex == -1)
    {
      if(segments.size()<1)
        throw new ImageWriteException("JPEG file has no APP segments.");
      result.addAll(1, newSegments);
    }
    else
    result.addAll(lastAppIndex + 1, newSegments);
   
    return result;
  }

  protected void writeSegments(OutputStream os, List segments)
      throws ImageWriteException, IOException
  {
    try
    {
      os.write(SOI);

      for (int i = 0; i < segments.size(); i++)
      {
        JFIFPiece piece = (JFIFPiece) segments.get(i);
        piece.write(os);
      }
      os.close();
      os = null;
    } finally
    {
      try
      {
        if (os != null)
          os.close();
      } catch (Exception e)
      {
        // swallow exception; already in the context of an exception.
      }
    }
  }

  // private void writeSegment(OutputStream os, JFIFPieceSegment piece)
  // throws ImageWriteException, IOException
  // {
  // byte markerBytes[] = convertShortToByteArray(JPEG_APP1_Marker,
  // JPEG_BYTE_ORDER);
  // if (piece.segmentData.length > 0xffff)
  // throw new JpegSegmentOverflowException("Jpeg segment is too long: "
  // + piece.segmentData.length);
  // int segmentLength = piece.segmentData.length + 2;
  // byte segmentLengthBytes[] = convertShortToByteArray(segmentLength,
  // JPEG_BYTE_ORDER);
  //
  // os.write(markerBytes);
  // os.write(segmentLengthBytes);
  // os.write(piece.segmentData);
  // }

  public static class JpegSegmentOverflowException extends
      ImageWriteException
  {
    public JpegSegmentOverflowException(String s)
    {
      super(s);
    }
  }

}
TOP

Related Classes of org.apache.sanselan.formats.jpeg.xmp.JpegRewriter$JFIFPiece

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.