Coverage Report - org.jaudiotagger.audio.flac.metadatablock.MetadataBlockDataPicture
 
Classes in this File Line Coverage Branch Coverage Complexity
MetadataBlockDataPicture
85%
61/72
67%
4/6
1.435
 
 1  
 package org.jaudiotagger.audio.flac.metadatablock;
 2  
 
 3  
 import org.jaudiotagger.audio.generic.Utils;
 4  
 import org.jaudiotagger.tag.InvalidFrameException;
 5  
 import org.jaudiotagger.tag.TagField;
 6  
 import org.jaudiotagger.tag.TagFieldKey;
 7  
 import org.jaudiotagger.tag.id3.valuepair.TextEncoding;
 8  
 import org.jaudiotagger.tag.reference.PictureTypes;
 9  
 
 10  
 import java.io.ByteArrayOutputStream;
 11  
 import java.io.IOException;
 12  
 import java.io.RandomAccessFile;
 13  
 import java.io.UnsupportedEncodingException;
 14  
 import java.nio.ByteBuffer;
 15  
 import java.util.logging.Logger;
 16  
 
 17  
 
 18  
 /**
 19  
  * Picture Block
 20  
  * <p/>
 21  
  * <p/>
 22  
  * <p>This block is for storing pictures associated with the file, most commonly cover art from CDs.
 23  
  * There may be more than one PICTURE block in a file. The picture format is similar to the APIC frame in ID3v2.
 24  
  * The PICTURE block has a type, MIME type, and UTF-8 description like ID3v2, and supports external linking via URL
 25  
  * (though this is discouraged). The differences are that there is no uniqueness constraint on the description field,
 26  
  * and the MIME type is mandatory. The FLAC PICTURE block also includes the resolution, color depth, and palette size
 27  
  * so that the client can search for a suitable picture without having to scan them all
 28  
  * <p/>
 29  
  * Format:
 30  
  * <Size in bits> Info
 31  
  * <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  
  * <32>         The length of the MIME type string in bytes.
 33  
  * <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.
 34  
  * <32>         The length of the description string in bytes.
 35  
  * <n*8>         The description of the picture, in UTF-8.
 36  
  * <32>         The width of the picture in pixels.
 37  
  * <32>         The height of the picture in pixels.
 38  
  * <32>         The color depth of the picture in bits-per-pixel.
 39  
  * <32>         For indexed-color pictures (e.g. GIF), the number of colors used, or 0 for non-indexed pictures.
 40  
  * <32>         The length of the picture data in bytes.
 41  
  * <n*8>         The binary picture data.
 42  
  */
 43  
 public class MetadataBlockDataPicture implements MetadataBlockData, TagField
 44  
 {
 45  
     public static final String IMAGE_IS_URL = "-->";
 46  
 
 47  
     private int pictureType;
 48  
     private String mimeType;
 49  
     private String description;
 50  
     private int width;
 51  
     private int height;
 52  
     private int colourDepth;
 53  
     private int indexedColouredCount;
 54  
     private byte[] imageData;
 55  
 
 56  
     // Logger Object
 57  4
     public static Logger logger = Logger.getLogger("org.jaudiotagger.audio.flac.MetadataBlockDataPicture");
 58  
 
 59  
     /**
 60  
      * Construct picture block by reading from file
 61  
      */
 62  
     //TODO check for buffer underflows see http://research.eeye.com/html/advisories/published/AD20071115.html
 63  
     public MetadataBlockDataPicture(MetadataBlockHeader header, RandomAccessFile raf) throws IOException, InvalidFrameException
 64  17
     {
 65  17
         ByteBuffer rawdata = ByteBuffer.allocate(header.getDataLength());
 66  17
         int bytesRead = raf.getChannel().read(rawdata);
 67  17
         if (bytesRead < header.getDataLength())
 68  
         {
 69  0
             throw new IOException("Unable to read required number of databytes read:" + bytesRead + ":required:" + header.getDataLength());
 70  
         }
 71  17
         rawdata.rewind();
 72  
 
 73  
         //Picture Type
 74  17
         pictureType = rawdata.getInt();
 75  17
         if (pictureType >= PictureTypes.getInstanceOf().getSize())
 76  
         {
 77  0
             throw new InvalidFrameException("PictureType was:" + pictureType + "but the maximum allowed is " + (PictureTypes.getInstanceOf().getSize() - 1));
 78  
         }
 79  
 
 80  
         //MimeType
 81  17
         int mimeTypeSize = rawdata.getInt();
 82  17
         mimeType = getString(rawdata, mimeTypeSize, "ISO-8859-1");
 83  
 
 84  
         //Description
 85  17
         int descriptionSize = rawdata.getInt();
 86  17
         description = getString(rawdata, descriptionSize, "UTF-8");
 87  
 
 88  
         //Image width
 89  17
         width = rawdata.getInt();
 90  
 
 91  
         //Image height
 92  17
         height = rawdata.getInt();
 93  
 
 94  
         //Colour Depth
 95  17
         colourDepth = rawdata.getInt();
 96  
 
 97  
         //Indexed Colour Count
 98  17
         indexedColouredCount = rawdata.getInt();
 99  
 
 100  
         //ImageData
 101  17
         int rawdataSize = rawdata.getInt();
 102  17
         imageData = new byte[rawdataSize];
 103  17
         rawdata.get(imageData);
 104  
 
 105  17
         logger.info("Read image:" + this.toString());
 106  
 
 107  17
     }
 108  
 
 109  
     /**
 110  
      * Construct new MetadataPicture block
 111  
      */
 112  
     public MetadataBlockDataPicture(byte[] imageData, int pictureType, String mimeType, String description, int width, int height, int colourDepth, int indexedColouredCount)
 113  5
     {
 114  
         //Picture Type
 115  5
         this.pictureType = pictureType;
 116  
 
 117  
         //MimeType
 118  5
         this.mimeType = mimeType;
 119  
 
 120  
         //Description
 121  5
         this.description = description;
 122  
 
 123  5
         this.width = width;
 124  
 
 125  5
         this.height = height;
 126  
 
 127  5
         this.colourDepth = colourDepth;
 128  
 
 129  5
         this.indexedColouredCount = indexedColouredCount;
 130  
         //ImageData
 131  5
         this.imageData = imageData;
 132  5
     }
 133  
 
 134  
     private String getString(ByteBuffer rawdata, int length, String charset) throws IOException
 135  
     {
 136  34
         byte[] tempbuffer = new byte[length];
 137  34
         rawdata.get(tempbuffer);
 138  34
         return new String(tempbuffer, charset);
 139  
     }
 140  
 
 141  
     public byte[] getBytes()
 142  
     {
 143  
         try
 144  
         {
 145  66
             ByteArrayOutputStream baos = new ByteArrayOutputStream();
 146  66
             baos.write(Utils.getSizeBEInt32(pictureType));
 147  66
             baos.write(Utils.getSizeBEInt32(mimeType.length()));
 148  66
             baos.write(mimeType.getBytes("ISO-8859-1"));
 149  66
             baos.write(Utils.getSizeBEInt32(description.length()));
 150  66
             baos.write(description.getBytes("UTF-8"));
 151  66
             baos.write(Utils.getSizeBEInt32(width));
 152  66
             baos.write(Utils.getSizeBEInt32(height));
 153  66
             baos.write(Utils.getSizeBEInt32(colourDepth));
 154  66
             baos.write(Utils.getSizeBEInt32(indexedColouredCount));
 155  66
             baos.write(Utils.getSizeBEInt32(imageData.length));
 156  66
             baos.write(imageData);
 157  66
             return baos.toByteArray();
 158  
 
 159  
         }
 160  0
         catch (IOException ioe)
 161  
         {
 162  0
             throw new RuntimeException(ioe.getMessage());
 163  
         }
 164  
     }
 165  
 
 166  
     public int getLength()
 167  
     {
 168  22
         return getBytes().length;
 169  
     }
 170  
 
 171  
     public int getPictureType()
 172  
     {
 173  17
         return pictureType;
 174  
     }
 175  
 
 176  
     public String getMimeType()
 177  
     {
 178  48
         return mimeType;
 179  
     }
 180  
 
 181  
     public String getDescription()
 182  
     {
 183  15
         return description;
 184  
     }
 185  
 
 186  
     public int getWidth()
 187  
     {
 188  3
         return width;
 189  
     }
 190  
 
 191  
     public int getHeight()
 192  
     {
 193  3
         return height;
 194  
     }
 195  
 
 196  
     public int getColourDepth()
 197  
     {
 198  3
         return colourDepth;
 199  
     }
 200  
 
 201  
     public int getIndexedColourCount()
 202  
     {
 203  3
         return indexedColouredCount;
 204  
     }
 205  
 
 206  
     public byte[] getImageData()
 207  
     {
 208  31
         return imageData;
 209  
     }
 210  
 
 211  
     /**
 212  
      * @return true if imagedata  is held as a url rather than actually being imagedata
 213  
      */
 214  
     public boolean isImageUrl()
 215  
     {
 216  31
         return getMimeType().equals(IMAGE_IS_URL);
 217  
     }
 218  
 
 219  
     /**
 220  
      * @return the image url if there is otherwise return an empty String
 221  
      */
 222  
     public String getImageUrl()
 223  
     {
 224  10
         if (isImageUrl())
 225  
         {
 226  9
             return Utils.getString(getImageData(), 0, getImageData().length, TextEncoding.CHARSET_ISO_8859_1);
 227  
         }
 228  
         else
 229  
         {
 230  1
             return "";
 231  
         }
 232  
     }
 233  
 
 234  
     public String toString()
 235  
     {
 236  17
         return PictureTypes.getInstanceOf().getValueForId(pictureType) + ":" + mimeType + ":" + description + ":" + "width:" + width + ":height:" + height + ":colourdepth:" + colourDepth + ":indexedColourCount:" + indexedColouredCount + ":image size in bytes:" + imageData.length;
 237  
     }
 238  
 
 239  
     /**
 240  
      * This method copies the data of the given field to the current data.<br>
 241  
      *
 242  
      * @param field The field containing the data to be taken.
 243  
      */
 244  
     public void copyContent(TagField field)
 245  
     {
 246  0
         throw new UnsupportedOperationException();
 247  
     }
 248  
 
 249  
     /**
 250  
      * Returns the Id of the represented tag field.<br>
 251  
      * This value should uniquely identify a kind of tag data, like title.
 252  
      * {@link org.jaudiotagger.audio.generic.AbstractTag} will use the &quot;id&quot; to summarize multiple
 253  
      * fields.
 254  
      *
 255  
      * @return Unique identifier for the fields type. (title, artist...)
 256  
      */
 257  
     public String getId()
 258  
     {
 259  0
         return TagFieldKey.COVER_ART.name();
 260  
     }
 261  
 
 262  
     /**
 263  
      * This method delivers the binary representation of the fields data in
 264  
      * order to be directly written to the file.<br>
 265  
      *
 266  
      * @return Binary data representing the current tag field.<br>
 267  
      * @throws java.io.UnsupportedEncodingException
 268  
      *          Most tag data represents text. In some cases the underlying
 269  
      *          implementation will need to convert the text data in java to
 270  
      *          a specific charset encoding. In these cases an
 271  
      *          {@link java.io.UnsupportedEncodingException} may occur.
 272  
      */
 273  
     public byte[] getRawContent() throws UnsupportedEncodingException
 274  
     {
 275  0
         return getBytes();
 276  
     }
 277  
 
 278  
     /**
 279  
      * Determines whether the represented field contains (is made up of) binary
 280  
      * data, instead of text data.<br>
 281  
      * Software can identify fields to be displayed because they are human
 282  
      * readable if this method returns <code>false</code>.
 283  
      *
 284  
      * @return <code>true</code> if field represents binary data (not human
 285  
      *         readable).
 286  
      */
 287  
     public boolean isBinary()
 288  
     {
 289  0
         return true;
 290  
     }
 291  
 
 292  
     /**
 293  
      * This method will set the field to represent binary data.<br>
 294  
      * <p/>
 295  
      * Some implementations may support conversions.<br>
 296  
      * As of now (Octobre 2005) there is no implementation really using this
 297  
      * method to perform useful operations.
 298  
      *
 299  
      * @param b <code>true</code>, if the field contains binary data.
 300  
      * @deprecated As for now is of no use. Implementations should use another
 301  
      *             way of setting this property.
 302  
      */
 303  
     public void isBinary(boolean b)
 304  
     {
 305  
         //Do nothing, always true
 306  0
     }
 307  
 
 308  
     /**
 309  
      * Identifies a field to be of common use.<br>
 310  
      * <p/>
 311  
      * Some software may differ between common and not common fields. A common
 312  
      * one is for sure the title field. A web link may not be of common use for
 313  
      * tagging. However some file formats, or future development of users
 314  
      * expectations will make more fields common than now can be known.
 315  
      *
 316  
      * @return <code>true</code> if the field is of common use.
 317  
      */
 318  
     public boolean isCommon()
 319  
     {
 320  0
         return true;
 321  
     }
 322  
 
 323  
     /**
 324  
      * Determines whether the content of the field is empty.<br>
 325  
      *
 326  
      * @return <code>true</code> if no data is stored (or empty String).
 327  
      */
 328  
     public boolean isEmpty()
 329  
     {
 330  0
         return false;
 331  
     }
 332  
 
 333  
 
 334  
 }