/* * $Id: PDFImage.java,v 1.2 2007/08/26 18:56:35 gil1 Exp $ * * $Date: 2007/08/26 18:56:35 $ * * This library 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.1 of the License, or (at your option) any later version. * * This library 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 library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package gnu.jpdf; import java.awt.Image; import java.awt.image.*; import java.io.*; import java.util.*; import java.util.zip.*; /** *
This implements the Image XObject. Calling one of the
* drawImage methods of PDFGraphics will
* put all the necessary code into the pdf file, and the image will
* be encoded in ascii base 85, then deflated in zip format.
PDFImage instance.
*
*/
public PDFImage()
{
super("/XObject");
}
/**
* Creates a new PDFImage instance.
*
* @param img an Image value
*/
public PDFImage(Image img) {
this();
setImage(img,img.getWidth(this), img.getHeight(this), this);
}
/**
* Creates a new PDFImage instance.
*
* @param img an Image value
* @param x an int value
* @param y an int value
* @param w an int value
* @param h an int value
* @param obs an ImageObserver value
*/
public PDFImage(Image img,ImageObserver obs) {
this();
setImage(img, img.getWidth(this), img.getHeight(this), obs);
}
/**
* Get the value of width.
* @return value of width.
*/
public int getWidth() {
return width;
}
/**
* Set the value of width.
* @param v Value to assign to width.
*/
public void setWidth(int v) {
this.width = v;
}
/**
* Get the value of height.
* @return value of height.
*/
public int getHeight() {
return height;
}
/**
* Set the value of height.
* @param v Value to assign to height.
*/
public void setHeight(int v) {
this.height = v;
}
/**
* Set the name
*
* @param n a String value
*/
public void setName(String n) {
name = n;
}
/**
* Get the name
*
* @return a String value
*/
public String getName() {
return name;
}
/**
* Set the image
*
* @param img an Image value
* @param x an int value
* @param y an int value
* @param w an int value
* @param h an int value
* @param obs an ImageObserver value
*/
public void setImage(Image img,int w,int h,ImageObserver obs) {
this.img = img;
width = w;
height = h;
}
/**
* Adobe's base 85 does not follow the format used by ipv6 * addresses. It simply starts with 33 and goes straight up without * skipping any characters
* *Parts of this method contributed by Mathew Hreljac
* * @param stringToEncode aString value
* @return a String value
*/
private String base85Encoding(String stringToEncode)
throws NumberFormatException {
if ((stringToEncode == null) || (stringToEncode.length() == 0)) {
//System.out.println("PDFImage.base85Encoding() null or blank String");
return "";
}
if ((stringToEncode.length() > 8) ||
((stringToEncode.length() % 2) != 0)) {
System.out.println("PDFImage.base85Encoding, Incorrect tuple length: " +
stringToEncode.length());
return "";
}
//System.out.println("str: " + stringToEncode);
// String buffer to use to return the String encoding
StringBuffer sb = new StringBuffer();
// Deal with a partial tuple (less than 8 hex digits)
// From Adobe's docs:
// "Given n (1, 2 or 3) bytes of binary data, the encoding first
// appends 4 - n zero bytes to make a complete 4-tuple. This 4-tuple
// is encoded in the usual way, but without applying the special
// z-case. Finally, only the first n+1 characters of the resulting
// 5-tuple are written out. Those characters are immediately followed
// by the EOD marker, ~>"
int numHexDigits = stringToEncode.length() / 2;
int numAppendBytes = 4 - numHexDigits;
for (int i = 0; i < numAppendBytes; i++) {
stringToEncode += "00";
}
VectorOutputStream value
* @exception IOException if an error occurs
*/
public void writeStream(OutputStream os) throws IOException {
// This is a non-deflated stream
/*
os.write("/Length ".getBytes());
// Accout for stream\n ... >\nendstream
os.write(Integer.toString(buf.size() + 18).getBytes());
os.write("\n/Filter /ASCII85Decode".getBytes());
os.write("\n>>\nstream\n".getBytes());
buf.writeTo(os);
os.write(">\nendstream\nendobj\n\n".getBytes());
*/
ByteArrayOutputStream b = new ByteArrayOutputStream();
DeflaterOutputStream dos = new DeflaterOutputStream(b);
buf.writeTo(dos);
dos.finish();
dos.close();
// FlatDecode is compatible with the java.util.zip.Deflater class
//os.write("/Filter [/FlateDecode /ASCIIHexDecode]\n".getBytes());
os.write("/Filter [/FlateDecode /ASCII85Decode]\n".getBytes());
os.write("/Length ".getBytes());
os.write(Integer.toString(b.size()).getBytes());
os.write("\n>>\nstream\n".getBytes());
b.writeTo(os);
os.write("\nendstream\nendobj\n".getBytes());
} // end writeStream
/**
* Compression needs to be improved here
* * @param os OutputStream to send the object to * @exception IOException on error */ public void write(OutputStream os) throws IOException { writeStart(os); // write the extra details os.write("/Subtype /Image\n/Name ".getBytes()); os.write(name.getBytes()); os.write("\n/Width ".getBytes()); os.write(Integer.toString(width).getBytes()); os.write("\n/Height ".getBytes()); os.write(Integer.toString(height).getBytes()); os.write("\n/BitsPerComponent 8\n/ColorSpace /DeviceRGB\n".getBytes()); // write the pixels to the stream //System.err.println("Processing image "+width+"x"+height+" pixels"); ByteArrayOutputStream bos = getStream(); int w = width; int h = height; int x = 0; int y = 0; int[] pixels = new int[w * h]; PixelGrabber pg = new PixelGrabber(img, x, y, w, h, pixels, 0, w); try { pg.grabPixels(); } catch (InterruptedException e) { System.err.println("interrupted waiting for pixels!"); return; } if ((pg.getStatus() & ImageObserver.ABORT) != 0) { System.err.println("image fetch aborted or errored"); return; } StringBuffer out = new StringBuffer(); for (int j = 0; j < h; j++) { for (int i = 0; i < w; i++) { //System.out.print("p[" + j * w + i+ "]=" + pixels[j * w + i] + "."); out.append(handlePixel(x+i, y+j, pixels[j * w + i])); if (out.toString().length() >= 8) { String tuple = out.substring(0, 8); out.delete(0, 8); // Convert !!!!! to 'z' String encTuple = base85Encoding(tuple); if (encTuple.equals("!!!!!")) { encTuple = "z"; } bos.write(encTuple.getBytes()); } } } // This should be the only partial tuple case, String lastTuple = base85Encoding(out.toString()); //System.out.println("lastTuple: " + lastTuple); bos.write(lastTuple.getBytes()); bos.write("~".getBytes()); //System.out.println("Processing done"); // this will write the actual stream setDeflate(false); writeStream(os); // Note: we do not call writeEnd() on streams! } /** *Converts a pixel to a hex string
* * @param x anint value
* @param y an int value
* @param p an int value
* @return a String value
*/
public static String handlePixel(int x, int y, int p) {
int alpha = (p >> 24) & 0xff;
int red = (p >> 16) & 0xff;
int green = (p >> 8) & 0xff;
int blue = (p ) & 0xff;
String redHex = Integer.toHexString(red);
String greenHex = Integer.toHexString(green);
String blueHex = Integer.toHexString(blue);
if (redHex.length() == 1) {
redHex = "0" + redHex;
}
if (greenHex.length() == 1) {
greenHex = "0" + greenHex;
}
if (blueHex.length() == 1) {
blueHex = "0" + blueHex;
}
return redHex + greenHex + blueHex;
} // end handlePixel
/**
* Describe imageUpdate method here.
*
* @param img an Image value
* @param infoflags an int value
* @param x an int value
* @param y an int value
* @param w an int value
* @param h an int value
* @return a boolean value
*/
public boolean imageUpdate(Image img,int infoflags,int x,int y,int w,int h) {
System.err.println("img="+img+"\ninfoflags="+infoflags+
"\nx="+x+" y="+y+" w="+w+" h="+h);
//if(img == this.img) {
if(infoflags==ImageObserver.WIDTH)
width = w;
if(infoflags==ImageObserver.HEIGHT)
height = h;
//return true;
//}
return false;
}
} // end class PDFImage