package uk.co.mmscomputing.imageio.pdf;
import java.io.*;
import java.util.*;
import uk.co.mmscomputing.io.ModModREADOutputStream;
import uk.co.mmscomputing.io.RLEBit1OutputStream;
public class PDFFilter{
static public class PDFEncoder extends FilterOutputStream{
protected int len=0;
public PDFEncoder(OutputStream out){
super(out);
}
public int getLength(){return len;}
public void write(byte[] b,int off,int len)throws IOException{
for(int i=off;i<off+len;i++){
write(b[i]);
}
}
public void write(int b)throws IOException{
super.write(b);len++;
}
}
static public class PDFLineEncoder extends PDFEncoder{
public PDFLineEncoder(OutputStream out){
super(out);
}
public void write(int b)throws IOException{
if((len%255)==254){super.write('\n');}
super.write(b);
}
}
static public class PDFASCIIHexEncoder extends PDFLineEncoder{
public PDFASCIIHexEncoder(OutputStream out){
super(out);
}
public void write(int b)throws IOException{
int hi = (b>>4) & 0x000F;
int lo = b & 0x000F;
hi = (hi<10)?'0'+hi:'A'+(hi-10);
lo = (lo<10)?'0'+lo:'A'+(lo-10);
super.write(hi);super.write(lo);
}
public void close()throws IOException{
super.write('>');
super.close();
}
}
static public class PDFASCII85Encoder extends PDFLineEncoder{ // [1] p.31
int i=0; long val=0;
public PDFASCII85Encoder(OutputStream out){
super(out);
}
public void write(int b)throws IOException{ // ASCII character between 33 ('!') and 117 (u)
val <<= 8;
val |= (b&0x00FF);
i++;
if(i==4){
if(val==0){
super.write('z');
}else{
int c85=85*85*85*85;
for(int j=0;j<5;j++){
super.write('!'+((int)(val/c85))); val%=c85; c85/=85;
}
}
i=0;val=0;
}
}
public void close()throws IOException{
if(i>0){ // if length of data not multiple of 4
for(int j=i;j<4;j++){val<<=8;} // append 4-i zero bytes
int c85=85*85*85*85; // encode in usual way, but without z case
for(int j=0;j<i+1;j++){ // write only first i+1 characters
super.write('!'+((int)(val/c85))); val%=c85; c85/=85;
}
}
super.write('~');super.write('>');
super.close();
}
}
/*
static public class PDFDCTEncoder extends PDFEncoder{
public PDFDCTEncoder(OutputStream out)throws IOException{
super(out);
}
}
*/
static public class PDFCCITTFaxEncoder extends PDFEncoder{
RLEBit1OutputStream rlos; // byte = 8 black and white pixel
ModModREADOutputStream mmros; // T.6 MMR
int offLine,bytesPerLine;
public PDFCCITTFaxEncoder(OutputStream out,int width)throws IOException{
super(out);
bytesPerLine = (width+7)>>3;
mmros = new ModModREADOutputStream(out,width);
rlos = new RLEBit1OutputStream(mmros);
out = rlos;
rlos.setStartCodeWord(0x0001); // white run first
mmros.writeEOL(); // T.6 does not really write EOL; only new line initialization
offLine = 0;
}
public void write(int b)throws IOException{
if(offLine==bytesPerLine){
rlos.setStartCodeWord(0x0001); // white run first
mmros.writeEOL(); // T.6 does not really write EOL; only new line initialization
offLine=0;
}
super.write(b);
offLine++;
}
public void close()throws IOException{
rlos.flush();
mmros.writeEOFB();
mmros.flush();
super.close();
}
}
static private PDFEncoder getEncoder(
OutputStream out,
PDFDictionary dict,
String coderName
)throws IOException{
if(coderName.equals("ASCIIHexDecode")){
return new PDFASCIIHexEncoder(out);
}else if(coderName.equals("ASCII85Decode")){
return new PDFASCII85Encoder(out);
}else if(coderName.equals("DCTDecode")){
/*
This will be encoded before PDFStream.setInputStream
see. PDFXObject.PDFImage.write
*/
return new PDFEncoder(out);
}else if(coderName.equals("CCITTFaxDecode")){
int width = ((PDFObject.PDFInteger)dict.get("Width")).getValue();
//System.err.println("width = "+width);
// return new PDFCCITTFaxEncoder(out,width);
/*
This will be encoded before PDFStream.setInputStream
see. PDFXObject.PDFImage.write
*/
return new PDFEncoder(out);
}
throw new IllegalArgumentException();
}
static private PDFEncoder getEncoder(
OutputStream out,
PDFDictionary dict,
PDFArray filter,
int i
)throws IOException{
PDFObject.PDFName coder = (PDFObject.PDFName)filter.elementAt(i);
String coderName = coder.getName();
PDFEncoder encoder = getEncoder(out,dict,coderName);
try{
return getEncoder(encoder,dict,(PDFArray)filter,i+1);
}catch(ArrayIndexOutOfBoundsException e){
return encoder;
}
}
static PDFEncoder getEncoder(
OutputStream out,
PDFDictionary dict,
PDFObject filter
)throws IOException{
if(filter instanceof PDFArray){
return getEncoder(out,dict,(PDFArray)filter,0);
}else if(filter instanceof PDFObject.PDFName){
return getEncoder(out,dict,((PDFObject.PDFName)filter).getName());
}
throw new IllegalArgumentException();
}
public static void main(String[] argv){
try{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PDFASCII85Encoder encoder = new PDFASCII85Encoder(baos);
// FF D8 FF E0 00 10 4A 46 49 46
byte[] buf = {(byte)0xFF,(byte)0xD8,(byte)0xFF,(byte)0xE0,(byte)0x00,(byte)0x10,(byte)0xA4,(byte)0x46,(byte)0x49,(byte)0x46};
encoder.write(buf);
encoder.close();
System.err.println(new String(baos.toByteArray()));
}catch(Exception e){
e.printStackTrace();
}
}
}
/*
[1] Portable Document Format Reference Manual
ISBN 0-201-62628-4
1996
*/