Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/jfor/jfor/rtflib/rtfdoc/RtfExternalGraphic.java


1   package org.jfor.jfor.rtflib.rtfdoc;
2   
3   import org.jfor.jfor.rtflib.rtfdoc.RtfElement;
4   import org.jfor.jfor.rtflib.rtfdoc.RtfContainer;
5   import org.jfor.jfor.rtflib.rtfdoc.RtfAttributes;
6   
7   import org.jfor.jfor.tools.ImageConstants;
8   import org.jfor.jfor.tools.ImageUtil;
9   import org.jfor.jfor.tools.jpeg.JpegEncoderFactory;
10  import org.jfor.jfor.tools.jpeg.JPEGException;
11  import org.jfor.jfor.tools.jpeg.IJpegEncoder;
12  
13  import java.io.BufferedInputStream;
14  import java.io.BufferedOutputStream;
15  import java.io.InputStreamReader;
16  
17  import java.io.ByteArrayOutputStream;
18  import java.io.InputStream;
19  import java.io.IOException;
20  import java.io.Writer;
21  
22  import java.io.File;
23  import java.net.URL;
24  import java.net.MalformedURLException;
25  import java.util.Hashtable;
26  
27  /*-----------------------------------------------------------------------------
28   * jfor - Open-Source XSL-FO to RTF converter - see www.jfor.org
29   *
30   * ====================================================================
31   * jfor Apache-Style Software License.
32   * Copyright (c) 2002 by the jfor project. All rights reserved.
33   *
34   * Redistribution and use in source and binary forms, with or without
35   * modification, are permitted provided that the following conditions
36   * are met:
37   *
38   * 1. Redistributions of source code must retain the above copyright
39   * notice, this list of conditions and the following disclaimer.
40   *
41   * 2. Redistributions in binary form must reproduce the above copyright
42   * notice, this list of conditions and the following disclaimer in
43   * the documentation and/or other materials provided with the
44   * distribution.
45   *
46   * 3. The end-user documentation included with the redistribution,
47   * if any, must include the following acknowledgment:
48   * "This product includes software developed
49   * by the jfor project (http://www.jfor.org)."
50   * Alternately, this acknowledgment may appear in the software itself,
51   * if and wherever such third-party acknowledgments normally appear.
52   *
53   * 4. The name "jfor" must not be used to endorse
54   * or promote products derived from this software without prior written
55   * permission.  For written permission, please contact info@jfor.org.
56   *
57   * 5. Products derived from this software may not be called "jfor",
58   * nor may "jfor" appear in their name, without prior written
59   * permission of info@jfor.org.
60   *
61   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
62   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
63   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
64   * DISCLAIMED.  IN NO EVENT SHALL THE JFOR PROJECT OR ITS CONTRIBUTORS BE
65   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
66   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
67   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
68   * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
69   * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
70   * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
71   * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
72   * ====================================================================
73   * Contributor(s):
74   *  @author Gianugo Rabellino gianugo@rabellino.it
75  -----------------------------------------------------------------------------*/
76  
77  /**
78   * Creates an RTF image from an external graphic file.
79   * This class belongs to the <fo:external-graphic> tag processing. <br>
80   *
81   * Supports relative path like "../test.gif", too (01-08-24) <br>
82   *
83   * Limitations:
84   * <li>    Only the image types PNG, JPEG and EMF are supported
85   * <li>    The GIF is supported, too, but will be converted to JPG
86   * <li>    Only the attributes SRC (required), WIDTH, HEIGHT, SCALING are supported
87   * <li>    The SCALING attribute supports (uniform | non-uniform)
88   *
89   * Known Bugs:
90   * <li>    If the emf image has a desired size, the image will be clipped
91   * <li>    The emf, jpg & png image will not be displayed in correct size
92   *
93   *  @author <a href="mailto:a.putz@skynamics.com">Andreas Putz</a>
94   */
95  
96  public class RtfExternalGraphic extends RtfElement
97  {
98    /** Exception thrown when an image file/URL cannot be read */
99    public static class ExternalGraphicException extends IOException
100   {
101     ExternalGraphicException(String reason)
102     {
103       super(reason);
104     }
105   }
106 
107   //////////////////////////////////////////////////
108   // @@ Members
109   //////////////////////////////////////////////////
110 
111 
112   /**
113    * The url of the image
114    */
115   protected URL url = null;
116 
117   /**
118    * The height of the image
119    */
120   protected int height = -1;
121 
122   /**
123    * The desired percent value of the height
124    */
125   protected int heightPercent = -1;
126 
127   /**
128    * The desired height
129    */
130   protected int heightDesired = -1;
131 
132   /**
133    * Flag whether the desired height is a percentage
134    */
135   protected boolean perCentH = false;
136 
137   /**
138    * The width of the image
139    */
140   protected int width = -1;
141 
142   /**
143    * The desired percent value of the width
144    */
145   protected int widthPercent = -1;
146 
147   /**
148    * The desired width
149    */
150   protected int widthDesired = -1;
151 
152   /**
153    * Flag whether the desired width is a percentage
154    */
155   protected boolean perCentW = false;
156 
157   /**
158    * Flag whether the image size shall be adjusted
159    */
160   protected boolean scaleUniform = false;
161 
162   /**
163    * Graphic compression rate
164    */
165    protected int graphicCompressionRate = 80;
166 
167 
168   //////////////////////////////////////////////////
169   // @@ Construction
170   //////////////////////////////////////////////////
171 
172 
173   /**
174    * Default constructor.
175    * Create an RTF element as a child of given container.
176    *
177    * @param container a <code>RtfContainer</code> value
178    * @param writer a <code>Writer</code> value
179    */
180   public RtfExternalGraphic(RtfContainer container, Writer writer) throws IOException {
181     super (container, writer);
182   }
183 
184   /**
185    * Default constructor.
186    *
187    * @param container a <code>RtfContainer</code> value
188    * @param writer a <code>Writer</code> value
189    * @param attributes a <code>RtfAttributes</code> value
190    */
191   public RtfExternalGraphic(RtfContainer container, Writer writer,
192   RtfAttributes attributes) throws IOException {
193     super (container, writer, attributes);
194   }
195 
196 
197   //////////////////////////////////////////////////
198   // @@ RtfElement implementation
199   //////////////////////////////////////////////////
200 
201     /** RtfElement override - catches ExternalGraphicException and writes a warning
202      *  message to the document if image cannot be read
203      */
204   protected void writeRtfContent() throws IOException {
205       try {
206         writeRtfContentWithException();
207       } catch(ExternalGraphicException ie) {
208         writeExceptionInRtf(ie);
209       }
210     }
211 
212   /**
213    * Writes the RTF content to m_writer - this one throws ExternalGraphicExceptions
214    *
215    * @exception IOException On error
216    */
217   protected void writeRtfContentWithException() throws IOException {
218 
219     if (m_writer == null) {
220       return;
221     }
222 
223 
224     if (url == null) {
225       throw new ExternalGraphicException("The attribute 'url' of <fo:external-graphic> is null.");
226     }
227 
228         String linkToRoot = System.getProperty( "jfor_link_to_root" );
229         if (linkToRoot != null)
230         {
231             m_writer.write( "{\\field {\\* \\fldinst { INCLUDEPICTURE \"" );
232             m_writer.write( linkToRoot );
233             File urlFile = new File( url.getFile() );
234             m_writer.write( urlFile.getName() );
235             m_writer.write( "\" \\\\* MERGEFORMAT \\\\d }}}" );
236             return;
237         }
238 
239     getRtfFile ().getLog ().logInfo ("Writing image '" + url + "'.");
240 
241 
242     byte[] data = null;
243     try
244     {
245     // image reading patch provided by Michael Krause <michakurt@web.de>
246     final BufferedInputStream bin = new BufferedInputStream(url.openStream());
247     final ByteArrayOutputStream bout = new ByteArrayOutputStream();
248             while (true) {
249               final int datum = bin.read();
250                if (datum == -1) break;
251                 bout.write(datum);
252              }
253              bout.flush();
254              data = bout.toByteArray();
255     }
256     catch (Exception e) {
257       throw new ExternalGraphicException("The attribute 'src' of <fo:external-graphic> has a invalid value: '" + url + "' (" + e + ")");
258     }
259 
260     if (data == null) {
261       return;
262     }
263 
264     // Determine image file format
265     String file = url.getFile ();
266     int type = determineImageType(data, file.substring(file.lastIndexOf(".") + 1));
267 
268     if (type >= ImageConstants.I_TO_CONVERT_BASIS) {
269       // convert
270       int to = ImageConstants.CONVERT_TO [type - ImageConstants.I_TO_CONVERT_BASIS];
271 
272       if (to == ImageConstants.I_JPG) {
273         try {
274           //convert to jpeg
275           final IJpegEncoder jpgEncoder = new JpegEncoderFactory().getEncoder();
276                     data = jpgEncoder.encodeJPEG(graphicCompressionRate,data);
277           type = to;
278         }
279         catch (JPEGException e) {
280                     throw new IOException("JPEG conversion error, src = '" + url + "' (" + e + ")");
281         }
282       }
283       else {
284         type = ImageConstants.I_NOT_SUPPORTED;
285       }
286     }
287 
288 
289     if (type == ImageConstants.I_NOT_SUPPORTED) {
290       throw new ExternalGraphicException("The tag <fo:external-graphic> does not support " + file.substring(file.lastIndexOf(".") + 1) + " - image type.");
291     }
292 
293     String rtfImageCode = ImageConstants.RTF_TAGS [type];
294 
295     // Writes the beginning of the rtf image
296 
297     writeGroupMark(true);
298     writeStarControlWord("shppict");
299     writeGroupMark(true);
300     writeControlWord("pict");
301 
302     StringBuffer buf = new StringBuffer(data.length * 3);
303 
304     writeControlWord(rtfImageCode);
305 
306     if (type == ImageConstants.I_PNG) {
307       width = ImageUtil.getIntFromByteArray(data, 16, 4, true);
308       height = ImageUtil.getIntFromByteArray(data, 20, 4, true);
309     }
310     else if (type == ImageConstants.I_JPG) {
311       int basis = -1;
312       byte ff = (byte) 0xff;
313       byte c0 = (byte) 0xc0;
314       for (int i = 0; i < data.length; i++) {
315         byte b = data[i];
316         if (b != ff)
317           continue;
318         if (i == data.length - 1)
319           continue;
320         b = data[i + 1];
321         if (b != c0)
322           continue;
323         basis = i + 5;
324         break;
325       }
326 
327       if (basis != -1) {
328         width = ImageUtil.getIntFromByteArray(data, basis + 2, 2, true);
329         height = ImageUtil.getIntFromByteArray(data, basis, 2, true);
330       }
331     }
332     else if (type == ImageConstants.I_EMF) {
333       width = ImageUtil.getIntFromByteArray(data, 151, 4, false);
334       height = ImageUtil.getIntFromByteArray(data, 155, 4, false);
335     }
336 
337     // Set image size
338     if (width != -1) {
339       writeControlWord("picw" + width);
340     }
341     if (height != -1) {
342       writeControlWord("pich" + height);
343     }
344 
345     if (widthDesired != -1) {
346       if (perCentW) {
347         writeControlWord("picscalex" + widthDesired);
348       }
349       else {
350         writeControlWord("picscalex" + widthDesired * 100 / width);
351       }
352 
353       writeControlWord("picwgoal" + widthDesired);
354     }
355     else if (scaleUniform && heightDesired != -1) {
356       if (perCentH) {
357         writeControlWord("picscalex" + heightDesired);
358       }
359       else {
360         writeControlWord("picscalex" + heightDesired * 100 / height);
361       }
362     }
363 
364     if (heightDesired != -1) {
365       if (perCentH) {
366         writeControlWord("picscaley" + heightDesired);
367       }
368       else {
369         writeControlWord("picscaley" + heightDesired * 100 / height);
370       }
371 
372       writeControlWord("pichgoal" + heightDesired);
373     }
374     else if (scaleUniform && widthDesired != -1) {
375       if (perCentW) {
376         writeControlWord("picscaley" + widthDesired);
377       }
378       else {
379         writeControlWord("picscaley" + widthDesired * 100 / width);
380       }
381     }
382 
383     for (int i = 0; i < data.length; i++) {
384       int iData = data [i];
385 
386       // Make positive byte
387       if (iData < 0)
388         iData += 256;
389 
390       if (iData < 16) {
391         // Set leading zero and append
392         buf.append('0');
393       }
394 
395       buf.append(Integer.toHexString(iData));
396     }
397 
398     int len = buf.length();
399     char[] chars = new char[len];
400 
401     buf.getChars(0, len, chars, 0);
402     m_writer.write(chars);
403 
404     // Writes the end of RTF image
405 
406     writeGroupMark(false);
407     writeGroupMark(false);
408   }
409 
410 
411   //////////////////////////////////////////////////
412   // @@ Member access
413   //////////////////////////////////////////////////
414 
415   /**
416    * Sets the desired height of the image.
417    *
418    * @param theHeight The desired image height
419    */
420   public void setHeight(String theHeight) {
421     this.heightDesired = ImageUtil.getInt(theHeight);
422     this.perCentH = ImageUtil.isPercent(theHeight);
423   }
424 
425   /**
426    * Sets the desired width of the image.
427    *
428    * @param theWidth The desired image width
429    */
430   public void setWidth(String theWidth) {
431     this.widthDesired = ImageUtil.getInt(theWidth);
432     this.perCentW = ImageUtil.isPercent(theWidth);
433   }
434 
435   /**
436    * Sets the flag whether the image size shall be adjusted.
437    *
438    * @param value
439    * true    image width or height shall be adjusted automatically\n
440    * false   no adjustment
441    */
442   public void setScaling(String value) {
443     if (value.equalsIgnoreCase("uniform")) {
444       this.scaleUniform = true;
445     }
446   }
447 
448   /**
449    * Sets the url of the image.
450    *
451    * @param urlString Image url like "file://..."
452    * @throws IOException On error
453    */
454   public void setURL(String urlString) throws IOException
455   {
456     URL tmpUrl = null;
457     try
458     {
459       tmpUrl = new URL (urlString);
460     }
461     catch (MalformedURLException e)
462     {
463       try
464       {
465         tmpUrl = new File (urlString).toURL ();
466       }
467       catch (MalformedURLException ee)
468       {
469         throw new ExternalGraphicException("The attribute 'src' of <fo:external-graphic> has a invalid value: '" + urlString + "' (" + ee + ")");
470       }
471     }
472     this.url = tmpUrl;
473   }
474 
475   /**
476    * Gets  the compression rate for the image in percent.
477    * @return Compression rate
478    */
479   public int getCompressionRate ()
480   {
481     return graphicCompressionRate;
482   }
483 
484   /**
485    * Sets the compression rate for the image in percent.
486    *
487    * @param percent Compression rate
488    * @return
489    *  true:   The compression rate is valid (0..100)\n
490    *  false:  The compression rate is invalid
491    */
492   public boolean setCompressionRate (int percent)
493   {
494     if (percent < 1 || percent > 100)
495       return false;
496 
497     graphicCompressionRate = percent;
498     return true;
499   }
500 
501 
502   //////////////////////////////////////////////////
503   // @@ Helpers
504   //////////////////////////////////////////////////
505 
506 
507   /**
508    * Determines wheter the image is a jpeg.
509    *
510    * @param data Image
511    *
512    * @return
513    * true    If JPEG type\n
514    * false   Other type
515    */
516   private boolean isJPEG(byte[] data) {
517     // Indentifier "0xFFD8" on position 0
518     byte [] pattern = new byte [] {(byte) 0xFF, (byte) 0xD8};
519 
520     return ImageUtil.compareHexValues(pattern, data, 0, true);
521   }
522 
523   /**
524    * Determines wheter the image is a png.
525    *
526    * @param data Image
527    *
528    * @return
529    * true    If PNG type\n
530    * false   Other type
531    */
532   private boolean isPNG(byte[] data) {
533     // Indentifier "PNG" on position 1
534     byte [] pattern = new byte [] {(byte) 0x50, (byte) 0x4E, (byte) 0x47};
535 
536     return ImageUtil.compareHexValues(pattern, data, 1, true);
537   }
538 
539   /**
540    * Determines wheter the image is a emf.
541    *
542    * @param data Image
543    *
544    * @return
545    * true    If EMF type\n
546    * false   Other type
547    */
548   private boolean isEMF(byte[] data) {
549     // No offical Indentifier known
550     byte [] pattern = new byte [] {(byte) 0x01, (byte) 0x00, (byte) 0x00};
551 
552     return ImageUtil.compareHexValues(pattern, data, 0, true);
553   }
554 
555   /**
556    * Determines wheter the image is a gif.
557    *
558    * @param data Image
559    *
560    * @return
561    * true    If GIF type\n
562    * false   Other type
563    */
564   private boolean isGIF(byte[] data) {
565     // Indentifier "GIF8" on position 0
566     byte [] pattern = new byte [] {(byte) 0x47, (byte) 0x49, (byte) 0x46, (byte) 0x38};
567 
568     return ImageUtil.compareHexValues(pattern, data, 0, true);
569   }
570 
571   /**
572    * Determines wheter the image is a gif.
573    *
574    * @param data Image
575    *
576    * @return
577    * true    If BMP type\n
578    * false   Other type
579    */
580   private boolean isBMP(byte[] data) {
581     // Indentifier "BM" on position 0
582     byte [] pattern = new byte [] {(byte) 0x42, (byte) 0x4D};
583 
584     return ImageUtil.compareHexValues(pattern, data, 0, true);
585   }
586 
587   /**
588    * Determine image file format.
589    *
590    * @param data Image
591    * @param ext Image extension
592    *
593    * @return Image type by ImageConstants.java
594    */
595   private int determineImageType(byte [] data, String ext) {
596     int type = ImageConstants.I_NOT_SUPPORTED;
597 
598     if (isPNG(data)) {
599       type = ImageConstants.I_PNG;
600     }
601     else if (isJPEG(data)) {
602       type = ImageConstants.I_JPG_C;
603     }
604     else if (isEMF(data)) {
605       type = ImageConstants.I_EMF;
606     }
607     else if (isGIF(data)) {
608       type = ImageConstants.I_GIF;
609     }
610     else {
611       Object tmp = ImageConstants.SUPPORTED_IMAGE_TYPES.get(ext.toLowerCase());
612       if (tmp != null) {
613         type = ((Integer) tmp).intValue();
614       }
615     }
616 
617     return type;
618   }
619 
620   /** true if this element would generate no "useful" RTF content */
621   public boolean isEmpty()
622   {
623     return url==null;
624   }
625 }