diff --git a/extramodules/it/eng/jhove/module/odf/JingDriver.java b/extramodules/it/eng/jhove/module/odf/JingDriver.java
new file mode 100644
index 000000000..dc3835fbb
--- /dev/null
+++ b/extramodules/it/eng/jhove/module/odf/JingDriver.java
@@ -0,0 +1,76 @@
+package it.eng.jhove.module.odf;
+
+import com.thaiopensource.util.PropertyMapBuilder;
+import com.thaiopensource.validate.SchemaReader;
+import com.thaiopensource.validate.ValidateProperty;
+import com.thaiopensource.validate.ValidationDriver;
+import com.thaiopensource.validate.rng.RngProperty;
+import com.thaiopensource.xml.sax.ErrorHandlerImpl;
+import java.io.IOException;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import java.io.OutputStream;
+
+import java.io.InputStream;
+
+
+/**
+ * JingDriver is a wrapper to use Jing as a library and not as an
+ * application without changing jing own source code.
+ *
+ * Created: Fri Oct 20 14:22:14 2006
+ *
+ * @author Gian Uberto Lauri
+ * @version $Revision$
+ */
+public class JingDriver {
+
+ /**
+ * doValidation
preform a relaxed ng validation using jing
+ * code (-i option, since we had some barfing with Oasis nrg during
+ * pre-tests)
+ *
+ * @param schema a String
relaxed nrg schema against who
+ * the file is to be validated.
+ * @param xmlFileName a String
xml file to validate
+ * @param boust a ByteArrayOutputStream
byte output stream
+ * to collect jing barfing...
+ * @return a boolean
true if the file is valid, false
+ * otherwise.
+ */
+ public boolean doValidation(InputStream schema,
+ String xmlFileName,
+ OutputStream boust) {
+ ErrorHandlerImpl eh = new ErrorHandlerImpl(System.out);
+ PropertyMapBuilder properties = new PropertyMapBuilder();
+ ValidateProperty.ERROR_HANDLER.put(properties, eh);
+ RngProperty.CHECK_ID_IDREF.add(properties);
+ SchemaReader sr = null;
+
+ // Simulate -i option, since without the program barfs on
+ // Oasis Open Document nrg.
+ properties.put(RngProperty.CHECK_ID_IDREF, null);
+
+ boolean hadError = false;
+ try {
+ ValidationDriver driver = new ValidationDriver(properties.toPropertyMap(), sr);
+ InputSource in = new InputSource(schema);
+ if (driver.loadSchema(in)) {
+
+ if (!driver.validate(ValidationDriver.uriOrFileInputSource(xmlFileName)))
+ hadError = true;
+ }
+ else
+ hadError = true;
+ }
+ catch (SAXException e) {
+ hadError = true;
+ eh.printException(e);
+ }
+ catch (IOException e) {
+ hadError = true;
+ eh.printException(e);
+ }
+ return ! hadError;
+ }
+}
diff --git a/extramodules/it/eng/jhove/module/odf/ManifestHandler.java b/extramodules/it/eng/jhove/module/odf/ManifestHandler.java
new file mode 100644
index 000000000..7d98cb817
--- /dev/null
+++ b/extramodules/it/eng/jhove/module/odf/ManifestHandler.java
@@ -0,0 +1,82 @@
+package it.eng.jhove.module.odf;
+
+import org.xml.sax.helpers.DefaultHandler;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import java.util.List;
+import it.eng.jhove.Booolean;
+
+/**
+ * Describe class ManifestHandler here.
+ *
+ *
+ * Created: Mon Oct 23 08:45:53 2006
+ *
+ * @author Gian Uberto Lauri
+ * @version $Revision$
+ */
+public class ManifestHandler extends DefaultHandler {
+
+ private Booolean isEncrypted;
+ private List entries;
+
+ /**
+ * Crea una nuova istanza di ManifestHandler
.
+ *
+ */
+ public ManifestHandler(List entries,
+ Booolean isEncrypted) {
+ this.entries = entries;
+ this.isEncrypted = isEncrypted;
+ }
+
+ /**
+ * startElement
+ *
+ * @param nameSpaceUri a String
+ * @param localName a String
+ * @param rawName a String
+ * @param attributes an Attributes
+ * @exception SAXException
+ */
+ public final void startElement(final String nameSpaceUri,
+ final String localName,
+ final String rawName,
+ final Attributes attributes)
+ throws SAXException {
+
+ if (rawName.equals(FILE_ENTRY)) {
+ String type="";
+ String path="";
+ int size=0;
+
+ for (int i = 0; i < attributes.getLength(); i++) {
+ if (attributes.getQName(i).equals(ATTR_MEDIA)) {
+ type = attributes.getValue(i);
+ }
+ if (attributes.getQName(i).equals(ATTR_PATH)) {
+ path = attributes.getValue(i);
+ }
+ if (attributes.getQName(i).equals(ATTR_SIZE)) {
+ size = Integer.parseInt(attributes.getValue(i));
+ }
+
+
+ }
+ ManifestEntry entry = new ManifestEntry(type, path, size);
+ entries.add(entry);
+ }
+ else if (rawName.equals(CRYP_ENTRT)) {
+ isEncrypted.setFlag(true);
+ }
+
+ }
+
+ private final static String FILE_ENTRY = "manifest:file-entry";
+
+ private final static String CRYP_ENTRT = "manifest:encryption-data";
+
+ private final static String ATTR_MEDIA = "manifest:media-type";
+ private final static String ATTR_PATH = "manifest:full-path";
+ private final static String ATTR_SIZE = "manifest:size";
+}
diff --git a/extramodules/it/eng/jhove/module/odf/MetaHandler.java b/extramodules/it/eng/jhove/module/odf/MetaHandler.java
new file mode 100644
index 000000000..9c77566d2
--- /dev/null
+++ b/extramodules/it/eng/jhove/module/odf/MetaHandler.java
@@ -0,0 +1,87 @@
+package it.eng.jhove.module.odf;
+
+import org.xml.sax.helpers.DefaultHandler;
+import edu.harvard.hul.ois.jhove.RepInfo;
+import org.xml.sax.SAXException;
+import org.xml.sax.Attributes;
+import java.text.SimpleDateFormat;
+import java.text.ParsePosition;
+import java.util.Date;
+
+/**
+ * Describe class MetaHandler here.
+ *
+ *
+ * Created: Mon Oct 23 13:25:46 2006
+ *
+ * @author Gian Uberto Lauri
+ * @version $Revision$
+ */
+public class MetaHandler extends DefaultHandler {
+ protected RepInfo info;
+ protected StringBuffer tagContent;
+ protected boolean isInDate=false;
+ protected boolean isInCreationDate=false;
+ protected SimpleDateFormat sdf1=new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
+ /**
+ * Cretes a new instance of MetaHandler
.
+ *
+ */
+ public MetaHandler(RepInfo info) {
+ this.info= info;
+ }
+
+ /**
+ * SAX parser callback method.
+ */
+ public void startElement (String namespaceURI, String localName,
+ String rawName, Attributes atts)
+ throws SAXException
+ {
+ tagContent = new StringBuffer ();
+ if (rawName.equals(TAG_DATE)) {
+ isInDate=true;
+ }
+ if (rawName.equals(TAG_CREATION)) {
+ isInCreationDate=true;
+ }
+
+ }
+
+ /**
+ * SAX parser callback method.
+ */
+ public void characters (char [] ch, int start, int length)
+ throws SAXException
+ {
+ tagContent.append (ch, start, length);
+
+ }
+
+ /**
+ * SAX parser callback method.
+ */
+ public void endElement (String namespaceURI, String localName,
+ String rawName)
+ throws SAXException
+ {
+ if ( isInDate ) {
+ info.setLastModified(contentToDate());
+ isInDate=false;
+ }
+ if ( isInCreationDate ) {
+ info.setCreated(contentToDate());
+ isInCreationDate=false;
+ }
+
+
+ }
+
+ private final Date contentToDate() {
+ // Open document date format is yyyy-mm-ddThh:mm:ss
+ return sdf1.parse(tagContent.toString(), new ParsePosition(0));
+
+ }
+ private final static String TAG_DATE="dc:date";
+ private final static String TAG_CREATION="meta:creation-date";
+}
diff --git a/extramodules/it/eng/jhove/module/odf/OdfModule.java b/extramodules/it/eng/jhove/module/odf/OdfModule.java
new file mode 100644
index 000000000..84d84c959
--- /dev/null
+++ b/extramodules/it/eng/jhove/module/odf/OdfModule.java
@@ -0,0 +1,775 @@
+package it.eng.jhove.module.odf;
+
+import edu.harvard.hul.ois.jhove.ErrorMessage;
+import edu.harvard.hul.ois.jhove.InfoMessage;
+import edu.harvard.hul.ois.jhove.JhoveBase;
+import edu.harvard.hul.ois.jhove.JhoveException;
+import edu.harvard.hul.ois.jhove.Module;
+import edu.harvard.hul.ois.jhove.ModuleBase;
+import edu.harvard.hul.ois.jhove.OutputHandler;
+import it.eng.jhove.Booolean;
+import it.eng.jhove.RepInfo;
+import it.eng.jhove.NullHandler;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipException;
+import java.util.zip.ZipFile;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParserFactory;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.XMLReaderFactory;
+
+
+/**
+ * Class OdfModule A module for Jhove - JSTOR/Harvard Object
+ * Validation Environment intended to recognize and validate ODF files
+ * formatted according to the definition in "Open Document Format for
+ * Office Applications (OpenDocument) v1.0"
+ *
+ *
+ * Created: Fri Oct 20 15:59:25 2006
+ *
+ * @author Gian Uberto Lauri
+ * @version $Revision$
+ */
+public class OdfModule extends ModuleBase implements Module {
+
+
+ /**
+ * Crea una nuova istanza di OdfModule
.
+ *
+ */
+ public OdfModule() {
+ super (NAME, RELEASE, DATE, FORMAT, COVERAGE, MIMETYPES, WELLFORMED,
+ VALIDITY, REPINFO, NOTE, RIGHTS, RANDOM);
+
+ }
+
+ /**
+ * init
+ *
+ * @param string a String
+ * @exception Exception
+ */
+ public final void init(final String string) throws Exception {
+
+ }
+
+ /**
+ * initParse
initializes the status of the parser.
+ *
+ */
+ public final void initParse() {
+ super.initParse();
+ }
+
+ /**
+ * parse
+ *
+ * @param inputStream an InputStream
+ * @param repInfo a RepInfo
+ * @param n an int
+ * @return an int
+ * @exception IOException
+ */
+ public final int parse(final InputStream inputStream,
+ final RepInfo repInfo,
+ final int n) throws IOException {
+ boolean alreadyNonPreservable=false;
+
+ initParse();
+ repInfo.setFormat(FORMAT[0]);
+ repInfo.setModule (this);
+
+ // First step. To work with ZIP classes you need a File, not a
+ // stream. But you have a stream and this stream could arrive
+ // from far far away... Solution, build yourself a nice
+ // temporary file. Well'use quite some within this module...
+
+ String tempdir = _je.getTempDirectory();
+ File tempFile = null;
+ if (tempdir == null) {
+ tempFile = File.createTempFile("odfTemp", ".zip");
+
+ }
+ else {
+ tempFile = File.createTempFile("odfTemp", ".zip",
+ new File(tempdir));
+
+ }
+
+
+ tempFile.deleteOnExit();
+
+ FileOutputStream fous = new FileOutputStream(tempFile);
+
+ byte b[] = new byte[1];
+
+ while (inputStream.read(b) == 1) {
+ fous.write(b);
+ }
+
+ fous.close();
+
+ // ------------ Now let's begin
+ ZipFile zipf=null;
+// Map componentFiles = new HashMap();
+
+ FileInputStream fins = new FileInputStream(tempFile);
+
+ boolean matchesP = doTheCheck((InputStream)fins);
+
+ fins.close();
+
+ if (!matchesP) {
+ repInfo.setWellFormed(false);
+ return(0);
+ }
+
+ try {
+
+ zipf = new ZipFile(tempFile);
+
+ ZipEntry mimeTypeZE = zipf.getEntry(MIMETYPE);
+
+ if (mimeTypeZE == null) {
+ repInfo.setValid(false);
+ repInfo.setWellFormed(false);
+ repInfo.setMessage(new ErrorMessage("Corrupted or invaild ODF Package: mimetype comonent is missing."));
+ zipf.close();
+ return 0;
+
+ }
+
+ ZipEntry manifestZE = zipf.getEntry(MANIFEST);
+ if (manifestZE == null) {
+ repInfo.setValid(false);
+ repInfo.setWellFormed(false);
+ repInfo.setMessage(new ErrorMessage("Corrupted or invaild ODF Package: manifest is missing."));
+ zipf.close();
+ return 0;
+
+ }
+
+ if (! setProfileMime(repInfo, zipf, mimeTypeZE)) {
+ repInfo.setValid(false);
+ repInfo.setWellFormed(true);
+ repInfo.setMessage(new ErrorMessage("Invalid ODF Package, unexpected type: " + repInfo.getMimeType()));
+ zipf.close();
+ return 0;
+
+ }
+ repInfo.setSigMatch(_name);
+
+ // Process Manifest
+ String manifestFileName = dumpPart(manifestZE, zipf);
+ JingDriver jd = new JingDriver();
+
+ ByteArrayOutputStream errorStream = new ByteArrayOutputStream(1024);
+
+ if (! jd.doValidation(getResource(SCHEMA_MANIFEST),
+ manifestFileName,
+ errorStream) ) {
+ StringBuffer buf = new StringBuffer("Invalid ODF Package, manifest failed Relaxed NG validation: " );
+ buf.append(errorStream.toString());
+ repInfo.setValid(false);
+ repInfo.setWellFormed(false);
+ repInfo.setMessage(new ErrorMessage(buf.toString()));
+ }
+
+ XMLReader parser = getXmlParser();
+ List elementList = new ArrayList();
+ Booolean isEncrypted = new Booolean(false,"");
+ ManifestHandler manifestHandler = new ManifestHandler(elementList,
+ isEncrypted);
+
+ parser.setContentHandler (manifestHandler);
+
+ /*
+ * Attempt to set schema awareness to avoid validation
+ * errors.
+ */
+ try {
+ parser.setFeature("http://xml.org/sax/features/validation",
+ false);
+ parser.setFeature("http://xml.org/sax/features/namespace-prefixes",
+ true);
+ parser.setProperty ("http://java.sun.com/xml/jaxp/" +
+ "properties/schemaLanguage",
+ "http://www.w3.org/2001/XMLSchema");
+ }
+ catch (SAXException e)
+ {}
+
+ try {
+ parser.parse (manifestFileName);
+
+ } catch (SAXException excptn) {
+ repInfo.setValid(false);
+ repInfo.setWellFormed(false);
+ repInfo.setMessage(new ErrorMessage("Mmalformed manifest : " +
+ excptn.getMessage()));
+ zipf.close();
+ return 0;
+
+ }
+ // Process components
+ JhoveBase jhoveBase;
+ try {
+
+ jhoveBase = new JhoveBase();
+ jhoveBase.init(_je.getConfigFile(),
+ _je.getSaxClass());
+
+ } catch (JhoveException excptn) {
+
+ repInfo.setValid(false);
+ repInfo.setWellFormed(false);
+ repInfo.setMessage(new ErrorMessage("Can't instance engine to analyze package parts. Cause : " +
+ excptn.getMessage()));
+ zipf.close();
+ return 0;
+ }
+
+ for (Iterator iter = elementList.iterator(); iter.hasNext();) {
+ ManifestEntry mnfe = (ManifestEntry) iter.next();
+
+ /*
+ * If the size field is set then the data is
+ * encrypted, therefore we can't process it. If the
+ * type equals application/binary (not a IANA
+ * registered one!) then the data is skipped as
+ * application specific.
+ */
+ if (mnfe.size != 0) {
+ StringBuffer tmp = new StringBuffer("Warning, part ");
+ tmp.append(mnfe.fullPath);
+ tmp.append(" is encrypted, validation skipped.");
+ repInfo.setMessage(new InfoMessage(tmp.toString()));
+ }
+ else if (mimeTypeMap.get(mnfe.mediaType) != null &&
+ ! mnfe.fullPath.equals(SKIP_ROOT)) {
+ String subDocName;
+ int idxof = mnfe.fullPath.indexOf("/");
+ if (idxof<1) {
+
+ subDocName= mnfe.fullPath.substring(0, idxof);
+
+ }
+ else {
+
+ subDocName = mnfe.fullPath;
+
+ }
+
+ RepInfo subDoc = new RepInfo(subDocName);
+ subDoc.setModule(this);
+ subDoc.setFormat("ODF");
+ subDoc.setProfile((String)mimeTypeMap.get(mnfe.mediaType));
+ subDoc.setMimeType(mnfe.mediaType);
+ subDoc.setValid(true);
+ subDoc.setWellFormed(true);
+ subDoc.setConsistent(true);
+ repInfo.putEmbeddedRepInfo(subDocName, subDoc);
+ }
+ else if ( mnfe.mediaType.equals("") ||
+ mnfe.mediaType.equals(SKIP_TYPE) ||
+ mnfe.mediaType.startsWith(SKIP_APP)) {
+
+ }
+ else {
+ ZipEntry part = zipf.getEntry(mnfe.fullPath);
+ if (part==null) {
+ // Manifest holds information of "virtual"
+ // file entries, like Configurations2 (a directory)
+ break;
+ }
+
+ // good for processing
+ if (! part.isDirectory() &&
+ part.getSize() > 0) {
+ String partFileName = dumpPart(part, zipf);
+
+ if (mnfe.mediaType.equals(XRNG_TYPE)) {
+ if (! jd.doValidation(getResource(SCHEMA_OPENDOCUMENT),
+ partFileName,
+ errorStream)) {
+
+ NullHandler nullHandler = new NullHandler();
+
+ try {
+ jhoveBase.dispatch(_app,
+ null,
+ null,
+ nullHandler,
+ null,
+ new String[] {partFileName});
+
+ String partDocName;
+ int idxofp = mnfe.fullPath.indexOf("/");
+ if (idxofp<1) {
+
+ partDocName = mnfe.fullPath;
+
+ }
+ else {
+
+ partDocName= mnfe.fullPath.substring(0, idxofp-1);
+
+ }
+
+ RepInfo current = repInfo.getEmbeddedRepInfo(partDocName);
+ if (current==null) {
+ current=repInfo;
+ }
+
+
+ for (Iterator iter2 = nullHandler.getRepInfos(); iter2.hasNext();) {
+ if ( ((RepInfo)iter2.next()).getWellFormed() != RepInfo.TRUE) {
+ repInfo.setValid(false);
+ }
+ }
+
+ if (repInfo.getValid() == RepInfo.TRUE && ! alreadyNonPreservable) {
+ alreadyNonPreservable=true;
+ repInfo.setProfile((String)mimeTypeMap.get(repInfo.getMimeType()));
+ }
+ else {
+ StringBuffer buf = new StringBuffer("Invalid ODF Package, component ");
+ buf.append(mnfe.fullPath);
+ buf.append(" failed both Relaxed NG and normal XML validation therefore" );
+ buf.append(" is not well formed.");
+ repInfo.setValid(false);
+ repInfo.setMessage(new ErrorMessage(buf.toString()));
+ }
+
+
+
+ } catch (Exception excptn) {
+ StringBuffer buf = new StringBuffer("Invalid ODF Package, component ");
+ buf.append(mnfe.fullPath);
+ buf.append(" failed both Relaxed NG and normal XML validation " );
+ buf.append(" due this error: ");
+ buf.append(excptn.getMessage());
+ repInfo.setValid(false);
+ repInfo.setMessage(new ErrorMessage(buf.toString()));
+ }
+ }
+ if (mnfe.fullPath.equals(META_FILE)) {
+ MetaHandler metaHandler = new MetaHandler(repInfo);
+
+ parser.setContentHandler (metaHandler);
+
+ try {
+ parser.parse (partFileName);
+
+ } catch (SAXException excptn) {
+ repInfo.setValid(false);
+ repInfo.setMessage(new ErrorMessage("malformed meta.xml: " +
+ excptn.getMessage()));
+ }
+
+ }
+ }
+ else if (mnfe.mediaType.startsWith(IMG_TYPE)) {
+ // For the files in the Picture directory
+ NullHandler nullHandler = new NullHandler();
+
+ try {
+ jhoveBase.dispatch(_app,
+ null,
+ null,
+ nullHandler,
+ null,
+ new String[] {partFileName});
+
+ } catch (Exception excptn) {
+ StringBuffer xxx = new StringBuffer("File ");
+ xxx.append(mnfe.fullPath);
+ xxx.append("analysis failed. Cause: ");
+ xxx.append(excptn.getMessage());
+ repInfo.setMessage(new ErrorMessage(xxx.toString()));
+ }
+
+
+ String partDocName;
+ int idxofp = mnfe.fullPath.indexOf("/");
+ if (idxofp<10) {
+
+ partDocName = mnfe.fullPath;
+
+ }
+ else {
+
+ partDocName= mnfe.fullPath.substring(0, idxofp-1);
+
+ }
+
+ RepInfo current = repInfo.getEmbeddedRepInfo(partDocName);
+ if (current==null) {
+ current=repInfo;
+ }
+
+
+ for (Iterator iter2 = nullHandler.getRepInfos(); iter2.hasNext();) {
+ current.addContainedRepInfo((RepInfo)iter2.next());
+ }
+
+ }
+
+
+
+ } // End "good for processing"
+
+ }
+
+ } // end of the loop on the manifest.
+
+
+ } catch (ZipException excptn) {
+ repInfo.setValid(false);
+ repInfo.setWellFormed(false);
+ repInfo.setMessage(new ErrorMessage("Invalid or corrupted ODF Package: " +
+ excptn.getMessage()));
+ } catch (IOException excptn) {
+ repInfo.setValid(false);
+ repInfo.setWellFormed(false);
+ repInfo.setMessage(new ErrorMessage("Error during ODF analysis: " +
+ excptn.getMessage()));
+ } catch (IllegalStateException excptn) {
+ repInfo.setValid(false);
+ repInfo.setWellFormed(false);
+ repInfo.setMessage(new ErrorMessage("Unexpected end of stream: " +
+ excptn.getMessage()));
+
+ }
+ catch (Throwable excptn) {
+ // TODO remove after debugging
+ excptn.printStackTrace();
+ }
+ finally {
+ try {
+ zipf.close();
+ } catch (Throwable excptn) {
+
+ }
+
+ }
+
+ // ------------ no other passes needed.
+ return 0;
+ }
+
+ /**
+ * parse
+ *
+ * @param randomAccessFile a RandomAccessFile
+ * @param repInfo a RepInfo
+ * @exception IOException
+ */
+ public final void parse(final RandomAccessFile randomAccessFile,
+ final RepInfo repInfo) throws IOException {
+ // Not used
+ }
+
+ /**
+ * param
+ *
+ * @param string a String
+ * @exception Exception
+ */
+ public final void param(final String string) throws Exception {
+
+ }
+
+ /**
+ * getDefaultParams
+ *
+ * @return a List
+ */
+ public final List getDefaultParams() {
+ return null;
+ }
+
+ /**
+ * checkSignatures
Checs for a valid Open Document Format file:
+ * the string "PK" at position 0, the string "mimetype" at position 30.
+ *
+ * @param file a File
+ * @param inputStream an InputStream
+ * @param repInfo a RepInfo
+ * @exception IOException
+ */
+ public final void checkSignatures(final File file,
+ final InputStream inputStream,
+ final RepInfo repInfo) throws IOException {
+
+ // If we got this far, take note that the signature is OK.
+ repInfo.setConsistent (doTheCheck(inputStream));
+
+
+ }
+
+ private final boolean doTheCheck(final InputStream inputStream)
+ throws IOException{
+ DataInputStream dis = getBufferedDataStream (inputStream, (_app != null) ?
+ _je.getBufferSize () : 0);
+ boolean rv = true;
+ for (int i = 0; i < 38; i++) {
+ int c = readUnsignedByte(dis,this);
+ rv &= ( header[i] == ' ' || header[i] == c );
+ }
+ return rv;
+ }
+
+ /**
+ * checkSignatures
Not used
+ *
+ * @param file a File
+ * @param randomAccessFile a RandomAccessFile
+ * @param repInfo a RepInfo
+ * @exception IOException
+ */
+ public final void checkSignatures(final File file,
+ final RandomAccessFile randomAccessFile,
+ final RepInfo repInfo)
+ throws IOException {
+ }
+
+ /**
+ * show
+ *
+ * @param outputHandler an OutputHandler
+ */
+ public final void show(final OutputHandler outputHandler) {
+
+ }
+
+
+ /**
+ * setVerbosity
+ *
+ * @param n an int
+ */
+ public final void setVerbosity(final int n) {
+
+ }
+
+
+ private final boolean setProfileMime(RepInfo repInfo, ZipFile zipf, ZipEntry zipnt)
+ throws IOException, ZipException, IllegalStateException {
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+
+ InputStream inst = new BufferedInputStream(zipf.getInputStream(zipnt));
+
+ byte b[] = new byte[1];
+
+ while (inst.read(b,0,1) != -1) {
+ buf.write(b);
+ }
+ inst.close();
+
+ repInfo.setMimeType(buf.toString());
+
+ repInfo.setProfile((String)mimeTypeMap.get(repInfo.getMimeType()));
+
+ return (repInfo.getProfile() != null);
+ }
+
+
+ private final String dumpPart(ZipEntry zent, ZipFile zfle)
+ throws IOException, ZipException{
+ String name = zent.getName().replace('/', '_');
+
+ File fout = File.createTempFile(name, ".odfpart");
+ String returnName = fout.getAbsolutePath();
+ fout.deleteOnExit();
+
+ OutputStream fous = new BufferedOutputStream( new FileOutputStream(fout));
+ InputStream inst = new BufferedInputStream(zfle.getInputStream(zent));
+
+ byte b[] = new byte[1];
+
+ while (inst.read(b) != -1) {
+ fous.write(b);
+ }
+ inst.close();
+ fous.close();
+ return returnName;
+ }
+
+ private final InputStream getResource(String resourceName) {
+ StringBuffer buf = new StringBuffer(RESOURCES);
+ buf.append(resourceName);
+ return getClass().getResourceAsStream(buf.toString());
+ }
+
+ private XMLReader getXmlParser()
+ throws IOException {
+ String saxClass = JhoveBase.getSaxClassFromProperties ();
+ XMLReader parser = null;
+ try {
+ if (saxClass == null) {
+ /* Use Java 1.4 methods to create default parser.
+ */
+ SAXParserFactory factory =
+ SAXParserFactory.newInstance();
+ factory.setNamespaceAware (true);
+ parser = factory.newSAXParser ().getXMLReader ();
+ }
+ else {
+ parser = XMLReaderFactory.createXMLReader (saxClass);
+ }
+ }
+ catch (ParserConfigurationException e) {
+ // If we can't get a SAX parser, we're stuck.
+ throw new IOException ("SAX parser not found: " +
+ saxClass +": "+ e.getMessage());
+ }
+ catch (SAXException excptn) {
+ throw new IOException ("SAX parser not found: " +
+ saxClass +": "+ excptn.getMessage());
+ }
+
+ return parser;
+ }
+
+
+ private static final String NAME = "ODF-engineering";
+ private static final String RELEASE = "1.0";
+ private static final int DATE[] = {2006, 9, 25};
+ private static final String FORMAT[] = {
+ "ODF",
+ "Open Document Format for Office Applications 1.0"
+ };
+ private static final String MIMETYPES[] = {"application/vnd.oasis.opendocument.text",
+ "application/vnd.oasis.opendocument.text-template",
+ "application/vnd.oasis.opendocument.graphics",
+ "application/vnd.oasis.opendocument.graphics-template",
+ "application/vnd.oasis.opendocument.presentation",
+ "application/vnd.oasis.opendocument.presentation-template",
+ "application/vnd.oasis.opendocument.spreadsheet",
+ "application/vnd.oasis.opendocument.spreadsheet-template",
+ "application/vnd.oasis.opendocument.chart",
+ "application/vnd.oasis.opendocument.chart-template",
+ "application/vnd.oasis.opendocument.image",
+ "application/vnd.oasis.opendocument.image-template",
+ "application/vnd.oasis.opendocument.formula",
+ "application/vnd.oasis.opendocument.formula-template",
+ "application/vnd.oasis.opendocument.text-master",
+ "application/vnd.oasis.opendocument.text-web"};
+
+ private static final String COVERAGE = "ODF";
+ private static final String WELLFORMED = null;
+ private static final String VALIDITY = null;
+ private static final String REPINFO = null;
+ private static final String NOTE = "Work in progress";
+ private static final String RIGHTS =
+ "Copyright 2006 Engineering Ingengeria Informatica S.p.a." +
+ "Released under the GNU Lesser General Public License." +
+ "Cryptoserver Library Copyright Engiweb Security, all rights reserved";
+ private static final boolean RANDOM = false;
+
+ private static final String PROFILES[] = {"Open Document Format Text Document",
+ "Open Document Format Text Document Template",
+ "Open Document Format Drawing",
+ "Open Document Format Drawing Template",
+ "Open Document Format Presentation Document",
+ "Open Document Format Presentation Document Template",
+ "Open Document Format Spreadsheet",
+ "Open Document Format Spreadsheet Template",
+ "Open Document Format Chart",
+ "Open Document Format Spreadsheet Chart Template",
+ "Open Document Format Image",
+ "Open Document Format Image Template",
+ "Open Document Format Mathematic Formula",
+ "Open Document Format Mathematic Formula Template",
+ "Open Document Format Global Text Document",
+ "Open Document Format HTML Text Document Template"};
+
+ private static final Map mimeTypeMap = new HashMap();
+
+ static {
+ for (int i = 0; i < MIMETYPES.length; i++) {
+ mimeTypeMap.put(MIMETYPES[i],PROFILES[i]);
+ }
+ }
+
+ // Magic number
+ private static final int header[] =new int[]{'P', 'K', ' ', ' ',
+ ' ', ' ', ' ', ' ',
+ ' ', ' ', ' ', ' ',
+ ' ', ' ', ' ', ' ',
+ ' ', ' ', ' ', ' ',
+ ' ', ' ', ' ', ' ',
+ ' ', ' ', ' ', ' ',
+ ' ', ' ', 'm', 'i',
+ 'm', 'e', 't', 'y',
+ 'p', 'e'};
+
+ // These are the name of the mandatory ZipEntries in an Open
+ // Document Format file.
+ private final static String MIMETYPE="mimetype";
+ private final static String MANIFEST="META-INF/manifest.xml";
+
+ // Schemas for the Relax NG validation
+ private final static String SCHEMA_MANIFEST = "OpenDocument-manifest-schema-v1.0-os.rng";
+ private final static String SCHEMA_OPENDOCUMENT = "OpenDocument-schema-v1.0-os.rng";
+ private final static String RESOURCES = "resources/";
+
+ // Media types that need "special processing"
+ private final static String
+ SKIP_TYPE = "application/binary"; // this is a non standard media
+ // type that has to be skipped
+
+ private final static String
+ XRNG_TYPE = "text/xml"; // all the xml file in a Open
+ // Document file are to be
+ // validated againist
+ // SCHEMA_OPENDOCUMENT
+
+ private final static String
+ SKIP_APP = "application/"; // Application specific binary
+ // stuff, i.e. substitutes for
+ // a faster OLE display or
+
+ private final static String
+ IMG_TYPE = "image/"; // Images have their own
+ // processing...
+
+ // Special contents
+ private final static String META_FILE = "meta.xml";
+ private final static String SKIP_ROOT = "/";
+
+}
+
+
+// package access structure.
+final class ManifestEntry {
+ final String mediaType;
+ final String fullPath;
+ final int size;
+
+ ManifestEntry(String mediaType, String fullPath, int size) {
+ this.mediaType = mediaType;
+ this.fullPath = fullPath;
+ this.size = size;
+ }
+
+}
diff --git a/extramodules/it/eng/jhove/module/png/PngModule.java b/extramodules/it/eng/jhove/module/png/PngModule.java
new file mode 100644
index 000000000..20f4d2eb5
--- /dev/null
+++ b/extramodules/it/eng/jhove/module/png/PngModule.java
@@ -0,0 +1,1341 @@
+package it.eng.jhove.module.png;
+
+import edu.harvard.hul.ois.jhove.Agent;
+import edu.harvard.hul.ois.jhove.AgentType;
+import edu.harvard.hul.ois.jhove.Checksummer;
+import edu.harvard.hul.ois.jhove.Document;
+import edu.harvard.hul.ois.jhove.DocumentType;
+import edu.harvard.hul.ois.jhove.ErrorMessage;
+import edu.harvard.hul.ois.jhove.ExternalSignature;
+import edu.harvard.hul.ois.jhove.Identifier;
+import edu.harvard.hul.ois.jhove.IdentifierType;
+import edu.harvard.hul.ois.jhove.InfoMessage;
+import edu.harvard.hul.ois.jhove.InternalSignature;
+import edu.harvard.hul.ois.jhove.ModuleBase;
+import edu.harvard.hul.ois.jhove.OutputHandler;
+import edu.harvard.hul.ois.jhove.Property;
+import edu.harvard.hul.ois.jhove.PropertyType;
+import edu.harvard.hul.ois.jhove.RepInfo;
+import edu.harvard.hul.ois.jhove.Signature;
+import edu.harvard.hul.ois.jhove.SignatureType;
+import edu.harvard.hul.ois.jhove.SignatureUseType;
+import it.eng.jhove.*;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.RandomAccessFile;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.zip.DataFormatException;
+import java.util.zip.Inflater;
+import java.util.zip.CRC32;
+
+/*
+ * This is a module for Jhove - JSTOR/Harvard Object Validation
+ * Environment
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ */
+
+/**
+ * Class PngModule
A module for Jhove - JSTOR/Harvard
+ * Object Validation Environment intended to recognize and validate
+ * PNG files formatted according to the W3C Functional
+ * specification. ISO/IEC 15948:2003 (E)
+ *
+ * This implementation lacks the control of IDATA content against the
+ * palette size
+ *
+ * For some reason, it needs java.util.zip.CRC32 for the CRC
+ * computation instead of using Jhove own.
+ *
+ * Created: Mon Sep 25 12:07:29 2006
+ *
+ * @author Gian Uberto Lauri
+ * @version $Revision$
+ */
+
+// TODO write the IDATA decompression algorithm to validate the
+// contents against the palette size.
+// This requires placing the IDATA data on a separate stream in
+// order to rebuild the compressed stream. A temporary file is
+// advisable since the data size could grow to invade the core of even
+// the stronger application servers.
+public class PngModule extends ModuleBase {
+
+ public static final boolean PNG_ENDIANITY=true;
+ /**
+ * Crea una nuova istanza di PngModule
.
+ *
+ */
+ public PngModule() {
+ super (NAME, RELEASE, DATE, FORMAT, COVERAGE, MIMETYPE, WELLFORMED,
+ VALIDITY, REPINFO, NOTE, RIGHTS, RANDOM);
+
+ keywordList = new HashMap();
+
+ keywordList.put("Title",
+ new Booolean(false,"Title")); // Short (one line) title or caption for image
+ keywordList.put("Author",
+ new Booolean(false,"Author")); // Name of image's creator
+ keywordList.put("Description",
+ new Booolean(false,"Description")); // Description of image (possibly long)
+ keywordList.put("Copyright",
+ new Booolean(false,"Copyright")); // Copyright notice
+ keywordList.put(CREATION_TIME_KEYWORD,
+ new Booolean(false,CREATION_TIME_KEYWORD)); // Time of original image creation
+ keywordList.put("Software",
+ new Booolean(false,"Software")); // Software used to create the image
+ keywordList.put("Disclaimer",
+ new Booolean(false,"Disclaimer")); // Legal disclaimer
+ keywordList.put("Warning",
+ new Booolean(false,"Warning")); // Warning of nature of content
+ keywordList.put("Source",
+ new Booolean(false,"Source")); // Device used to create the image
+ keywordList.put("Comment",
+ new Booolean(false,"Comment")); // Miscellaneous comment
+ }
+ // Implementation of edu.harvard.hul.ois.jhove.Module
+
+ /**
+ * init
+ *
+ * @param string a String
+ * @exception Exception
+ */
+ public final void init(final String string) throws Exception {
+
+ }
+
+ /**
+ * initParse
initializes the status of the parser.
+ *
+ */
+ public final void initParse() {
+ super.initParse();
+ expectingIHDR = RepInfo.TRUE;
+ expectingPLTE = RepInfo.UNDETERMINED;
+ expectingIDAT = RepInfo.TRUE;
+ expectingIEND = RepInfo.TRUE;
+ expecting_cHRM = RepInfo.UNDETERMINED;
+ expecting_gAMA = RepInfo.UNDETERMINED;
+ expecting_iCCP = RepInfo.UNDETERMINED;
+ expecting_sBIT = RepInfo.UNDETERMINED;
+ expecting_sRGB = RepInfo.UNDETERMINED;
+ expecting_tEXt = RepInfo.UNDETERMINED;
+ expecting_zTXt = RepInfo.UNDETERMINED;
+ expecting_iTXt = RepInfo.UNDETERMINED;
+ expecting_bKGD = RepInfo.UNDETERMINED;
+ expecting_hIST = RepInfo.UNDETERMINED;
+ expecting_pHYs = RepInfo.UNDETERMINED;
+ expecting_sPLT = RepInfo.UNDETERMINED;
+ expecting_tIME = RepInfo.UNDETERMINED;
+ expecting_tRNS = RepInfo.UNDETERMINED;
+
+ paletteSize = 0;
+ maxPaletteSize = 0;
+ shortPalette = false;
+ colorDepth = 0;
+
+ for (Iterator i = keywordList.values().iterator(); i.hasNext(); ) {
+ ((Booolean) i.next()).setFlag(false);
+ }
+
+ }
+
+ /**
+ * parse
Parse the content of a stream PNG image and
+ * store the results in RepInfo
+ *
+ * @param inputStream an InputStream
An InputStream,
+ * positioned at its beginning, which is generated from the object
+ * to be parsed. If multiple calls to parse
are made
+ * on the basis of a nonzero value being returned, a new
+ * InputStream must be provided each time.
+ * @param repInfo a RepInfo
A fresh (on the first
+ * call) RepInfo object which will be modified to reflect the
+ * results of the parsing If multiple calls to parse are made on
+ * the basis of a nonzero value being returned, the same RepInfo
+ * object should be passed with each call.
+ * @param n an int
Must be 0 in first call to
+ * parse. If parse returns a nonzero
+ * value, it must be called again with parseIndex equal to that
+ * return value.
+ * @return an int
+ * @exception IOException
+ */
+ public final int parse(final InputStream inputStream,
+ final RepInfo repInfo,
+ final int n) throws IOException {
+
+ StringBuffer sigName = new StringBuffer();
+
+ initParse();
+
+ // I have to pass it to each method, WHY SHOULD I USE a class
+ // instance ???
+ DataInputStream dstream = getBufferedDataStream (inputStream,
+ _app != null ?
+ _je.getBufferSize () : 0);
+ Agent agent = new Agent ("Harvard University Library",
+ AgentType.EDUCATIONAL);
+ agent.setAddress ("Engineering Ingegneria Informatica S.p.a., " +
+ "Direzione Supporto e Servizi Tecnologici, " +
+ "Corso Stati Uniti 23/A, 25100 Padova.");
+ agent.setTelephone ("+39 (49) 8283-411");
+ agent.setEmail("saint@eng.it");
+ _vendor = agent;
+
+ Document doc = new Document ("PNG (Portable Network Graphics): a file format (pronounced \"ping\"), for a lossless, portable, compressed individual computer graphics image transmitted across the Internet. Indexed-colour, greyscale, and truecolour images are supported, with optional transparency",
+ DocumentType.REPORT);
+ agent = new Agent ("W3 Consortium",
+ AgentType.STANDARD);
+ agent.setAddress ("E.M.E.A.: ERCIM, 2004 route des Lucioles, BP 93, 06902 Sophia-Antipolis Cedex, France\nJapan & Korea: Keio University, 5322 Endo, Fujisawa, Kanagawa 252-8520 Japan\nAll other countries: MIT, 32 Vassar Street, Room 32-G515, Cambridge, MA 02139 USA");
+ agent.setTelephone ("E.M.E.A. : +33.4.92.38.75.90\nJapan & Korea: +81.466.49.1170\nAll other countries: +1.617.253.2613");
+ agent.setWeb ("http://www.w3.org/");
+ doc.setAuthor (agent);
+ doc.setDate ("2003-11-10");
+ doc.setIdentifier (new Identifier ("http://www.w3.org/Graphics/GIF/spec-gif87.txt",
+ IdentifierType.URL));
+ _specification.add (doc);
+
+ Signature sig = new InternalSignature ("PNG", SignatureType.MAGIC,
+ SignatureUseType.MANDATORY, 0);
+ _signature.add (sig);
+
+ sig = new ExternalSignature (".png", SignatureType.EXTENSION,
+ SignatureUseType.OPTIONAL);
+ _signature.add (sig);
+
+ _bigEndian = false;
+ // end of parsing prologue.
+ if (! checkSignBytes(dstream,SIGNATURE)) {
+ repInfo.setMessage(new ErrorMessage ("Bad PNG Header", 0));
+ repInfo.setWellFormed (RepInfo.FALSE);
+ return 0;
+ }
+ repInfo.setFormat("PNG");
+
+ // If we got this far, take note that the signature is OK.
+ repInfo.setSigMatch(_name);
+ repInfo.setModule(this);
+ // First chunk MUST be IHDR
+ int declChunkLen = (int)(readUnsignedInt(dstream, PNG_ENDIANITY, this)
+ &0x7FFFFFFF);
+ chcks.reset();
+
+ int chunkSig = (int)(readUnsignedInt(dstream, PNG_ENDIANITY, this)&0x7FFFFFFF);
+ chcks.update(int2byteArray(chunkSig));
+
+ if (chunkSig != IHDR_HEAD_SIG ) {
+ repInfo.setWellFormed(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("IHDR header not found where expected." ));
+ return 0;
+ }
+ else {
+ checkIHDR(dstream,repInfo,declChunkLen);
+
+ if (somethingWrongP(repInfo))
+ return 0;
+
+ // not sure it's useful...
+ expectingIHDR = RepInfo.FALSE;
+ }
+
+ // The IHDR is where it should be and it's fine, now let's
+ // handle the other chunks.
+
+
+
+ while (expectingIEND == RepInfo.TRUE) {
+ declChunkLen = (int)(readUnsignedInt(dstream, PNG_ENDIANITY, this)
+ &0x7FFFFFFF);
+ // Each chunk has its checsum;
+ chcks.reset();
+
+ chunkSig = (int)(readUnsignedInt(dstream, PNG_ENDIANITY, this)&0x7FFFFFFF);
+ chcks.update(int2byteArray(chunkSig));
+
+ switch (chunkSig) {
+
+ case IHDR_HEAD_SIG:
+ repInfo.setWellFormed(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("Duplicated IHDR chunk." ));
+ break;
+
+ case PLTE_HEAD_SIG:
+ if (expectingPLTE == RepInfo.FALSE) {
+ repInfo.setWellFormed(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("Unexpected or duplicated PLTE chunk." ));
+
+ break;
+ }
+ checkPLTE(dstream,repInfo,declChunkLen);
+ expectingPLTE = RepInfo.FALSE;
+
+ break;
+
+ case IDAT_HEAD_SIG:
+ if (expectingPLTE == RepInfo.TRUE) {
+ repInfo.setWellFormed(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("Expected PLTE chunk not found." ));
+
+ break;
+ }
+
+ checkChunk(dstream, repInfo, declChunkLen,"IDAT");
+ expectingIDAT = RepInfo.FALSE;
+ break;
+
+ case IEND_HEAD_SIG:
+ if (expectingPLTE == RepInfo.TRUE) {
+ repInfo.setWellFormed(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("Expected PLTE chunk not found." ));
+
+ break;
+ }
+ if (expectingIDAT == RepInfo.TRUE) {
+ repInfo.setWellFormed(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("No IDAT chunk found." ));
+
+ break;
+ }
+ checkChunk(dstream, repInfo, declChunkLen,"IEND");
+ expectingIEND = RepInfo.FALSE;
+ break;
+
+ case cHRM_HEAD_SIG:
+ if (expectingPLTE == RepInfo.FALSE) {
+ repInfo.setWellFormed(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("cHRM chunk found after PLTE one." ));
+
+ break;
+ }
+ if (expectingIDAT == RepInfo.FALSE) {
+ repInfo.setWellFormed(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("cHRM chunk found after IDAT ones." ));
+
+
+ break;
+ }
+ if (expecting_cHRM == RepInfo.FALSE) {
+ repInfo.setWellFormed(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("Extra cHRM chunk found." ));
+
+
+ break;
+ }
+
+ checkChunk(dstream, repInfo, declChunkLen,"cHRM");
+ expecting_cHRM = RepInfo.FALSE;
+
+ break;
+
+ case gAMA_HEAD_SIG:
+ if (expectingPLTE == RepInfo.FALSE) {
+ repInfo.setWellFormed(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("gAMA chunk found after PLTE one." ));
+
+ break;
+ }
+ if (expectingIDAT == RepInfo.FALSE) {
+ repInfo.setWellFormed(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("gAMA chunk found after IDAT ones." ));
+
+ break;
+ }
+ if (expecting_gAMA == RepInfo.FALSE) {
+ repInfo.setWellFormed(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("Extra gAMA chunk found." ));
+
+ break;
+ }
+
+ checkChunk(dstream, repInfo, declChunkLen,"gAMA");
+ expecting_gAMA = RepInfo.FALSE;
+
+ break;
+
+ case iCCP_HEAD_SIG:
+
+ if (expectingPLTE == RepInfo.FALSE) {
+ repInfo.setWellFormed(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("iCCP chunk found after PLTE one." ));
+
+ break;
+ }
+ if (expectingIDAT == RepInfo.FALSE) {
+ repInfo.setWellFormed(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("iCCP chunk found after IDAT ones." ));
+
+ break;
+ }
+ if (expecting_iCCP == RepInfo.FALSE) {
+ repInfo.setWellFormed(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("Extra iCCP chunk found." ));
+
+ break;
+ }
+ if (expecting_sRGB == RepInfo.FALSE) {
+ repInfo.setWellFormed(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("iCCP chunk with sRGB chunk found." ));
+
+ break;
+ }
+
+ checkChunk(dstream, repInfo, declChunkLen,"iCCP");
+ expecting_iCCP = RepInfo.FALSE;
+ expecting_sRGB = RepInfo.FALSE;
+ break;
+
+ case sBIT_HEAD_SIG:
+ if (expectingPLTE == RepInfo.FALSE) {
+ repInfo.setWellFormed(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("sBIT chunk found after PLTE one." ));
+
+ break;
+ }
+ if (expectingIDAT == RepInfo.FALSE) {
+ repInfo.setWellFormed(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("sBIT chunk found after IDAT ones." ));
+
+ break;
+ }
+ if (expecting_sBIT == RepInfo.FALSE) {
+ repInfo.setWellFormed(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("Extra sBIT chunk found." ));
+
+ break;
+ }
+
+ checkChunk(dstream, repInfo, declChunkLen,"sBIT");
+ expecting_sBIT = RepInfo.FALSE;
+
+
+ break;
+
+ case sRGB_HEAD_SIG:
+ if (expectingPLTE == RepInfo.FALSE) {
+ repInfo.setWellFormed(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("sRGB chunk found after PLTE one." ));
+
+ break;
+ }
+ if (expectingIDAT == RepInfo.FALSE) {
+ repInfo.setWellFormed(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("sRGB chunk found after IDAT ones." ));
+
+ break;
+ }
+ if (expecting_sRGB == RepInfo.FALSE) {
+ repInfo.setWellFormed(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("Extra sRGB chunk found." ));
+
+ break;
+ }
+ if (expecting_iCCP == RepInfo.FALSE) {
+ repInfo.setWellFormed(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("iCPP chunk after sRGB chunk found." ));
+
+ break;
+ }
+
+ checkChunk(dstream, repInfo, declChunkLen,"sRGB");
+ expecting_sRGB = RepInfo.FALSE;
+ expecting_iCCP = RepInfo.FALSE;
+
+ break;
+
+ case tEXt_HEAD_SIG:
+
+ checkChunk(dstream, repInfo, declChunkLen,"tEXT");
+ break;
+
+ case zTXt_HEAD_SIG:
+
+ checkChunk(dstream, repInfo, declChunkLen,"zEXT");
+ break;
+
+ case iTXt_HEAD_SIG:
+
+ checkChunk(dstream, repInfo, declChunkLen,"iEXT");
+ break;
+
+ case bKGD_HEAD_SIG:
+
+ if (expectingPLTE == RepInfo.TRUE) {
+ repInfo.setWellFormed(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("gAMA chunk found before PLTE one." ));
+
+
+ break;
+ }
+ if (expectingIDAT == RepInfo.FALSE) {
+ repInfo.setWellFormed(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("gAMA chunk found after IDAT ones." ));
+
+ break;
+ }
+ if (expecting_gAMA == RepInfo.FALSE) {
+ repInfo.setWellFormed(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("Extra gAMA chunk found." ));
+
+ break;
+ }
+
+ checkChunk(dstream, repInfo, declChunkLen,"bKGRD");
+ expecting_gAMA = RepInfo.FALSE;
+ break;
+
+ case hIST_HEAD_SIG:
+
+ if (expectingPLTE == RepInfo.TRUE) {
+ repInfo.setWellFormed(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("hIST chunk found before PLTE one." ));
+
+ break;
+ }
+ if (expectingIDAT == RepInfo.FALSE) {
+ repInfo.setWellFormed(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("hIST chunk found after IDAT ones." ));
+
+ break;
+ }
+ if (expecting_hIST == RepInfo.FALSE) {
+ repInfo.setWellFormed(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("Extra hIST chunk found." ));
+
+ break;
+ }
+
+ checkChunk(dstream, repInfo, declChunkLen,"hIST");
+ expecting_hIST = RepInfo.FALSE;
+ break;
+
+ case tRNS_HEAD_SIG:
+
+ if (expectingPLTE == RepInfo.TRUE) {
+ repInfo.setWellFormed(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("tRNS chunk found before PLTE one." ));
+
+ break;
+ }
+ if (expectingIDAT == RepInfo.FALSE) {
+ repInfo.setWellFormed(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("tRNS chunk found after IDAT ones." ));
+
+ break;
+ }
+ if (expecting_tRNS == RepInfo.FALSE) {
+ repInfo.setWellFormed(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("Extra tRNS chunk found." ));
+
+ break;
+ }
+
+ checkChunk(dstream, repInfo, declChunkLen,"tRNS");
+ expecting_tRNS = RepInfo.FALSE;
+ break;
+
+ case pHYs_HEAD_SIG:
+
+ if (expectingIDAT == RepInfo.FALSE) {
+ repInfo.setWellFormed(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("pHYs chunk found after IDAT ones." ));
+
+ break;
+ }
+ if (expecting_pHYs == RepInfo.FALSE) {
+ repInfo.setWellFormed(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("Extra pHYs chunk found." ));
+
+ break;
+ }
+
+ checkChunk(dstream, repInfo, declChunkLen,"pHYs");
+ expecting_pHYs = RepInfo.FALSE;
+ break;
+
+ case sPLT_HEAD_SIG:
+
+ if (expectingIDAT == RepInfo.FALSE) {
+ repInfo.setWellFormed(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("sPLT chunk found after IDAT ones." ));
+
+ break;
+ }
+ checkChunk(dstream, repInfo, declChunkLen,"sPLT");
+
+ break;
+
+ case tIME_HEAD_SIG:
+ if (expecting_tIME == RepInfo.FALSE) {
+ repInfo.setWellFormed(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("Extra tIME chunk found." ));
+
+ break;
+ }
+
+ checktIME(dstream, repInfo, declChunkLen);
+ expecting_tIME = RepInfo.FALSE;
+ break;
+
+ default:
+ // Some strong choices. Reject undefined non ancillary chunks
+ if (( chunkSig & ( PROP_ANCILLARY ) ) == 0) {
+ repInfo.setValid(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("Unknown non ancillary chunk found. Datastream not interpretable!" ));
+
+ break;
+
+ }
+
+ sigName.delete(0, sigName.length());
+
+ sigName.append((char)((chunkSig & 0x7F000000) >>> 24) );
+ sigName.append((char)((chunkSig & 0x007F0000) >>> 16) );
+ sigName.append((char)((chunkSig & 0x00007F00) >>> 8) );
+ sigName.append((char)((chunkSig & 0x0000007F)) );
+ // a private chunk, check the CRC
+ checkChunk(dstream, repInfo, declChunkLen, sigName.toString());
+ break;
+ }
+
+
+ if (somethingWrongP(repInfo))
+ return 0;
+
+ }
+
+
+ // epilogue
+ if (expectingPLTE == RepInfo.TRUE) {
+ repInfo.setWellFormed(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("PLTE chunk not found (but was expected)." ));
+
+ }
+
+
+ return 0;
+ }
+
+ /**
+ * parse
+ *
+ * @param randomAccessFile a RandomAccessFile
+ * @param repInfo a RepInfo
+ * @exception IOException
+ */
+ public final void parse(final RandomAccessFile randomAccessFile,
+ final RepInfo repInfo) throws IOException {
+ // Not implemented.
+ }
+
+ /**
+ * applyDefaultParams
+ *
+ * @exception Exception
+ */
+ public final void applyDefaultParams() throws Exception {
+
+ }
+
+ /**
+ * resetParams
+ *
+ * @exception Exception
+ */
+ public final void resetParams() throws Exception {
+
+ }
+
+ /**
+ * param
+ *
+ * @param string a String
+ * @exception Exception
+ */
+ public final void param(final String string) throws Exception {
+
+ }
+
+ /**
+ * setVerbosity
+ *
+ * @param n an int
+ */
+ public final void setVerbosity(final int n) {
+
+ }
+
+ /**
+ * getDefaultParams
+ *
+ * @return a List
+ */
+ public final List getDefaultParams() {
+ return null;
+ }
+
+ /**
+ * checkSignatures
+ *
+ * @param file a File
+ * @param inputStream an InputStream
+ * @param repInfo a RepInfo
+ * @exception IOException
+ */
+ public final void checkSignatures(final File file,
+ final InputStream inputStream,
+ final RepInfo repInfo) throws IOException {
+ DataInputStream dis = getBufferedDataStream (inputStream, _app != null ?
+ _je.getBufferSize () : 0);
+ if (! checkSignBytes(dis,SIGNATURE)) {
+ repInfo.setConsistent (false);
+ return;
+ }
+ }
+
+ /**
+ * checkSignatures
Not used
+ *
+ * @param file a File
+ * @param stream a InputStream
+ * @param repInfo a RepInfo
+ * @exception IOException
+ */
+ public final void checkSignatures(final File file,
+ final RandomAccessFile stream,
+ final RepInfo repInfo)
+ throws IOException {
+ // Not used.
+ }
+
+ /**
+ * show
+ *
+ * @param outputHandler an OutputHandler
+ */
+ public final void show(final OutputHandler outputHandler) {
+
+ }
+
+
+ private final boolean checkSignBytes(final DataInputStream inputStream,
+ final int sigBytes[])
+ throws IOException {
+ int max = sigBytes.length;
+ int c;
+ boolean rv = true;
+
+ for (int i = 0; i < max; i++) {
+ c = readUnsignedByte(inputStream,this);
+ rv &= (c == sigBytes[i]);
+ }
+
+ return rv;
+ }
+
+
+ private final void checkIHDR(final DataInputStream inputStream,
+ final RepInfo repInfo,
+ final int declChunkLen)
+ throws IOException {
+
+ // W3C recommendations states that height and width are integers that
+ // range from 0 to 2^31
+ int tmp = (int)(readUnsignedInt(inputStream, PNG_ENDIANITY, this)&0xFFFFFFFF);
+ chcks.update(int2byteArray(tmp));
+
+ if (tmp == 0) {
+ repInfo.setValid(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("Illegal 0 value for height." ));
+
+ }
+
+ tmp = (int)(readUnsignedInt(inputStream, PNG_ENDIANITY, this)&0xFFFFFFFF);
+ chcks.update(int2byteArray(tmp));
+
+ if (tmp == 0) {
+ repInfo.setValid(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("Illegal 0 value for width." ));
+
+ }
+
+ tmp = readUnsignedByte(inputStream, this);
+ chcks.update((byte)tmp);
+
+ int colorType = readUnsignedByte(inputStream, this);
+ chcks.update((byte)colorType);
+
+ switch (colorType) {
+ case 0:
+ if (tmp != 1 &&
+ tmp != 2 &&
+ tmp != 4 &&
+ tmp != 8 &&
+ tmp != 16) {
+ repInfo.setValid(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("In IHDR, illegal value for bit depth for colour type " +
+
+ colorType + ": " +tmp ));
+
+ }
+ repInfo.setProfile("PNG GrayScale");
+
+ expectingPLTE=RepInfo.FALSE;
+ case 3:
+ if (tmp != 1 &&
+ tmp != 2 &&
+ tmp != 4 &&
+ tmp != 8 ) {
+ repInfo.setValid(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("In IHDR, illegal value for bit depth for colour type " +
+ colorType + ": " +tmp ));
+
+ }
+ // We need to find a palette!
+ expectingPLTE = RepInfo.TRUE;
+ colorDepth = tmp;
+ maxPaletteSize = 1 << tmp ;
+ repInfo.setProfile("PNG Indexed");
+
+ break;
+ case 4:
+ expectingPLTE=RepInfo.FALSE;
+ if (tmp != 8 &&
+ tmp != 16) {
+ repInfo.setValid(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("In IHDR, valore illegale per la profondita` dei bit per il colour type " +
+ colorType + ": " +tmp ));
+
+ }
+
+ repInfo.setProfile("PNG GrayScale with Alpha");
+ break;
+ case 6:
+ expectingPLTE=RepInfo.FALSE;
+ expecting_tRNS=RepInfo.FALSE;
+ if (tmp != 8 &&
+ tmp != 16) {
+ repInfo.setValid(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("In IHDR, valore illegale per la profondita` dei bit per il colour type " +
+ colorType + ": " +tmp ));
+
+ }
+ repInfo.setProfile("PNG Truecolor with Alpha");
+ break;
+ case 2:
+ expectingPLTE=RepInfo.FALSE;
+ expecting_tRNS=RepInfo.FALSE;
+ if (tmp != 8 &&
+ tmp != 16) {
+ repInfo.setValid(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("In IHDR, valore illegale per la profondita` dei bit per il colour type " +
+ colorType + ": " +tmp ));
+
+ }
+
+ repInfo.setProfile("PNG Truecolor");
+ break;
+ default:
+ repInfo.setValid(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("In IHDR, valore illegale per il colour type (" +
+ colorType +")"));
+
+ break;
+ }
+
+ // Compression
+ tmp = readUnsignedByte(inputStream, this);
+ chcks.update((byte)tmp);
+
+ if (tmp!=0) {
+ repInfo.setMessage(new InfoMessage("Attenzione, tipo di compressine " +
+ tmp + " not conforme alla raccommandazione del W3C."));
+ }
+
+
+ // filtering
+ tmp = readUnsignedByte(inputStream, this);
+ chcks.update((byte)tmp);
+
+ if (tmp!=0) {
+ repInfo.setMessage(new InfoMessage("Attenzione, tipo di filtro " +
+ tmp + " no ancora standardizzato dal W3C."));
+ }
+
+ // interlace
+
+ tmp = readUnsignedByte(inputStream, this);
+ chcks.update((byte)tmp);
+
+ if (tmp!=0 && tmp!=1) {
+ repInfo.setMessage(new InfoMessage("Attenzione, tipo di interlacciamento " +
+ tmp + " no ancora standardizzato dal W3C."));
+ }
+
+ long crc32 = readUnsignedInt(inputStream, PNG_ENDIANITY, this);
+
+ if (crc32 != chcks.getValue()) {
+ repInfo.setValid(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("Errore CRC nel chunk IHDR" ));
+ }
+
+ }
+
+ private final void checkPLTE(final DataInputStream inputStream,
+ final RepInfo repInfo,
+ final int declChunkLen)
+ throws IOException {
+
+ if ((declChunkLen % 3) != 0) {
+ repInfo.setValid(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("Lunghezza PLTE non valida." ));
+ }
+
+
+ // Scan the palette
+ paletteSize=0;
+
+ for (int i = 0; i < declChunkLen; i++) {
+ int tmp = readUnsignedByte(inputStream, this);
+ chcks.update((byte)tmp);
+
+ tmp = readUnsignedByte(inputStream, this);
+ chcks.update((byte)tmp);
+
+ tmp = readUnsignedByte(inputStream, this);
+ chcks.update((byte)tmp);
+
+ paletteSize++;
+
+ }
+
+ if (paletteSize > maxPaletteSize) {
+ repInfo.setValid(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("Too many palette items in PLTE chunk" ));
+
+
+ }
+ shortPalette = (paletteSize < maxPaletteSize);
+ long crc32 = readUnsignedInt(inputStream, PNG_ENDIANITY, this);
+
+ if (crc32 != chcks.getValue()) {
+ repInfo.setValid(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("CRC Error in PLTE chunk" ));
+
+ }
+ }
+
+ private final void checktIME(final DataInputStream inputStream,
+ final RepInfo repInfo,
+ final int declChunkLen)
+ throws IOException {
+ int yhrHigh = readUnsignedByte(inputStream, this);
+ chcks.update((byte)yhrHigh);
+
+ int yhrLow = readUnsignedByte(inputStream, this);
+ chcks.update((byte)yhrLow);
+
+ int month = readUnsignedByte(inputStream, this);
+ chcks.update((byte)month);
+
+ if (month < 1 || month > 12) {
+ repInfo.setValid(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("Illegal month value in tIME chunk"));
+
+ }
+
+ int day = readUnsignedByte(inputStream, this);
+ chcks.update((byte)day);
+
+ if (day < 1 || day > 31) {
+ repInfo.setValid(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("Illegal day value in tIME chunk"));
+
+ }
+
+ int hour = readUnsignedByte(inputStream, this);
+ chcks.update((byte)hour);
+
+ if (hour < 0 || hour > 23) {
+ repInfo.setValid(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("Illegal hour value in tIME chunk"));
+
+ }
+
+ int minute = readUnsignedByte(inputStream, this);
+ chcks.update((byte)minute);
+
+ if (minute < 0 || minute > 59) {
+ repInfo.setValid(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("Illegal minute value in tIME chunk"));
+
+ }
+
+ int second = readUnsignedByte(inputStream, this);
+ chcks.update((byte)second);
+
+ if (second < 0 || second > 60) {
+ repInfo.setValid(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("Illegal second value in tIME chunk"));
+
+ }
+
+ long crc32 = readUnsignedInt(inputStream, PNG_ENDIANITY, this);
+
+ if (crc32 != chcks.getValue()) {
+ repInfo.setValid(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("CRC Error in tIME chunk" ));
+
+ }
+
+ // Some operations to deal with GregorianCalendar class, that
+ // has 0 base month numbering and doesn't like leap seconds
+ second = (second == 60) ? 59 : second;
+ month--;
+ repInfo.setLastModified(new GregorianCalendar((yhrHigh<<8)+yhrLow ,
+ month,
+ day,
+ hour,
+ minute,
+ second).getTime());
+
+ }
+
+
+ private final void checktEXT(final DataInputStream inputStream,
+ final RepInfo repInfo,
+ int declChunkLen)
+ throws IOException {
+ int c=-1;
+ int keywordLen=0;
+ StringBuffer buf = new StringBuffer();
+
+ while (keywordLen < MAX_KEYWORD_LEN) {
+ c = readUnsignedByte(inputStream, this);
+ chcks.update((byte)c);
+
+ declChunkLen--;
+
+ if (c==0) {
+ break;
+ }
+
+ keywordLen++;
+ buf.append((char)c);
+ }
+
+
+ if (keywordLen == MAX_KEYWORD_LEN) {
+ // we hit MAX_KEYWORD_LEN, let's check if there's the
+ // mandatory 0 byte
+ c = readUnsignedByte(inputStream, this);
+ chcks.update((byte)c);
+ declChunkLen--;
+
+ if (c != 0) {
+ // segnalare errore e scartare
+ repInfo.setValid(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("Missing 0 byte after keyword"));
+
+ checkChunk(inputStream, repInfo, declChunkLen, "tEXT");
+ buf.append((char)c);
+
+ }
+
+
+ return;
+ }
+
+ String keyword = buf.toString();
+
+ // so far we got a keyword and the null ( 0 ) separator, lets'
+ // get the value, set a property and check that everything is
+ // OK with the CRC.
+
+ buf.delete(0, buf.length() );
+
+ while (declChunkLen > 0) {
+ c = readUnsignedByte(inputStream, this);
+ chcks.update((byte)c);
+ declChunkLen--;
+
+ }
+
+ String value = buf.toString();
+
+ Property p = new Property(keyword,
+ PropertyType.STRING,
+ value);
+ repInfo.setProperty(p);
+ Booolean bol = (Booolean)keywordList.get(keyword);
+
+ if (bol != null) {
+ bol.setFlag(true);
+ }
+
+ long crc32 = readUnsignedInt(inputStream, PNG_ENDIANITY, this);
+
+ if (crc32 != chcks.getValue()) {
+ repInfo.setValid(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("CRC Error in tEXT chunk" ));
+
+ }
+
+
+ }
+
+ private final void checkChunk(final DataInputStream inputStream,
+ final RepInfo repInfo,
+ int declChunkLen,
+ final String chunkSig)
+ throws IOException {
+ int read = 0;
+
+ while (read < declChunkLen) {
+ int tmp = readUnsignedByte(inputStream, this);
+ chcks.update((byte)tmp);
+ read++;
+ }
+ long crc32 = readUnsignedInt(inputStream, PNG_ENDIANITY, this);
+
+ if (crc32 != chcks.getValue()) {
+ repInfo.setValid(RepInfo.FALSE);
+ repInfo.setMessage(new ErrorMessage("CRC Error in " + chunkSig +" chunk" ));
+
+ }
+
+ }
+
+ // Utility predicate
+ private final boolean somethingWrongP(RepInfo repInfo) {
+ return repInfo.getWellFormed() == RepInfo.FALSE ||
+ repInfo.getValid() == RepInfo.FALSE;
+ }
+
+ private final String getCompressedString(DataInputStream dis,
+ int bytesToRead)
+ throws IOException, DataFormatException {
+ ByteArrayOutputStream bous = new ByteArrayOutputStream();
+ Inflater pump = new Inflater();
+ byte o[] = new byte[1024];
+
+ while (bytesToRead > 0) {
+ int c = 0;
+
+ while (c < 1024 && bytesToRead > 0) {
+ o[c++] = (byte)(readUnsignedByte(dis,this));
+ bytesToRead--;
+ }
+
+ pump.setInput(o);
+
+ c=1;
+ while (!pump.needsInput() && c > 0) {
+ c = pump.inflate(o);
+
+ if (c>0)
+ bous.write(o,0,c);
+ }
+
+
+ }
+
+ return bous.toString("ISO-8859-1");
+
+ }
+
+ // Turns an 32 bit integer intto a byte array
+ private final byte[] int2byteArray(int a)
+ {
+ byte b[]=new byte[]{0,0,0,0};
+
+ b[3]=(byte)(a&0xFF);
+ a=a>>8;
+
+ b[2]=(byte)(a&0xFF);
+ a=a>>8;
+
+ b[1]=(byte)(a&0xFF);
+ a=a>>8;
+
+ b[0]=(byte)(a&0xFF);
+
+ return b;
+
+ }
+
+
+// private final Checksummer chcks = new Checksummer();
+
+ // Strangely, it seems it doesn't work with the other checksummer...
+ private final CRC32 chcks = new CRC32();
+
+ // PNG signature
+ private static final int SIGNATURE[] = {137, 80, 78, 71, 13, 10, 26, 10};
+
+ // Module instantiation constants.
+ private static final String NAME = "PNG-engineering";
+ private static final String RELEASE = "1.0";
+ private static final int DATE[] = {2006, 9, 25};
+ private static final String FORMAT[] = {
+ "PNG",
+ "Portable Network Graphics"
+ };
+ private static final String MIMETYPE[] = {"image/png"};
+ private static final String COVERAGE = "PNG";
+ private static final String WELLFORMED = null;
+ private static final String VALIDITY = null;
+ private static final String REPINFO = null;
+ private static final String NOTE = "Work in progress";
+ private static final String RIGHTS =
+ "Copyright 2006 Engineering Ingengeria Informatica S.p.a." +
+ "Released under the GNU Lesser General Public License." +
+ "Cryptoserver Library Copyright Engiweb Security, all rights reserved";
+ private static final boolean RANDOM = false;
+
+ /*
+ * Chunk signatures.
+ *
+ * Java *IS* Big Endian, PNG chunk signatures are 4 byte strings we
+ * *CAN* read into an int variable since all of them have bit 7
+ * set to 0.
+ *
+ * Therefore we can check each chunk signature against int
+ * constants (one opcode executed, no loops).
+ *
+ * About names: these name violate the Java naming rules for
+ * constants, but I prefer to keep the PNG chunk name cases, since
+ * they are meaningful for the properties of each chunk.
+ */
+ private final static int IHDR_HEAD_SIG = 0x49484452;
+ private final static int PLTE_HEAD_SIG = 0x504c5445;
+ private final static int IDAT_HEAD_SIG = 0x49444154;
+ private final static int IEND_HEAD_SIG = 0x49454e44;
+ private final static int cHRM_HEAD_SIG = 0x6348524d;
+ private final static int gAMA_HEAD_SIG = 0x67414d41;
+ private final static int iCCP_HEAD_SIG = 0x69434350;
+ private final static int sBIT_HEAD_SIG = 0x73424954;
+ private final static int sRGB_HEAD_SIG = 0x73524742;
+ private final static int tEXt_HEAD_SIG = 0x74455874;
+ private final static int zTXt_HEAD_SIG = 0x7a545874;
+ private final static int iTXt_HEAD_SIG = 0x69545874;
+ private final static int bKGD_HEAD_SIG = 0x624b4744;
+ private final static int hIST_HEAD_SIG = 0x68495354;
+ private final static int pHYs_HEAD_SIG = 0x70485973;
+ private final static int sPLT_HEAD_SIG = 0x73504c54;
+ private final static int tIME_HEAD_SIG = 0x74494d45;
+ private final static int tRNS_HEAD_SIG = 0x74524e53;
+ // Property bit masks
+ private final static int PROP_SAFE_TO_COPY = 0x00000020;
+ private final static int PROP_PRIVATE = 0x00002000;
+ private final static int PROP_RESERVED = 0x00200000;
+ private final static int PROP_ANCILLARY = 0x20000000;
+
+ // Maximum keyword lenght
+ private final static int MAX_KEYWORD_LEN = 79;
+
+ // Standard keyword for the creation timestamp
+ private final static String CREATION_TIME_KEYWORD = "Creation Time";
+
+ /*------------------------------------------------------------------*
+ |******************************************************************|
+ |* *|
+ |* Parser inner state flags. *|
+ |* *|
+ |* The state is represented by a score of flags associated to the *|
+ |* chunks that should appear no more than once. The code uses the *|
+ |* flags to manage the partial ordering in the chunk layout *|
+ |* *|
+ |* Flags have the value RepInfo.UNDETERMINED when the chunk may *|
+ |* either appear or not, RepInfo.TRUE when the chunk is expected *|
+ |* but has yet to be found, RepInfo.FALSE when the chunk should *|
+ |* not appear any more. Istantiation values are for documentation *|
+ |* purpose only and are repeated in the initParse() method. *|
+ |* *|
+ |******************************************************************|
+ *------------------------------------------------------------------*/
+
+ /*
+ * Starting chunk, must be the first one, expected.
+ */
+ private int expectingIHDR = RepInfo.TRUE;
+
+ /*
+ * This is 3 state flag: it is unknown until you know you need to
+ * find the PLTE chunk, when it turs to RepInfo.TRUE or/and you
+ * know you should not find such block any more (i.e. from color
+ * type and bit depth or because you already got this chunk.
+ */
+ private int expectingPLTE = RepInfo.UNDETERMINED;
+
+ /*
+ * Data chunk, turns to RepInfo.false upon finding the firs chunk
+ * of this type
+ */
+ private int expectingIDAT = RepInfo.TRUE;
+
+ /*
+ * Ending chunk, this flag is used to handle the end of file
+ * condition, if it happens when the flag is RepInfo.TRUE; then
+ * the file is not well formed.
+ */
+ private int expectingIEND = RepInfo.TRUE;
+
+ // non critical chunks
+
+ private int expecting_cHRM = RepInfo.UNDETERMINED;
+ private int expecting_gAMA = RepInfo.UNDETERMINED;
+ private int expecting_iCCP = RepInfo.UNDETERMINED;
+ private int expecting_sBIT = RepInfo.UNDETERMINED;
+ private int expecting_sRGB = RepInfo.UNDETERMINED;
+ private int expecting_tEXt = RepInfo.UNDETERMINED;
+ private int expecting_zTXt = RepInfo.UNDETERMINED;
+ private int expecting_iTXt = RepInfo.UNDETERMINED;
+ private int expecting_bKGD = RepInfo.UNDETERMINED;
+ private int expecting_hIST = RepInfo.UNDETERMINED;
+ private int expecting_pHYs = RepInfo.UNDETERMINED;
+ private int expecting_sPLT = RepInfo.UNDETERMINED;
+ private int expecting_tIME = RepInfo.UNDETERMINED;
+ private int expecting_tRNS = RepInfo.UNDETERMINED;
+
+ // Palette size, in colours
+ private int maxPaletteSize = 0;
+ private int paletteSize = 0;
+ private boolean shortPalette = false;
+ private int colorDepth = 0;
+
+ private Map keywordList;
+
+ private final static String PNG_PROFILES[] =
+ new String[] { "PNG GrayScale", // 0
+ "Unused", // 1
+ "PNG Truecolor", // 2
+ "PNG Indexed", // 3
+ "PNG GrayScale with Alpha", // 4
+ "Unused", // 5
+ "PNG Truecolor with Alpha"}; // 6
+
+}