package uk.co.mmscomputing.imageio.pdf;
import java.io.*;
import java.util.*;
import java.awt.image.*;
import java.awt.color.*;
import uk.co.mmscomputing.imageio.jpeg.JPEGImageWriter;
import uk.co.mmscomputing.imageio.jpeg.JFIFInputStream;
import uk.co.mmscomputing.io.RLEBit1OutputStream;
import uk.co.mmscomputing.io.ModHuffmanOutputStream;
import uk.co.mmscomputing.io.ModModREADOutputStream;
import uk.co.mmscomputing.io.BitSwapOutputStream;
import uk.co.mmscomputing.io.ModHuffmanInputStream;
import uk.co.mmscomputing.io.ModModREADInputStream;
import uk.co.mmscomputing.io.BitSwapInputStream;
import uk.co.mmscomputing.io.RLEBitInputStream;
public class PDFImage extends PDFXObject{
public PDFImage(String name, BufferedImage image) throws IOException{ // Writer
super("Image");
int width = image.getWidth();
int height = image.getHeight();
// [1] p.79 Name must match the name used in the XObject dictionary within the page's Resources dictionary
put("Name", new PDFObject.PDFName(name));
put("Width", new PDFObject.PDFInteger(width));
put("Height", new PDFObject.PDFInteger(height));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ColorModel cm = image.getColorModel();
if((image.getType()==BufferedImage.TYPE_BYTE_BINARY)&&(cm.getPixelSize()==1)){
put("BitsPerComponent", new PDFObject.PDFInteger(1));
put("ColorSpace", new PDFObject.PDFName("DeviceGray"));
setFilter("CCITTFaxDecode");
PDFDictionary ccittDict = new PDFDictionary(); // but for CCITTFaxDecode
// ccittDict.put("K",new PDFObject.PDFInteger(0)); // ModHuffmanOutputStream
ccittDict.put("K",new PDFObject.PDFInteger(-1)); // ModModREADOutputStream
ccittDict.put("Columns",new PDFObject.PDFInteger(width));
ccittDict.put("Rows",new PDFObject.PDFInteger(height));
put("DecodeParms",ccittDict);
encodeCCITT(baos,image);
}else{
if(image.getType()==BufferedImage.TYPE_BYTE_GRAY){ // one component; grey scale
put("BitsPerComponent", new PDFObject.PDFInteger(8));
put("ColorSpace", new PDFObject.PDFName("DeviceGray"));
String[] filters = {/*"ASCII85Decode",*/"DCTDecode"}; // /Filter /ASCII85Decode /DCTDecode
setFilters(filters);
}else{ // three components; YCbCr
put("BitsPerComponent", new PDFObject.PDFInteger(8)); // only RGB for the moment
put("ColorSpace", new PDFObject.PDFName("DeviceRGB"));
String[] filters = {/*"ASCII85Decode",*/"DCTDecode"}; // /Filter /ASCII85Decode /DCTDecode
setFilters(filters);
}
JPEGImageWriter.write(baos,image);
}
setInputStream(baos.toByteArray());
}
// public void write(PDFFile out)throws IOException{
// super.write(out);
// }
private void encodeCCITT(ByteArrayOutputStream baos, BufferedImage image)throws IOException{
// System.out.println(getClass().getName()+"\tPDF BINARY CCITTFaxDecode");
int width = image.getWidth();
int height = image.getHeight();
WritableRaster raster=image.getRaster();
DataBufferByte buffer=(DataBufferByte)raster.getDataBuffer();
byte[] imgdata=(byte[])buffer.getData();
BitSwapOutputStream bsos = new BitSwapOutputStream(baos);
ModHuffmanOutputStream mhos = new ModModREADOutputStream(bsos,width);
RLEBit1OutputStream rlos = new RLEBit1OutputStream(mhos);
int len=width>>3; // eight pixel per byte
int end=8-(width&0x07); // how many bits of last byte represent image data
int off=0;
if(end==8){ // image row ends at byte boundary
for(int y=0;y<height;y++){
rlos.setStartCodeWord(0x0001); // white run first; White is Zero: 1s in image => 0s in compressed data
mhos.writeEOL(); // T.6: we don't write EOL code, we just set up buffers here
rlos.write(imgdata,off,len);
rlos.flush();
off+=len;
}
}else{
for(int y=0;y<height;y++){
rlos.setStartCodeWord(0x0001); // white run first; White is Zero: 1s in image => 0s in compressed data
mhos.writeEOL(); // T.6: we don't write EOL code, we just set up buffers here
rlos.write(imgdata,off,len);
rlos.writeBits(imgdata[off+len],7,end); // write end of line pixel
rlos.flush();
off+=len+1;
}
}
if(mhos instanceof ModModREADOutputStream){ // T.6: write EOFB
((ModModREADOutputStream)mhos).writeEOFB();
}
rlos.close();
}
private int imageType = -1;
public PDFImage(PDFDictionary dict){ // Reader
super(dict);
}
public void read(PDFScanner s)throws IOException{
super.read(s);
String[] filters = getFilters();
int i=0;
while(i<(filters.length-1)){
// filter
i++;
}
String filter = filters[i];
if(filter.equals("DCTDecode")){
String colorspace = ((PDFObject.PDFName)get("ColorSpace")).getName();
if(colorspace.equals("DeviceGray")){
// int bpc = ((PDFObject.PDFInteger)get("BitsPerComponent")).getValue();
imageType = BufferedImage.TYPE_BYTE_GRAY;
return;
}else if(colorspace.equals("DeviceRGB")){
// int bpc = ((PDFObject.PDFInteger)get("BitsPerComponent")).getValue();
imageType = BufferedImage.TYPE_INT_RGB;
return;
}
throw new IOException(getClass().getName()+".read:\n\tCannot read image stream. Unknown 'ColorSpace' "+colorspace+".");
}else if(filter.equals("CCITTFaxDecode")){
String colorspace = ((PDFObject.PDFName)get("ColorSpace")).getName();
if(colorspace.equals("DeviceGray")){
imageType = BufferedImage.TYPE_BYTE_BINARY;
return;
}
}
throw new IOException(getClass().getName()+".read:\n\tCannot read image stream. Unknown 'Filter' "+filter+".");
}
public BufferedImage getImage()throws IOException{
BufferedImage image = null;
int width = ((PDFObject.PDFInteger)get("Width")).getValue();
int height = ((PDFObject.PDFInteger)get("Height")).getValue();
switch(imageType){
case BufferedImage.TYPE_BYTE_GRAY:
image = new BufferedImage(width,height,BufferedImage.TYPE_BYTE_GRAY);
decodeJPEGGray(image,data);
break;
case BufferedImage.TYPE_INT_RGB:
image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
decodeJPEGRGB(image,data);
break;
case BufferedImage.TYPE_BYTE_BINARY:
image = new BufferedImage(width,height,BufferedImage.TYPE_BYTE_BINARY);
decodeT6(image,data);
break;
}
return image;
}
private void decodeJPEGGray(BufferedImage image,InputStream data)throws IOException{
WritableRaster raster=image.getRaster();
DataBufferByte buffer=(DataBufferByte)raster.getDataBuffer();
byte[] imgdata=(byte[])buffer.getData();
new JFIFInputStream(data).read(imgdata);
}
private void decodeJPEGRGB(BufferedImage image,InputStream data)throws IOException{
WritableRaster raster=image.getRaster();
DataBufferInt buffer=(DataBufferInt)raster.getDataBuffer();
int[] imgdata=(int[])buffer.getData();
new JFIFInputStream(data).read(imgdata);
}
private void decodeT6(BufferedImage image,InputStream data)throws IOException{
WritableRaster raster=image.getRaster();
DataBufferByte buffer=(DataBufferByte)raster.getDataBuffer();
byte[] imgdata=(byte[])buffer.getData();
int width = image.getWidth();
// int height = image.getHeight();
BitSwapInputStream bsis = new BitSwapInputStream(data);
ModHuffmanInputStream mhis = new ModModREADInputStream(bsis,width);
RLEBitInputStream rlis = new RLEBitInputStream(mhis);
// rlis.setInvert(invert);
int off=0;
if((width&0x0007)==0){
byte[] buf=new byte[width>>3];int len=0;
while(true){
rlis.resetToStartCodeWord(); // start next line with white
mhis.readEOL(); // set settings for a new line
try{
len=rlis.read(buf); // read one image line
if(len==-1){break;} // end of page
System.arraycopy(buf,0,imgdata,off,len); // copy line to image buffer
}catch(ModHuffmanInputStream.ModHuffmanCodingException mhce){
System.out.println(getClass().getName()+".copyin:\n\t"+mhce);
}
off+=len;
}
}else{
byte[] buf=new byte[(width+7)>>3];int len=0,ecw=8-(width&0x0007),bits;
while(true){
rlis.resetToStartCodeWord(); // start next line with white
mhis.readEOL(); // set settings for a new line
try{
len=rlis.read(buf,0,buf.length-1); // read one image line
if(len==-1){break;} // end of page
bits=rlis.readBits(7,ecw);
buf[len]=(byte)bits;
System.arraycopy(buf,0,imgdata,off,len+1); // copy line to image buffer
}catch(ModHuffmanInputStream.ModHuffmanCodingException mhce){
System.out.println(getClass().getName()+".copyin:\n\t"+mhce);
}
off+=len+1;
}
}
}
}
/*
[1] Portable Document Format Reference Manual
ISBN 0-201-62628-4
1996
*/