/* * Copyright 2012 Brendan McCarthy (brendan@oddsoftware.net) * * This file is part of Feedscribe. * * Feedscribe is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 * as published by the Free Software Foundation. * * Feedscribe is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Feedscribe. If not, see <http://www.gnu.org/licenses/>. */ package net.oddsoftware.android.utils; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import net.oddsoftware.android.feedscribe.Globals; public class TagParser { private File mFile; private String mReason; private byte[] mImageData = null; private String mMimeType = null; public TagParser(File f) { mFile = f; } public boolean findImage() { FileInputStream stream = null; boolean success = false; try { if(mFile != null) { stream = new FileInputStream(mFile); } if(stream != null) { success = findImage(stream); } stream.close(); } catch(IOException exc) { Globals.LOG.w("io error parsing file for image", exc); } return success; } private boolean findImage(FileInputStream stream) throws IOException { short[] fileHeader = new short[10]; int pos = 0; if( ! readAll(stream, fileHeader) ) { mReason = "unable to read header"; return false; } if( fileHeader[0] != 'I' || fileHeader[1] != 'D' || fileHeader[2] != '3' || fileHeader[3] != 0x03 ) { mReason = "no header match"; return false; } int flags = fileHeader[5] << 8; if( (flags & 0x80) != 0) { mReason = "unsyncronisation"; return false; } boolean extendedHeader = false; if( (flags & 0x40) != 0) { extendedHeader = true; } if( (flags & 0x1F) != 0) { mReason = "unknown flags"; return false; } int tagSize = 0; for(int i = 6; i < 10; ++i) { short b = fileHeader[i]; if( (b & 0x80) != 0) { mReason = "invalid length byte"; return false; } tagSize = tagSize << 7; tagSize |= b; } if( tagSize > 1024 * 1024) { mReason = "tag size too large " + tagSize; return false; } if(extendedHeader) { short[] extendedHeaderLength = new short[4]; if(!readAll(stream, extendedHeaderLength)) { mReason = "unable to read extended header length"; return false; } int len = (extendedHeaderLength[0] << 24) | (extendedHeaderLength[1] << 16) | (extendedHeaderLength[2] << 8) | extendedHeaderLength[3]; stream.skip(len); pos += 4 + len; } while( pos < tagSize ) { short[] frameHeader = new short[10]; if(! readAll(stream, frameHeader)) { mReason = "unable to read frame at " + pos; return false; } pos += 10; int frameSize = ( frameHeader[4] << 24 ) | ( frameHeader[5] << 16 ) | ( frameHeader[6] << 8 ) | ( frameHeader[7] << 0 ); if ( frameHeader[0] == 'A' && frameHeader[1] == 'P' && frameHeader[2] == 'I' && frameHeader[3] == 'C' ) { int textEncoding = stream.read(); String mimeType = readString(stream, 0); int pictureType = stream.read(); String description = readString(stream, textEncoding); if( textEncoding == -1 || pictureType == -1 || mimeType == null || description == null) { mReason = "unable to read picture frame"; return false; } int framePos = 3; // textEncoding + pictureType + null from mime type framePos += mimeType.length(); framePos += description.length() + 1; if(textEncoding == 1) // unicode? then add more { framePos += description.length() + 1; } int remaining = frameSize - framePos; if( (remaining < 1024 * 1024) && (remaining > 0) ) { mImageData = new byte[remaining]; if( readAll(stream, mImageData) ) { mMimeType = mimeType; return true; } else { mReason = "unable to read picture data"; return false; } } pos += frameSize; } else { stream.skip(frameSize); pos += frameSize; } } return false; } private boolean readAll(FileInputStream stream, short[] bytes) throws IOException { for(int i = 0; i < bytes.length; ++i) { int b = stream.read(); if(b == -1) { return false; } else { bytes[i] = (short) b; } } return true; } private boolean readAll(FileInputStream stream, byte[] buffer) throws IOException { int pos = 0; while(pos < buffer.length) { int retval = stream.read(buffer, pos, buffer.length - pos); if( retval == -1) { return false; } else { pos += retval; } } return true; } private String readString(FileInputStream stream, int encoding) throws IOException { StringBuilder builder = new StringBuilder(); if(encoding == 0) { while(true) { int b = stream.read(); if( b == -1) { return null; } else if( b == 0 ) { return builder.toString(); } else { builder.append('.'); } } } else if(encoding == 1) { while(true) { int b1 = stream.read(); int b2 = stream.read(); if( b1 == -1 || b2 == -1) { return null; } else if( b1 == 0 && b2 == 0) { return builder.toString(); } else { builder.append('.'); } } } return null; } public String getImageMimeType() { return mMimeType; } public byte[] getImageData() { return mImageData; } public String getReason() { return mReason; } }