package uk.co.mmscomputing.imageio.pdf; import java.io.*; import java.util.*; import java.util.*; import java.awt.image.BufferedImage; import javax.imageio.stream.ImageInputStream; // all ASCII // binary data must be encoded in ASCII hex or ASCII base-85 // no line in a pdf file may be longer than 255 chars // line delimiter \n (10), \r (13), \r\n (10,13) public class PDFFile implements PDFConstants{ // <PDF file> ::= <header> // <body> // <cross-reference table> // <trailer> private PDFHeader header; private PDFInfo info; private PDFBody body; private PDFCrossReferenceTable crossReferenceTable; private PDFTrailer trailer; private OutputStream out; private ImageInputStream in; public PDFFile(){ header = new PDFHeader(); crossReferenceTable = new PDFCrossReferenceTable(); body = new PDFBody(crossReferenceTable); info = new PDFInfo(body); trailer = new PDFTrailer(); } public PDFBody getBody(){return body;} public PDFCatalog getCatalog(){return body.getCatalog();} public void write(OutputStream out)throws IOException{ this.out=out; header.write(this); body.write(this); crossReferenceTable.write(this); trailer.put("Size",body.getSize()); trailer.put("Root",body.getRoot()); trailer.setInfo(info); trailer.write(this,crossReferenceTable.getLastCrossReferenceSectionOffset()); } public int getOffset(){ return ((ByteArrayOutputStream)out).size(); } public void write(byte b)throws IOException{ out.write(b); } public void write(char b)throws IOException{ out.write((byte)b); } public void write(byte[] bytes)throws IOException{ out.write(bytes); } public void write(String s)throws IOException{ write(s.getBytes()); } public void writeln(String s)throws IOException{ write(s.getBytes());write('\n'); } public void write(int i)throws IOException{ write(Integer.toString(i)); } public void writeln()throws IOException{ write('\n'); } public void read(ImageInputStream in)throws IOException{ PDFScanner s=new PDFScanner(body,in); header.read(s); trailer.read(s); // System.err.println("startxref: "+trailer.getStartXRef()); // PDFIndirectObject root = trailer.getRoot(); // int objectNumber = root.getObjectNumber(); // int generationNumber = root.getGenerationNumber(); s.seek(trailer.getStartXRef()); crossReferenceTable = new PDFCrossReferenceTable(trailer.getSize()); crossReferenceTable.read(s); // get list of indirect objects PDFCrossReferenceEntry[] entries = crossReferenceTable.getEntries(); for(int i=1;i<entries.length;i++){ PDFCrossReferenceEntry entry = crossReferenceTable.getEntry(i); PDFIndirectObject indobj = body.getIndirectObject(entry.getObjectNumber(),entry.getGenerationNumber()); indobj.setOffset(entry.getOffset()); } boolean again;int tries=0; do{ again=false; for(int i=1;i<entries.length;i++){ PDFCrossReferenceEntry entry = crossReferenceTable.getEntry(i); if(entry.getObject()==null){ PDFIndirectObject indobj = body.getIndirectObject(entry.getObjectNumber(),entry.getGenerationNumber()); s.seek(entry.getOffset()); try{ indobj.read(s); // System.err.println("\n\n"+indobj.toString()+"\n"); entry.setObject(indobj); }catch(Exception e){ again=true; // Probably couldn't resolve 'Length' for a stream. // System.err.println("\n\nREADING RESOURCE["+i+"] => EXCEPTION: "+e+"\n"); // e.printStackTrace(); } } } tries++; // System.err.println("try = "+tries); }while(again&&(tries<10)); } public PDFPage getNewPage(){return body.getCatalog().getNewPage();} public BufferedImage getImage(int index)throws IOException{ int size = trailer.getSize(); int ind = 0; for(int i=1;i<size;i++){ PDFCrossReferenceEntry entry = crossReferenceTable.getEntry(i); PDFObject object = entry.getDirectObject(); if(object instanceof PDFImage){ if(ind==index){ return ((PDFImage)object).getImage(); } ind++; } } return null; } static public class PDFHeader{ // <header> ::= <PDF version> private String header = "PDF-1.1"; public void write(PDFFile out)throws IOException{ out.writeln("%"+header); out.write("%");out.write((byte)129);out.write((byte)129);out.write((byte)129);out.write((byte)129);out.writeln(); } public void read(PDFScanner s)throws IOException{ header=s.scanComment(); // System.err.println("header = "+header); } } static public class PDFTrailer extends PDFDictionary{ // <trailer> ::= trailer // << // <trailer key-value pair>+ // >> // startxref // <cross-reference table start address> // %%EOF private int xref = -1; public void setStartXRef(int offset){xref=offset;} public int getStartXRef(){ return xref;} public int getSize(){ return ((PDFObject.PDFInteger)get("Size")).getValue();} public PDFIndirectObject getRoot(){return ((PDFIndirectObject)((PDFIndirectReference)get("Root")).getIndirectObject());} public void setInfo(PDFInfo info){ if(info!=null){put("Info",info.getReference());} } public void read(PDFScanner s)throws IOException{ s.seek(s.getLength()-256); // find token 'trailer' at end of file s.find(T_TRAILER); s.scan(); super.read(s); s.scan(); if(s.symbol!=T_STARTXREF){throw new IOException(getClass().getName()+".read:\n\tTrailer: Cannot find keyword <startxref>.");} // A normal s.scan() would scan %%EOF as well [reach EOF] and then seek does not work anymore ??? Why ??? // s.scan(); s.scanStartXRefNumber(); if(s.symbol!=T_INTEGER){throw new IOException(getClass().getName()+".read:\n\tTrailer: Cannot find startxref value.");} xref = s.intval; } public void write(PDFFile out,int xref)throws IOException{ out.writeln("trailer"); super.write(out); out.writeln("startxref"); out.write(xref); out.writeln(); out.write("%%EOF"); } } static public class PDFIncrementalUpdate{ // todo } }