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

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

/*
* 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$JFIFPieceImageData

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.