FileDocCategorySizeDatePackage
MetadataBlockDataPicture.javaAPI DocJaudiotagger 2.0.411999Wed Jun 08 11:59:18 BST 2011org.jaudiotagger.audio.flac.metadatablock

MetadataBlockDataPicture.java

package org.jaudiotagger.audio.flac.metadatablock;

import org.jaudiotagger.audio.generic.Utils;
import org.jaudiotagger.tag.FieldKey;
import org.jaudiotagger.tag.InvalidFrameException;
import org.jaudiotagger.tag.TagField;
import org.jaudiotagger.tag.id3.valuepair.TextEncoding;
import org.jaudiotagger.tag.reference.PictureTypes;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.util.logging.Logger;


/**
 * Picture Block
 * <p/>
 * <p/>
 * <p>This block is for storing pictures associated with the file, most commonly cover art from CDs.
 * There may be more than one PICTURE block in a file. The picture format is similar to the APIC frame in ID3v2.
 * The PICTURE block has a type, MIME type, and UTF-8 description like ID3v2, and supports external linking via URL
 * (though this is discouraged). The differences are that there is no uniqueness constraint on the description field,
 * and the MIME type is mandatory. The FLAC PICTURE block also includes the resolution, color depth, and palette size
 * so that the client can search for a suitable picture without having to scan them all
 * <p/>
 * Format:
 * <Size in bits> Info
 * <32> The picture type according to the ID3v2 APIC frame: (There may only be one each of picture type 1 and 2 in a file)
 * <32> 	The length of the MIME type string in bytes.
 * <n*8> 	The MIME type string, in printable ASCII characters 0x20-0x7e. The MIME type may also be --> to signify that the data part is a URL of the picture instead of the picture data itself.
 * <32> 	The length of the description string in bytes.
 * <n*8> 	The description of the picture, in UTF-8.
 * <32> 	The width of the picture in pixels.
 * <32> 	The height of the picture in pixels.
 * <32> 	The color depth of the picture in bits-per-pixel.
 * <32> 	For indexed-color pictures (e.g. GIF), the number of colors used, or 0 for non-indexed pictures.
 * <32> 	The length of the picture data in bytes.
 * <n*8> 	The binary picture data.
 */
public class MetadataBlockDataPicture implements MetadataBlockData, TagField
{
    public static final String IMAGE_IS_URL = "-->";

    private int pictureType;
    private String mimeType;
    private String description;
    private int width;
    private int height;
    private int colourDepth;
    private int indexedColouredCount;
    private byte[] imageData;

    // Logger Object
    public static Logger logger = Logger.getLogger("org.jaudiotagger.audio.flac.MetadataBlockDataPicture");

    private void initFromByteBuffer(ByteBuffer rawdata) throws IOException, InvalidFrameException
    {
        //Picture Type
        pictureType = rawdata.getInt();
        if (pictureType >= PictureTypes.getInstanceOf().getSize())
        {
            throw new InvalidFrameException("PictureType was:" + pictureType + "but the maximum allowed is " + (PictureTypes.getInstanceOf().getSize() - 1));
        }

        //MimeType
        int mimeTypeSize = rawdata.getInt();
        mimeType = getString(rawdata, mimeTypeSize, "ISO-8859-1");

        //Description
        int descriptionSize = rawdata.getInt();
        description = getString(rawdata, descriptionSize, "UTF-8");

        //Image width
        width = rawdata.getInt();

        //Image height
        height = rawdata.getInt();

        //Colour Depth
        colourDepth = rawdata.getInt();

        //Indexed Colour Count
        indexedColouredCount = rawdata.getInt();

        //ImageData
        int rawdataSize = rawdata.getInt();
        imageData = new byte[rawdataSize];
        rawdata.get(imageData);

        logger.config("Read image:" + this.toString());
    }

    /**
     * Initialize MetaBlockDataPicture from byteBuffer
     *
     * @param rawdata
     * @throws IOException
     * @throws InvalidFrameException
     */
    public MetadataBlockDataPicture(ByteBuffer rawdata) throws IOException, InvalidFrameException
    {
        initFromByteBuffer(rawdata);
    }

    /**
     * Construct picture block by reading from file, the header informs us how many bytes we should be reading from
     *
     * @param header
     * @param raf
     * @throws java.io.IOException
     * @throws org.jaudiotagger.tag.InvalidFrameException
     */
    //TODO check for buffer underflows see http://research.eeye.com/html/advisories/published/AD20071115.html
    public MetadataBlockDataPicture(MetadataBlockHeader header, RandomAccessFile raf) throws IOException, InvalidFrameException
    {
        ByteBuffer rawdata = ByteBuffer.allocate(header.getDataLength());
        int bytesRead = raf.getChannel().read(rawdata);
        if (bytesRead < header.getDataLength())
        {
            throw new IOException("Unable to read required number of databytes read:" + bytesRead + ":required:" + header.getDataLength());
        }
        rawdata.rewind();
        initFromByteBuffer(rawdata);


    }

    /**
     * Construct new MetadataPicture block
     * @param imageData
     * @param pictureType
     * @param mimeType
     * @param description
     * @param width
     * @param height
     * @param colourDepth
     * @param indexedColouredCount
     */
    public MetadataBlockDataPicture(byte[] imageData, int pictureType, String mimeType, String description, int width, int height, int colourDepth, int indexedColouredCount)
    {
        //Picture Type
        this.pictureType = pictureType;

        //MimeType
        this.mimeType = mimeType;

        //Description
        this.description = description;

        this.width = width;

        this.height = height;

        this.colourDepth = colourDepth;

        this.indexedColouredCount = indexedColouredCount;
        //ImageData
        this.imageData = imageData;
    }

    private String getString(ByteBuffer rawdata, int length, String charset) throws IOException
    {
        byte[] tempbuffer = new byte[length];
        rawdata.get(tempbuffer);
        return new String(tempbuffer, charset);
    }

    public byte[] getBytes()
    {
        try
        {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            baos.write(Utils.getSizeBEInt32(pictureType));
            baos.write(Utils.getSizeBEInt32(mimeType.length()));
            baos.write(mimeType.getBytes("ISO-8859-1"));
            baos.write(Utils.getSizeBEInt32(description.length()));
            baos.write(description.getBytes("UTF-8"));
            baos.write(Utils.getSizeBEInt32(width));
            baos.write(Utils.getSizeBEInt32(height));
            baos.write(Utils.getSizeBEInt32(colourDepth));
            baos.write(Utils.getSizeBEInt32(indexedColouredCount));
            baos.write(Utils.getSizeBEInt32(imageData.length));
            baos.write(imageData);
            return baos.toByteArray();

        }
        catch (IOException ioe)
        {
            throw new RuntimeException(ioe.getMessage());
        }
    }

    public int getLength()
    {
        return getBytes().length;
    }

    public int getPictureType()
    {
        return pictureType;
    }

    public String getMimeType()
    {
        return mimeType;
    }

    public String getDescription()
    {
        return description;
    }

    public int getWidth()
    {
        return width;
    }

    public int getHeight()
    {
        return height;
    }

    public int getColourDepth()
    {
        return colourDepth;
    }

    public int getIndexedColourCount()
    {
        return indexedColouredCount;
    }

    public byte[] getImageData()
    {
        return imageData;
    }

    /**
     * @return true if imagedata  is held as a url rather than actually being imagedata
     */
    public boolean isImageUrl()
    {
        return getMimeType().equals(IMAGE_IS_URL);
    }

    /**
     * @return the image url if there is otherwise return an empty String
     */
    public String getImageUrl()
    {
        if (isImageUrl())
        {
            return Utils.getString(getImageData(), 0, getImageData().length, TextEncoding.CHARSET_ISO_8859_1);
        }
        else
        {
            return "";
        }
    }

    public String toString()
    {
        return PictureTypes.getInstanceOf().getValueForId(pictureType) + ":" + mimeType + ":" + description + ":" + "width:" + width + ":height:" + height + ":colourdepth:" + colourDepth + ":indexedColourCount:" + indexedColouredCount + ":image size in bytes:" + imageData.length;
    }

    /**
     * This method copies the data of the given field to the current data.<br>
     *
     * @param field The field containing the data to be taken.
     */
    public void copyContent(TagField field)
    {
        throw new UnsupportedOperationException();
    }

    /**
     * Returns the Id of the represented tag field.<br>
     * This value should uniquely identify a kind of tag data, like title.
     * {@link org.jaudiotagger.audio.generic.AbstractTag} will use the "id" to summarize multiple
     * fields.
     *
     * @return Unique identifier for the fields type. (title, artist...)
     */
    public String getId()
    {
        return FieldKey.COVER_ART.name();
    }

    /**
     * This method delivers the binary representation of the fields data in
     * order to be directly written to the file.<br>
     *
     * @return Binary data representing the current tag field.<br>
     * @throws java.io.UnsupportedEncodingException
     *          Most tag data represents text. In some cases the underlying
     *          implementation will need to convert the text data in java to
     *          a specific charset encoding. In these cases an
     *          {@link java.io.UnsupportedEncodingException} may occur.
     */
    public byte[] getRawContent() throws UnsupportedEncodingException
    {
        return getBytes();
    }

    /**
     * Determines whether the represented field contains (is made up of) binary
     * data, instead of text data.<br>
     * Software can identify fields to be displayed because they are human
     * readable if this method returns <code>false</code>.
     *
     * @return <code>true</code> if field represents binary data (not human
     *         readable).
     */
    public boolean isBinary()
    {
        return true;
    }

    /**
     * This method will set the field to represent binary data.<br>
     * <p/>
     * Some implementations may support conversions.<br>
     * As of now (Octobre 2005) there is no implementation really using this
     * method to perform useful operations.
     *
     * @param b <code>true</code>, if the field contains binary data.
     * @deprecated As for now is of no use. Implementations should use another
     *             way of setting this property.
     */
    public void isBinary(boolean b)
    {
        //Do nothing, always true
    }

    /**
     * Identifies a field to be of common use.<br>
     * <p/>
     * Some software may differ between common and not common fields. A common
     * one is for sure the title field. A web link may not be of common use for
     * tagging. However some file formats, or future development of users
     * expectations will make more fields common than now can be known.
     *
     * @return <code>true</code> if the field is of common use.
     */
    public boolean isCommon()
    {
        return true;
    }

    /**
     * Determines whether the content of the field is empty.<br>
     *
     * @return <code>true</code> if no data is stored (or empty String).
     */
    public boolean isEmpty()
    {
        return false;
    }


}