KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > gnu > jpdf > PDFImage


1 /*
2  * $Id: PDFImage.java,v 1.4 2002/04/05 13:01:40 ezb Exp $
3  *
4  * $Date: 2002/04/05 13:01:40 $
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  */

20 package gnu.jpdf;
21
22 import java.awt.Image JavaDoc;
23 import java.awt.image.*;
24 import java.io.*;
25 import java.util.*;
26 import java.util.zip.*;
27
28 /**
29  * <p>This implements the Image XObject. Calling one of the
30  * <code>drawImage</code> methods of <code>PDFGraphics</code> will
31  * put all the necessary code into the pdf file, and the image will
32  * be encoded in ascii base 85, then deflated in zip format.</p>
33  *
34  * @author Eric Z. Beard (original version by Peter Mount)
35  * @author Matthew Hreljac, mhreljac@hotmail.com
36  * @author $Author: ezb $
37  * @version $Revision: 1.4 $, $Date: 2002/04/05 13:01:40 $
38  */

39 public class PDFImage extends PDFStream implements ImageObserver, Serializable
40 {
41   /*
42    * NOTE: The original class is the work of Peter T. Mount, who released it
43    * in the uk.org.retep.pdf package. It was modified by Eric Z. Beard as
44    * follows:
45    * The package name was changed to gnu.jpdf.
46    * The formatting was changed a little bit.
47    * Images were not yet implemented, so the core of this
48    * class was mostly rewritten
49    * It is still licensed under the LGPL.
50    * Got some help with base85 methods from Mathew Hreljac
51    */

52
53
54   // Dimensions of the object.
55
private int objwidth;
56   private int objheight;
57
58   // Dimensions of the image.
59
private int width;
60   private int height;
61   private Image JavaDoc img;
62   private String JavaDoc name;
63
64
65   /**
66    * Creates a new <code>PDFImage</code> instance.
67    *
68    */

69   public PDFImage()
70   {
71     super("/XObject");
72   }
73
74   /**
75    * Creates a new <code>PDFImage</code> instance.
76    *
77    * @param img an <code>Image</code> value
78    */

79   public PDFImage(Image JavaDoc img) {
80     this();
81     setImage(img, 0, 0, img.getWidth(this), img.getHeight(this), this);
82   }
83
84   /**
85    * Creates a new <code>PDFImage</code> instance.
86    *
87    * @param img an <code>Image</code> value
88    * @param x an <code>int</code> value
89    * @param y an <code>int</code> value
90    * @param w an <code>int</code> value
91    * @param h an <code>int</code> value
92    * @param obs an <code>ImageObserver</code> value
93    */

94   public PDFImage(Image JavaDoc img,int x,int y,int w,int h,ImageObserver obs) {
95     this();
96     objwidth = w;
97     objheight = h;
98     setImage(img, x, y, img.getWidth(this), img.getHeight(this), obs);
99   }
100
101
102   /**
103    * Get the value of width.
104    * @return value of width.
105    */

106   public int getWidth() {
107     return width;
108   }
109
110   /**
111    * Set the value of width.
112    * @param v Value to assign to width.
113    */

114   public void setWidth(int v) {
115     this.width = v;
116   }
117
118   /**
119    * Get the value of height.
120    * @return value of height.
121    */

122   public int getHeight() {
123     return height;
124   }
125
126   /**
127    * Set the value of height.
128    * @param v Value to assign to height.
129    */

130   public void setHeight(int v) {
131     this.height = v;
132   }
133
134
135   /**
136    * Set the name
137    *
138    * @param n a <code>String</code> value
139    */

140   public void setName(String JavaDoc n) {
141     name = n;
142   }
143
144   /**
145    * Get the name
146    *
147    * @return a <code>String</code> value
148    */

149   public String JavaDoc getName() {
150     return name;
151   }
152
153   /**
154    * Set the image
155    *
156    * @param img an <code>Image</code> value
157    * @param x an <code>int</code> value
158    * @param y an <code>int</code> value
159    * @param w an <code>int</code> value
160    * @param h an <code>int</code> value
161    * @param obs an <code>ImageObserver</code> value
162    */

163   public void setImage(Image JavaDoc img,int x,int y,int w,int h,ImageObserver obs) {
164     this.img = img;
165     width = w;
166     height = h;
167   }
168
169
170
171   /**
172    * <p>Adobe's base 85 does not follow the format used by ipv6
173    * addresses. It simply starts with 33 and goes straight up without
174    * skipping any characters</p>
175    *
176    * <p>Parts of this method contributed by Mathew Hreljac</p>
177    *
178    * @param stringToEncode a <code>String</code> value
179    * @return a <code>String</code> value
180    */

181   private String JavaDoc base85Encoding(String JavaDoc stringToEncode)
182                      throws NumberFormatException JavaDoc {
183     if ((stringToEncode == null) || (stringToEncode.length() == 0)) {
184       //System.out.println("PDFImage.base85Encoding() null or blank String");
185
return "";
186     }
187     if ((stringToEncode.length() > 8) ||
188     ((stringToEncode.length() % 2) != 0)) {
189       System.out.println("PDFImage.base85Encoding, Incorrect tuple length: " +
190                          stringToEncode.length());
191       return "";
192     }
193     //System.out.println("str: " + stringToEncode);
194
// String buffer to use to return the String encoding
195
StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
196
197     // Deal with a partial tuple (less than 8 hex digits)
198
// From Adobe's docs:
199
// "Given n (1, 2 or 3) bytes of binary data, the encoding first
200
// appends 4 - n zero bytes to make a complete 4-tuple. This 4-tuple
201
// is encoded in the usual way, but without applying the special
202
// z-case. Finally, only the first n+1 characters of the resulting
203
// 5-tuple are written out. Those characters are immediately followed
204
// by the EOD marker, ~>"
205

206     int numHexDigits = stringToEncode.length() / 2;
207     int numAppendBytes = 4 - numHexDigits;
208     for (int i = 0; i < numAppendBytes; i++) {
209       stringToEncode += "00";
210     }
211     Vector digitVector = new Vector();
212     long number = Long.parseLong(stringToEncode, 16);
213     int remainder = 0;
214
215     while (number >= 85) {
216       remainder = (int) (number % 85);
217       number = number / 85;
218       digitVector.add( 0, new Integer JavaDoc( remainder ) );
219     }
220     digitVector.add( 0, new Integer JavaDoc( (int)number ) );
221
222     for ( int i = 0; i < digitVector.size(); i++) {
223       char c = (char) (((Integer JavaDoc)digitVector.elementAt(i)).intValue() + 33);
224       sb.append(c);
225     }
226     String JavaDoc tuple = sb.toString();
227     int len = tuple.length();
228     switch (len) {
229     case 1: tuple = "!!!!" + tuple; break;
230     case 2: tuple = "!!!" + tuple; break;
231     case 3: tuple = "!!" + tuple; break;
232     case 4: tuple = "!" + tuple; break;
233     default: break;
234     } // end switch
235
//System.out.println("enc tuple: " + tuple);
236

237     return (tuple);
238   } // end base85encoding
239

240
241
242   /**
243    * Writes the image to the stream
244    *
245    * @param os an <code>OutputStream</code> value
246    * @exception IOException if an error occurs
247    */

248   public void writeStream(OutputStream os) throws IOException {
249     // This is a non-deflated stream
250
/*
251     os.write("/Length ".getBytes());
252     // Accout for stream\n ... >\nendstream
253     os.write(Integer.toString(buf.size() + 18).getBytes());
254     os.write("\n/Filter /ASCII85Decode".getBytes());
255     os.write("\n>>\nstream\n".getBytes());
256     buf.writeTo(os);
257     os.write(">\nendstream\nendobj\n\n".getBytes());
258     */

259     ByteArrayOutputStream b = new ByteArrayOutputStream();
260     DeflaterOutputStream dos = new DeflaterOutputStream(b);
261     buf.writeTo(dos);
262     dos.finish();
263     dos.close();
264
265     // FlatDecode is compatible with the java.util.zip.Deflater class
266
//os.write("/Filter [/FlateDecode /ASCIIHexDecode]\n".getBytes());
267
os.write("/Filter [/FlateDecode /ASCII85Decode]\n".getBytes());
268     os.write("/Length ".getBytes());
269     os.write(Integer.toString(b.size()).getBytes());
270     os.write("\n>>\nstream\n".getBytes());
271     b.writeTo(os);
272     os.write("\nendstream\nendobj\n".getBytes());
273
274   } // end writeStream
275

276
277   /**
278    * <p>Compression needs to be improved here</p>
279    *
280    * @param os OutputStream to send the object to
281    * @exception IOException on error
282    */

283   public void write(OutputStream os) throws IOException
284   {
285     writeStart(os);
286
287     // write the extra details
288
os.write("/Subtype /Image\n/Name ".getBytes());
289     os.write(name.getBytes());
290     os.write("\n/Width ".getBytes());
291     os.write(Integer.toString(width).getBytes());
292     os.write("\n/Height ".getBytes());
293     os.write(Integer.toString(height).getBytes());
294     os.write("\n/BitsPerComponent 8\n/ColorSpace /DeviceRGB\n".getBytes());
295
296     // write the pixels to the stream
297
//System.err.println("Processing image "+width+"x"+height+" pixels");
298
ByteArrayOutputStream bos = getStream();
299
300     int w = width;
301     int h = height;
302     int x = 0;
303     int y = 0;
304     int[] pixels = new int[w * h];
305     PixelGrabber pg = new PixelGrabber(img, x, y, w, h, pixels, 0, w);
306     try {
307         pg.grabPixels();
308     } catch (InterruptedException JavaDoc e) {
309         System.err.println("interrupted waiting for pixels!");
310         return;
311     }
312     if ((pg.getStatus() & ImageObserver.ABORT) != 0) {
313         System.err.println("image fetch aborted or errored");
314         return;
315     }
316     StringBuffer JavaDoc out = new StringBuffer JavaDoc();
317     for (int j = 0; j < h; j++) {
318       for (int i = 0; i < w; i++) {
319         //System.out.print("p[" + j * w + i+ "]=" + pixels[j * w + i] + ".");
320
out.append(handlePixel(x+i, y+j, pixels[j * w + i]));
321         if (out.toString().length() >= 8) {
322           String JavaDoc tuple = out.substring(0, 8);
323           out.delete(0, 8);
324           // Convert !!!!! to 'z'
325
String JavaDoc encTuple = base85Encoding(tuple);
326           if (encTuple.equals("!!!!!")) {
327             encTuple = "z";
328           }
329           bos.write(encTuple.getBytes());
330         }
331       }
332     }
333     // This should be the only partial tuple case,
334

335     String JavaDoc lastTuple = base85Encoding(out.toString());
336     //System.out.println("lastTuple: " + lastTuple);
337
bos.write(lastTuple.getBytes());
338     bos.write("~".getBytes());
339
340
341     //System.out.println("Processing done");
342

343     // this will write the actual stream
344
setDeflate(false);
345
346     writeStream(os);
347
348     // Note: we do not call writeEnd() on streams!
349
}
350
351
352
353   /**
354    * <p>Converts a pixel to a hex string</p>
355    *
356    * @param x an <code>int</code> value
357    * @param y an <code>int</code> value
358    * @param p an <code>int</code> value
359    * @return a <code>String</code> value
360    */

361   public static String JavaDoc handlePixel(int x, int y, int p) {
362     int alpha = (p >> 24) & 0xff;
363     int red = (p >> 16) & 0xff;
364     int green = (p >> 8) & 0xff;
365     int blue = (p ) & 0xff;
366     String JavaDoc redHex = Integer.toHexString(red);
367     String JavaDoc greenHex = Integer.toHexString(green);
368     String JavaDoc blueHex = Integer.toHexString(blue);
369     if (redHex.length() == 1) {
370       redHex = "0" + redHex;
371     }
372     if (greenHex.length() == 1) {
373       greenHex = "0" + greenHex;
374     }
375     if (blueHex.length() == 1) {
376       blueHex = "0" + blueHex;
377     }
378     return redHex + greenHex + blueHex;
379   } // end handlePixel
380

381
382
383
384
385   /**
386    * Describe <code>imageUpdate</code> method here.
387    *
388    * @param img an <code>Image</code> value
389    * @param infoflags an <code>int</code> value
390    * @param x an <code>int</code> value
391    * @param y an <code>int</code> value
392    * @param w an <code>int</code> value
393    * @param h an <code>int</code> value
394    * @return a <code>boolean</code> value
395    */

396   public boolean imageUpdate(Image JavaDoc img,int infoflags,int x,int y,int w,int h) {
397     System.err.println("img="+img+"\ninfoflags="+infoflags+
398                "\nx="+x+" y="+y+" w="+w+" h="+h);
399     //if(img == this.img) {
400
if(infoflags==ImageObserver.WIDTH)
401       width = w;
402     if(infoflags==ImageObserver.HEIGHT)
403       height = h;
404
405     //return true;
406
//}
407
return false;
408   }
409
410 } // end class PDFImage
411
Popular Tags