All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.domenicseccareccia.jpegautorotate.imaging.JpegImageMetadata Maven / Gradle / Ivy

/**
 * Copyright (c) 2019-2021 Domenic Seccareccia and contributors
 *
 * 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 com.domenicseccareccia.jpegautorotate.imaging;

import com.domenicseccareccia.jpegautorotate.JpegAutorotateException;
import org.apache.commons.imaging.ImageReadException;
import org.apache.commons.imaging.ImageWriteException;
import org.apache.commons.imaging.formats.jpeg.JpegPhotoshopMetadata;
import org.apache.commons.imaging.formats.tiff.*;
import org.apache.commons.imaging.formats.tiff.constants.ExifTagConstants;
import org.apache.commons.imaging.formats.tiff.constants.TiffDirectoryConstants;
import org.apache.commons.imaging.formats.tiff.constants.TiffDirectoryType;
import org.apache.commons.imaging.formats.tiff.constants.TiffTagConstants;
import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoShort;
import org.apache.commons.imaging.formats.tiff.write.TiffOutputDirectory;
import org.apache.commons.imaging.formats.tiff.write.TiffOutputSet;

import java.awt.color.ICC_Profile;
import java.awt.image.BufferedImage;

class JpegImageMetadata {

    private org.apache.commons.imaging.formats.jpeg.JpegImageMetadata rawMetadata;
    private TiffOutputSet outputSet;
    private TiffOutputDirectory exifDirectory;
    private ICC_Profile iccProfile;
    private String xmpXml;
    private BufferedImage thumbnail;
    private int originalOrientation;

    protected JpegImageMetadata(final byte[] bytes) throws JpegAutorotateException {
        this.rawMetadata = JpegImageMetadataReader.readRawMetadata(bytes);
        TiffImageMetadata tempExif = JpegImageMetadataReader.readExif(this.rawMetadata);
        this.outputSet = JpegImageMetadataReader.readOutputSet(tempExif);
        this.exifDirectory = getOrCreateExifDirectory();
        this.iccProfile = JpegImageMetadataReader.readIccProfile(bytes);
        this.xmpXml = JpegImageMetadataReader.readXmpXml(bytes);
        this.thumbnail = JpegImageMetadataReader.readThumbnail(this.rawMetadata);
        this.originalOrientation = getOrientation();
    }

    protected TiffOutputSet getOutputSet() {
        return this.outputSet;
    }

    protected ICC_Profile getIccProfile() {
        return this.iccProfile;
    }

    protected String getXmpXml() {
        return this.xmpXml;
    }

    protected JpegPhotoshopMetadata getPhotoshop() {
        return this.rawMetadata.getPhotoshop();
    }

    protected void setThumbnail(BufferedImage thumbnail) {
        this.thumbnail = thumbnail;
    }

    protected BufferedImage getThumbnail() {
        return thumbnail;
    }

    /**
     * Updates the thumbnail image.
     *
     * @param bytes
     *              A {@code byte[]} containing thumbnail image data.
     */
    protected void updateThumbnail(final byte[] bytes) {
        TiffOutputDirectory thumbnailDirectory = outputSet.findDirectory(TiffDirectoryConstants.DIRECTORY_TYPE_SUB);
        JpegImageData jpg = new JpegImageData(thumbnailDirectory.getRawJpegImageData().offset, bytes.length, bytes);

        thumbnailDirectory.setJpegImageData(jpg);
    }

    /**
     * Attempts to get the EXIF {@code Orientation} metadata tag value.
     *
     * @return If successful, EXIF {@code Orientation} metadata tag value.
     * @throws JpegAutorotateException
     *              In the event the EXIF {@code Orientation} metadata tag is not found.
     */
    protected int getOrientation() throws JpegAutorotateException {
        TiffField field = this.rawMetadata.findEXIFValue(TiffTagConstants.TIFF_TAG_ORIENTATION);

        if (field == null) {
            throw new JpegAutorotateException("JPEG image does not have an EXIF Orientation metadata tag.");
        }

        try {
            return field.getIntValue();
        } catch (ImageReadException e) {
            throw new JpegAutorotateException("Unable to read JPEG image EXIF Orientation metadata tag.", e);
        }
    }

    /**
     * Attempts to read or create {@code ExifDirectory}.
     *
     * @return If successful, a valid {@code TiffOutputDirectory} instance.
     * @throws JpegAutorotateException
     *              In the event the {@code ExifDirectory} does not exist or
     *              is unable to be created.
     */
    private TiffOutputDirectory getOrCreateExifDirectory() throws JpegAutorotateException {
        try {
            return this.outputSet.getOrCreateExifDirectory();
        } catch (ImageWriteException e) {
            throw new JpegAutorotateException("Unable to get or create JPEG image EXIF metadata directory.", e);
        }
    }

    /**
     * Attempts to update {@code metadata} information:
     * 

    *
  • EXIF
  • *
  • XMP
  • *

* * @throws JpegAutorotateException * In the event the EXIF {@code metadata} is unable to be updated. */ protected void updateMetadata() throws JpegAutorotateException { updateExifOrientation(); updateAllMetadataDimensions(); } /** * Attempts to update all {@code metadata} information containing: *

    *
  • Height
  • *
  • Width
  • *

* * @throws JpegAutorotateException * In the event the EXIF {@code Height} and/or * {@code Width} metadata is unable to be updated. */ private void updateAllMetadataDimensions() throws JpegAutorotateException { if (this.outputSet.findField(ExifTagConstants.EXIF_TAG_EXIF_IMAGE_WIDTH) != null || this.outputSet.findField(ExifTagConstants.EXIF_TAG_EXIF_IMAGE_LENGTH) != null) { try { int height = this.rawMetadata.findEXIFValue(ExifTagConstants.EXIF_TAG_EXIF_IMAGE_LENGTH).getIntValue(); int width = this.rawMetadata.findEXIFValue(ExifTagConstants.EXIF_TAG_EXIF_IMAGE_WIDTH).getIntValue(); switch (this.originalOrientation) { case TiffTagConstants.ORIENTATION_VALUE_MIRROR_HORIZONTAL_AND_ROTATE_270_CW: case TiffTagConstants.ORIENTATION_VALUE_ROTATE_90_CW: case TiffTagConstants.ORIENTATION_VALUE_MIRROR_HORIZONTAL_AND_ROTATE_90_CW: case TiffTagConstants.ORIENTATION_VALUE_ROTATE_270_CW: height = this.rawMetadata.findEXIFValue(ExifTagConstants.EXIF_TAG_EXIF_IMAGE_WIDTH).getIntValue(); width = this.rawMetadata.findEXIFValue(ExifTagConstants.EXIF_TAG_EXIF_IMAGE_LENGTH).getIntValue(); break; default: break; } updateExifHeight(height); updateExifWidth(width); updateXmpXml(height, width); } catch (Exception e) { throw new JpegAutorotateException("Unable to read JPEG image EXIF Image Width and/or Image Height metadata tags.", e); } } } /** * Attempts to update EXIF {@code Orientation} metadata tag. * * @throws JpegAutorotateException * In the event the EXIF {@code Orientation} metadata tag * is unable to be updated. */ private void updateExifOrientation() throws JpegAutorotateException { try { this.outputSet.removeField(TiffTagConstants.TIFF_TAG_ORIENTATION); this.outputSet.getRootDirectory().add(TiffTagConstants.TIFF_TAG_ORIENTATION, ((Integer) TiffTagConstants.ORIENTATION_VALUE_HORIZONTAL_NORMAL).shortValue()); } catch (ImageWriteException e) { throw new JpegAutorotateException("Unable to update JPEG image EXIF Orientation metadata tag to Horizontal (normal).", e); } } /** * Attempts to update EXIF {@code Width} metadata tags. * * @param width * @throws JpegAutorotateException * In the event the EXIF {@code Width} metadata tags * are unable to be updated. */ private void updateExifWidth(int width) throws JpegAutorotateException { try { // EXIF Width this.outputSet.removeField(ExifTagConstants.EXIF_TAG_EXIF_IMAGE_WIDTH); this.exifDirectory.add(ExifTagConstants.EXIF_TAG_EXIF_IMAGE_WIDTH, ((Integer) width).shortValue()); // TIFF RelatedImageWidth TagInfoShort relatedImageWidth = new TagInfoShort("RelatedImageWidth", 0x1001, TiffDirectoryType.EXIF_DIRECTORY_INTEROP_IFD); if (this.outputSet.findField(relatedImageWidth) != null) { this.outputSet.removeField(relatedImageWidth); this.exifDirectory.add(relatedImageWidth, ((Integer) width).shortValue()); } } catch (ImageWriteException e) { throw new JpegAutorotateException("Unable to update JPEG image EXIF Image Width metadata tags.", e); } } /** * Attempts to update EXIF {@code Height} metadata tags. * * @param height * @throws JpegAutorotateException * In the event the EXIF {@code Height} metadata tags * are unable to be updated. */ private void updateExifHeight(int height) throws JpegAutorotateException { try { // EXIF Height this.outputSet.removeField(ExifTagConstants.EXIF_TAG_EXIF_IMAGE_LENGTH); this.exifDirectory.add(ExifTagConstants.EXIF_TAG_EXIF_IMAGE_LENGTH, ((Integer) height).shortValue()); // TIFF RelatedImageHeight TagInfoShort relatedImageHeight = new TagInfoShort("RelatedImageHeight", 0x1002, TiffDirectoryType.EXIF_DIRECTORY_INTEROP_IFD); if (this.outputSet.findField(relatedImageHeight) != null) { this.outputSet.removeField(relatedImageHeight); this.exifDirectory.add(relatedImageHeight, ((Integer) height).shortValue()); } } catch (ImageWriteException e) { throw new JpegAutorotateException("Unable to update JPEG image EXIF Image Height metadata tags.", e); } } /** * Attempts to update {@code xmpXml} metadata values. * * @param height * @param width */ private void updateXmpXml(int height, int width) { String xmp = getXmpXml(); if (xmp == null) { return; } this.xmpXml = xmp.replaceAll("tiff:Orientation=\"\\d\"", "tiff:Orientation=\"" + ((Integer) TiffTagConstants.ORIENTATION_VALUE_HORIZONTAL_NORMAL).shortValue() + "\"") .replaceAll("tiff:ImageLength=\"\\d+\"", "tiff:ImageLength=\"" + height + "\"") .replaceAll("tiff:ImageWidth=\"\\d+\"", "tiff:ImageWidth=\"" + width + "\"") .replaceAll("exif:PixelXDimension=\"\\d+\"", "exif:PixelXDimension=\"" + width + "\"") .replaceAll("exif:PixelYDimension=\"\\d+\"", "exif:PixelYDimension=\"" + height + "\"") .replaceAll("xmp:ThumbnailsHeight=\"\\d+\"", "xmp:ThumbnailsHeight=\"" + getThumbnail().getHeight() + "\"") .replaceAll("xmp:ThumbnailsWidth=\"\\d+\"", "xmp:ThumbnailsWidth=\"" + getThumbnail().getWidth() + "\""); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy