217 lines
7.2 KiB
Java
217 lines
7.2 KiB
Java
/**********************************************************************
|
|
* JDump - JSTOR/Harvard Object Validation Environment
|
|
* Copyright 2004 by the President and Fellows of Harvard College
|
|
*
|
|
* 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
|
|
**********************************************************************/
|
|
|
|
import edu.harvard.hul.ois.jhove.*;
|
|
import java.io.*;
|
|
import java.util.*;
|
|
|
|
/**
|
|
* Dump contents of JPEG2000 file in human-readable format.
|
|
*/
|
|
public class J2Dump extends Dump {
|
|
|
|
/* Fixed value for first 12 bytes */
|
|
private static final int[] sigByte =
|
|
{
|
|
0X00,
|
|
0X00,
|
|
0X00,
|
|
0X0C,
|
|
0X6A,
|
|
0X50,
|
|
0X20,
|
|
0X20,
|
|
0X0D,
|
|
0X0A,
|
|
0X87,
|
|
0X0A };
|
|
|
|
private static final boolean ENDIAN = true; /* bigEndian */
|
|
|
|
/******************************************************************
|
|
* MAIN ENTRY POINT.
|
|
******************************************************************/
|
|
|
|
/**
|
|
* Main entry point.
|
|
* @param args Command line arguments
|
|
*/
|
|
public static void main (String [] args)
|
|
{
|
|
if (args.length < 1) {
|
|
System.err.println ("usage: java J2Dump file");
|
|
System.exit (-1);
|
|
}
|
|
try {
|
|
FileInputStream file = new FileInputStream (args[0]);
|
|
BufferedInputStream buffer = new BufferedInputStream (file);
|
|
DataInputStream stream = new DataInputStream (buffer);
|
|
J2Dump dump = new J2Dump (); // Just to access contained classes
|
|
long os = 0;
|
|
boolean bigEndian = true;
|
|
int i;
|
|
for (i = 0; i < 12; i++) {
|
|
int ch;
|
|
ch = stream.readUnsignedByte();
|
|
if (ch != sigByte[i]) {
|
|
System.out.println ("No JPEG 2000 header");
|
|
System.exit (-2);
|
|
}
|
|
}
|
|
os += 12;
|
|
|
|
boolean endOfFile = false;
|
|
Stack boxStack = new Stack ();
|
|
while (!endOfFile) {
|
|
//If there are boxes on the stack, see if there's space
|
|
//left in the top one.
|
|
Box boxtop;
|
|
for (;;) {
|
|
boxtop = null;
|
|
if (boxStack.isEmpty ()) {
|
|
break;
|
|
}
|
|
boxtop = (Box) boxStack.peek ();
|
|
if (boxtop.bytesLeft > 0) {
|
|
break;
|
|
}
|
|
else {
|
|
boxStack.pop ();
|
|
}
|
|
}
|
|
|
|
// Read the header of a JP2 box
|
|
Box box = dump.new Box (stream);
|
|
try {
|
|
box.read ();
|
|
}
|
|
catch (EOFException e) {
|
|
endOfFile = true;
|
|
break;
|
|
}
|
|
os += box.length - box.bytesLeft;
|
|
|
|
// If it's contained in a superbox, subtract
|
|
// this box from its remaining length
|
|
if (boxtop != null) {
|
|
boxtop.bytesLeft -= box.length;
|
|
}
|
|
System.out.println (leading (os, 8) + os + ": " +
|
|
stackPrefix (boxStack) + box.type + " " + box.length);
|
|
if (box.isSuperbox ()) {
|
|
boxStack.push (box);
|
|
}
|
|
else {
|
|
os += box.bytesLeft;
|
|
stream.skipBytes((int) box.bytesLeft);
|
|
}
|
|
|
|
// A "length" of 0 means the box occupies the rest of the file.
|
|
if (box.length == 0) {
|
|
endOfFile = true;
|
|
}
|
|
}
|
|
}
|
|
catch (Exception e) {
|
|
e.printStackTrace (System.err);
|
|
System.exit (-2);
|
|
}
|
|
}
|
|
|
|
|
|
/* Constructs a qualifying prefix to indicate nested boxes. */
|
|
private static String stackPrefix (Stack boxStack)
|
|
{
|
|
StringBuffer retval = new StringBuffer ();
|
|
// In defiance of gravity, we rummage through the stack
|
|
// of boxes starting at the bottom.
|
|
for (int i = 0; i < boxStack.size(); i++) {
|
|
Box box = (Box) boxStack.elementAt (i);
|
|
// Remove trailing spaces from types for better readability
|
|
retval.append (box.type.trim() + "/");
|
|
}
|
|
return retval.toString ();
|
|
}
|
|
|
|
|
|
|
|
/** Local class for defining JPEG2000 boxes. */
|
|
class Box {
|
|
public String type;
|
|
public long length;
|
|
public long bytesLeft;
|
|
public boolean hasBoxes;
|
|
DataInputStream dstream;
|
|
|
|
public Box (DataInputStream stream)
|
|
{
|
|
this.dstream = stream;
|
|
}
|
|
|
|
|
|
/** Reads a box header and sets up for reading contents. */
|
|
public void read () throws IOException
|
|
{
|
|
length = ModuleBase.readUnsignedInt (dstream, ENDIAN, null);
|
|
long headerLength = 8;
|
|
type = read4Chars ();
|
|
// If the length field is 1, there is an 8-byte extended
|
|
// length field.
|
|
if (length == 1) {
|
|
length = ModuleBase.readSignedLong(dstream, true, null);
|
|
headerLength = 16;
|
|
}
|
|
bytesLeft = length - headerLength;
|
|
}
|
|
|
|
|
|
/* Reads a 4-character name */
|
|
private String read4Chars() throws IOException {
|
|
StringBuffer sbuf = new StringBuffer(4);
|
|
for (int i = 0; i < 4; i++) {
|
|
int ch = ModuleBase.readUnsignedByte(dstream, null);
|
|
sbuf.append((char) ch);
|
|
}
|
|
return sbuf.toString();
|
|
}
|
|
|
|
/** Returns true if this box contains other boxes.
|
|
* At present, we don't deal with the insides of boxes
|
|
* that contain both data and boxes (e.g., cref). */
|
|
public boolean isSuperbox ()
|
|
{
|
|
// If it's a known superbox type, we return true.
|
|
// If we've left any out, that will merely make us
|
|
// lose the subboxes of that type.
|
|
String [] supertypes = { "asoc", "cgrp", "comp", "drep",
|
|
"ftbl", "jp2h", "jpch",
|
|
"jplh", "res ", "uuid" };
|
|
for (int i = 0; i < supertypes.length; i++) {
|
|
if (supertypes[i].equals (type)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
}
|