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

Quick Search    Search Deep

Source code: org/apache/batik/apps/rasterizer/SVGConverter.java


1   /*
2   
3      Copyright 2001-2003  The Apache Software Foundation 
4   
5      Licensed under the Apache License, Version 2.0 (the "License");
6      you may not use this file except in compliance with the License.
7      You may obtain a copy of the License at
8   
9          http://www.apache.org/licenses/LICENSE-2.0
10  
11     Unless required by applicable law or agreed to in writing, software
12     distributed under the License is distributed on an "AS IS" BASIS,
13     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14     See the License for the specific language governing permissions and
15     limitations under the License.
16  
17   */
18  package org.apache.batik.apps.rasterizer;
19  
20  import java.awt.Color;
21  import java.awt.geom.Rectangle2D;
22  import java.io.File;
23  import java.io.FileFilter;
24  import java.io.FileNotFoundException;
25  import java.io.FileOutputStream;
26  import java.io.IOException;
27  import java.io.InputStream;
28  import java.io.OutputStream;
29  import java.util.HashMap;
30  import java.util.Map;
31  import java.util.Vector;
32  
33  import org.apache.batik.transcoder.Transcoder;
34  import org.apache.batik.transcoder.TranscoderInput;
35  import org.apache.batik.transcoder.TranscoderOutput;
36  import org.apache.batik.transcoder.image.ImageTranscoder;
37  import org.apache.batik.transcoder.image.JPEGTranscoder;
38  import org.apache.batik.transcoder.image.PNGTranscoder;
39  
40  /**
41   * This application can be used to convert SVG images to raster images.
42   * <br />
43   * Possible result raster image formats are PNG, JPEG, TIFF, and PDF. 
44   * The Batik Transcoder API is used to execute the conversion. FOP is 
45   * needed to be able to transcode to the PDF format<br />
46   *
47   * The source has to be list of files or URL (set by the <tt>setSources</tt> 
48   * method). <br />
49   *
50   * The destination can be:<br /><ul>
51   * <li><b>unspecified</b>. In that case, only file sources can be converted and 
52   * a file in the same directory as the source will be created.</li>
53   * <li><b>a directory</b>, set by the <tt>setDst</tt> method. In that case,
54   * the output files are created in that destination directory</li>
55   * <li><b>a file</b>. In case there is a <i>single 
56   * source</i>, the destination can be a single named file 
57   * (set with the <tt>setDst</tt> method.</li>)<br />
58   * </ul>
59   *
60   * <hr />
61   *
62   * There are a number of options which control the way the image is
63   * converted to the destination format:<br /><ul>
64   * <li>destinationType: controls the type of conversion which should be done. 
65   *     see the {@link DestinationType} documentation.</li>
66   * <li>width/height: they control the desired width and height, in user space,
67   *     for the output image.</li>
68   * <li>maxWidth/maxHeight: control the maximum width and height, 
69   *     in user space, of the output image.</li>
70   * <li>area: controls the specific sub-area of the image which should be 
71   *     rendered.</li>
72   * <li>backgroundColor: controls the color which is used to fill the 
73   *     background before rendering the image</li>
74   * <li>quality: relevant only for JPEG destinations, this controls the 
75   *     encoding quality.</li>
76   * <li>indexed: relevant only for PNG, controls the number of bits
77   *              used in writting of a palletized files.</li>
78   * <li>mediaType: controls the CSS media, or list of media, for which the 
79   *     image should be rendered.</li>
80   * <li>alternate: controls the alternate CSS stylesheet to activate, 
81   *     if any.</li>
82   * <li>language: controls the user language with which the SVG document 
83   *     should be converted.</li>
84   * <li>userStylesheet: defines the user stylesheet to apply to SVG documents 
85   *     in addition to other stylesheets referenced by or embedded in the 
86   *     SVG documents.</li>
87   * <li>pixelUnitToMillimeter: defines the size of a pixel in millimeters 
88   *     to use when processing the SVG documents.</li>
89   * </ul>
90   *
91   * @version $Id: SVGConverter.java,v 1.22 2005/03/27 08:58:29 cam Exp $
92   * @author <a href="mailto:Henri.Ruini@nokia.com">Henri Ruini</a>
93   * @author <a href="mailto:vhardy@apache.org">Vincent Hardy</a>
94   */
95  public class SVGConverter {
96      // 
97      // Error codes reported by the SVGConverter
98      //
99  
100     //
101     // Reported when no source file has been specified.
102     //
103     public static final String ERROR_NO_SOURCES_SPECIFIED
104         = "SVGConverter.error.no.sources.specified";
105 
106     //
107     // Reported when there is more than one valid input source
108     // and no output directory has been set and the source is
109     // not a file.
110     //
111     public static final String ERROR_CANNOT_COMPUTE_DESTINATION
112         = "SVGConverter.error.cannot.compute.destination";
113 
114     //
115     // Reported when the dst is a file and there are multiple 
116     // sources.
117     //
118     public static final String ERROR_CANNOT_USE_DST_FILE
119         = "SVGConverter.error.cannot.use.dst.file";
120 
121     //
122     // Reported when the <tt>Transcoder</tt> for the requested
123     // <tt>destinationType</tt> cannot be found.
124     //
125     public static final String ERROR_CANNOT_ACCESS_TRANSCODER 
126         = "SVGConverter.error.cannot.access.transcoder";
127 
128     //
129     // Reported when the source is found to be the same as
130     // the destination. Note that it is not guaranteed that
131     // this error condition will always be detected.
132     //
133     public static final String ERROR_SOURCE_SAME_AS_DESTINATION
134         = "SVGConverter.error.source.same.as.destination";
135 
136     //
137     // Reported when one of the sources cannot be read.
138     //
139     public static final String ERROR_CANNOT_READ_SOURCE
140         = "SVGConverter.error.cannot.read.source";
141 
142     //
143     // Reported when an error happens while opening a source
144     // file.
145     //
146     public static final String ERROR_CANNOT_OPEN_SOURCE
147         = "SVGConverter.error.cannot.open.source";
148 
149     //
150     // Reported if the output is not writeable. This may 
151     // happen if the output file already exists and does not
152     // have write permission.
153     //
154     public static final String ERROR_OUTPUT_NOT_WRITEABLE
155         = "SVGConverter.error.output.not.writeable";
156 
157     //
158     // Reported when an error happens while trying to open
159     // the output file for writing.
160     //
161     public static final String ERROR_CANNOT_OPEN_OUTPUT_FILE
162         = "SVGConverter.error.cannot.open.output.file";
163 
164     //
165     // Reported when the converter was not able to create 
166     // the destination directory for the files.
167     //
168     public static final String ERROR_UNABLE_TO_CREATE_OUTPUT_DIR
169         = "SVGConverter.error.unable.to.create.output.dir";
170 
171     //
172     // Reported when an error occurs while convertion the 
173     // source file.
174     //
175     public static final String ERROR_WHILE_RASTERIZING_FILE
176         = "SVGConverter.error.while.rasterizing.file";
177 
178     //
179     // Class variables and constants 
180     //
181 
182     /** SVG file extension */
183     protected static final String SVG_EXTENSION = ".svg";
184 
185     /** Default quality value.  A value of -1 means disabled. */
186     protected static final float DEFAULT_QUALITY 
187         = -1f;
188 
189     /** Maximum quality value */
190     protected static final float MAXIMUM_QUALITY
191         = .99F;
192 
193     /** Default result type */
194     protected static final DestinationType DEFAULT_RESULT_TYPE 
195         = DestinationType.PNG;
196 
197     /** Default width */
198     protected static final float DEFAULT_WIDTH = -1;
199 
200     /** Default height */
201     protected static final float DEFAULT_HEIGHT = -1;
202 
203     /** Result type */
204     protected DestinationType destinationType = DEFAULT_RESULT_TYPE;
205 
206     /** Output image height. */
207     protected float height = DEFAULT_HEIGHT;
208 
209     /** Output image width. */
210     protected float width = DEFAULT_WIDTH;
211 
212     /** Maximum output image height. */
213     protected float maxHeight = DEFAULT_HEIGHT;
214 
215     /** Maximum output image width. */
216     protected float maxWidth = DEFAULT_WIDTH;
217 
218     /** Output image quality. */
219     protected float quality = DEFAULT_QUALITY;
220 
221     /** Should output Image be indexed . */
222     protected int indexed = -1;
223 
224     /** Output AOI area. */
225     protected Rectangle2D area = null;
226 
227     /** Language */
228     protected String language = null;
229 
230     /** User stylesheet */
231     protected String userStylesheet = null;
232 
233     /** Millimeters Per Pixel */
234     protected float pixelUnitToMillimeter = -1f;
235 
236     /** Validation flag */
237     protected boolean validate = false;
238 
239     /** Execute the 'onload' scripts flag */
240     protected boolean executeOnload = false;
241 
242     /** Set of allowed script types. */
243     protected String allowedScriptTypes = null;
244 
245     /** Controls whether scripts can only have the same origin as 
246         the document which references them. */
247     protected boolean constrainScriptOrigin = true;
248 
249     /** Controls whether scripts should be run securely or not */
250     protected boolean securityOff = false;
251 
252     /** Sources files or URLs */
253     protected Vector sources = null;
254 
255     /** 
256      * Destination image path. Can be a file (for single source) or
257      *  a directory 
258      */
259     protected File dst;
260 
261     /** Background color for the output images. */
262     protected Color backgroundColor = null;
263 
264     /** Media type for which the SVG image should be rendered */
265     protected String mediaType = null;
266 
267     /** Default value for the font-family when it is unspecified */
268     protected String defaultFontFamily = null;
269 
270     /** Alternate stylesheet for which should be applied to the SVG */
271     protected String alternateStylesheet = null;
272 
273     /** Contents of <tt>fileset</tt> elements. */
274     protected Vector files = new Vector();
275 
276     /**
277      * Controls some aspects of the converter's operation,
278      *  such as whether or not it should proceed in some
279      *  error situations. See {@link SVGConverterController}
280      */
281     protected SVGConverterController controller;
282 
283     //
284     // Default constructor
285     //
286     public SVGConverter(){
287         this(new DefaultSVGConverterController());
288     }
289 
290     //
291     // Constructor
292     //
293     public SVGConverter(SVGConverterController controller){
294         if (controller == null){
295             throw new IllegalArgumentException();
296         }
297 
298         this.controller = controller;
299     }
300 
301     // 
302     // Property get/set methods 
303     // 
304 
305     /**
306      * Sets the <tt>destinationType</tt> attribute value. 
307      * Should not be null.
308      */
309     public void setDestinationType(DestinationType destinationType) {
310         if(destinationType == null){
311             throw new IllegalArgumentException();
312         }
313         this.destinationType = destinationType;
314     }
315 
316     public DestinationType getDestinationType(){
317         return destinationType;
318     }
319 
320     /**
321      * In less than or equal to zero, the height is not
322      * constrained on the output image. The height is in
323      * user space.
324      */
325     public void setHeight(float height) {
326         this.height = height;
327     }
328 
329     public float getHeight(){
330         return height;
331     }
332 
333     /**
334      * In less than or equal to zero, the width is not
335      * constrained on the output image. The width is in
336      * user space.
337      */
338     public void setWidth(float width) {
339         this.width = width;
340     }
341 
342     public float getWidth(){
343         return width;
344     }
345 
346     /**
347      * If less than or equal to zero, the maximum height 
348      * does not have any effect on the output image. 
349      * The maximum height is in user space.
350      */
351     public void setMaxHeight(float height) {
352         this.maxHeight = height;
353     }
354 
355     public float getMaxHeight(){
356         return maxHeight;
357     }
358 
359     /**
360      * If less than or equal to zero, the maximum width 
361      * does not have any effect on the output image. 
362      * The maximum width is in user space.
363      */
364     public void setMaxWidth(float width) {
365         this.maxWidth = width;
366     }
367 
368     public float getMaxWidth(){
369         return maxWidth;
370     }
371 
372     /**
373      * Sets the JPEG encoding quality. The value should be strictly
374      * less than 1. If the value is less than zero, then the maximum
375      * encoding quality is used.
376      */
377     public void setQuality(float quality) throws IllegalArgumentException {
378         if(quality >= 1){
379             throw new IllegalArgumentException();
380         }
381 
382         this.quality = quality;
383     }
384 
385     public float getQuality(){
386         return quality;
387     }
388 
389     /**
390      * Tells the PNG encoder to reduce the image to 256 colors, so the
391      * PNG file is indexed.
392      */
393     public void setIndexed(int bits) throws IllegalArgumentException {
394         this.indexed = bits;
395     }
396 
397     public int getIndexed(){
398         return indexed;
399     }
400 
401     /**
402      * Sets the user language. If the value is null, then the default (see 
403      * {@link org.apache.batik.bridge.UserAgent#getLanguages})
404      * is used.
405      */
406     public void setLanguage(String language){
407         this.language = language;
408     }
409 
410     public String getLanguage(){
411         return language;
412     }
413 
414     /**
415      * Sets the user stylesheet. May be null.
416      */
417     public void setUserStylesheet(String userStylesheet){
418         this.userStylesheet = userStylesheet;
419     }
420 
421     public String getUserStylesheet(){
422         return userStylesheet;
423     }
424 
425     /**
426      * Sets the millimeters per pixel constant. A negative
427      * value will cause the default value 
428      * (see {@link org.apache.batik.bridge.UserAgent#getPixelUnitToMillimeter})
429      * to be used.
430      */
431     public void setPixelUnitToMillimeter(float pixelUnitToMillimeter){
432         this.pixelUnitToMillimeter = pixelUnitToMillimeter;
433     }
434 
435     public float getPixelUnitToMillimeter(){
436         return pixelUnitToMillimeter;
437     }
438 
439     /**
440      * Sets the <tt>area</tt> as a Rectangle. This value can
441      * be null in which case the whole image will be rendered. If the 
442      * area is not null, then only the portion of the image it
443      * defines will be rendered.
444      */
445     public void setArea(Rectangle2D area){
446         this.area = area;
447     }
448 
449     public Rectangle2D getArea(){
450         return area;
451     }
452 
453     /**
454      * Sets the list of individual SVG sources. The strings 
455      * can be either URLs or file names. Note that invalid
456      * sources (e.g., read-protected files or invalid URLs)
457      * will cause <tt>SVGConverterExceptions</tt> to be 
458      * thrown during the transcoding process (see {@link #execute});
459      */
460     public void setSources(String[] sources) {
461         if(sources == null){
462             this.sources = null;
463         }
464         else{
465             this.sources = new Vector();
466             for (int i=0; i<sources.length; i++){
467                 if (sources[i] != null){
468                     this.sources.addElement(sources[i]);
469                 }
470             }
471 
472             if (this.sources.size() == 0){
473                 this.sources = null;
474             }
475         }
476     }
477 
478     public Vector getSources(){
479         return sources;
480     }
481 
482     /**
483      * When converting a single source, dst can be a file.
484      * Othewise, it should be a directory.
485      */
486     public void setDst(File dst) {
487         this.dst = dst;
488     }
489 
490     public File getDst(){
491         return dst;
492     }
493 
494     /**
495      * Sets the <tt>backgroundColor</tt> value. This can be
496      * null in which case no color will be used to fill the 
497      * background before rendering this SVG image.
498      */
499     public void setBackgroundColor(Color backgroundColor){
500         this.backgroundColor = backgroundColor;
501     }
502 
503     public Color getBackgroundColor(){
504         return backgroundColor;
505     }
506 
507     /**
508      * Sets the <tt>mediaType</tt> value. This value controls
509      * the CSS media for which the image should be rendered. It 
510      * can be null, in which case no specific media selectors will
511      * apply. If it is not null, it can contain space separated values
512      * of the medias for which the image should be rendered. For example,
513      * "screen", "print" or "scree projection" are valid values.
514      */
515     public void setMediaType(String mediaType){
516         this.mediaType = mediaType;
517     }
518 
519     public String getMediaType(){
520         return mediaType;
521     }
522 
523     /**
524      * Sets the <tt>defaultFontFamily</tt> value. This value controls
525      * the default value for the font-family CSS property when that
526      * property is unspecified.
527      */
528     public void setDefaultFontFamily(String defaultFontFamily) {
529         this.defaultFontFamily = defaultFontFamily;
530     }
531 
532     public String getDefaultFontFamily() {
533         return defaultFontFamily;
534     }
535 
536     /**
537      * Sets the <tt>alternateStyleSheet</tt> value. This value
538      * controls the CSS alternate stylesheet to select in the 
539      * rendered SVG file(s). It may be null, in which case no alternate
540      * stylesheet will be selected.
541      */
542     public void setAlternateStylesheet(String alternateStylesheet){
543         this.alternateStylesheet = alternateStylesheet;
544     }
545 
546     public String getAlternateStylesheet(){
547         return alternateStylesheet;
548     }
549 
550     /**
551      * Defines whether or not input sources should be validated in
552      * the conversion process
553      */
554     public void setValidate(boolean validate){
555         this.validate = validate;
556     }
557 
558     public boolean getValidate(){
559         return validate;
560     }
561 
562     /**
563      * Sets whether or not scripts attached to the DOM using 'onload'
564      * event attribute must be executed before rasterizing.
565      *
566      * @param b true means scripts will be executed
567      */
568     public void setExecuteOnload(boolean b){
569         this.executeOnload = b;
570     }
571 
572     /**
573      * Returns true if the scripts attached to the DOM using 'onload'
574      * event attribute is going to be executed before rasterizing,
575      * false otherwise.
576      */
577     public boolean getExecuteOnload(){
578         return executeOnload;
579     }
580     
581     /**
582      * Sets the set of allowed script types (i.e., the set of possible
583      * values for the type attribute in the &lt;script&gt; element),
584      * as a comma separated list of allowed values.
585      */
586     public void setAllowedScriptTypes(String allowedScriptTypes){
587         this.allowedScriptTypes = allowedScriptTypes;
588     }
589 
590     /**
591      * Returns the list of allowed script types.
592      *
593      * @see #setAllowedScriptTypes
594      */
595     public String getAllowedScriptTypes(){
596         return allowedScriptTypes;
597     }
598 
599     /**
600      * Sets whether scripts should only be loaded from the same
601      * location as the documents referencing them.
602      */
603     public void setConstrainScriptOrigin(boolean constrainScriptOrigin){
604         this.constrainScriptOrigin = constrainScriptOrigin;
605     }
606 
607     /**
608      * Returns whether scripts can only be loaded from the same
609      * origin as the documents referencing them.
610      */
611     public boolean getConstrainScriptOrigin(){
612         return constrainScriptOrigin;
613     }
614 
615     /**
616      * Sets whether or not scripts should be run securely
617      */
618     public void setSecurityOff(boolean securityOff){
619         this.securityOff = securityOff;
620     }
621 
622     /**
623      * Returns whether or not scripts will be run securely
624      */
625     public boolean getSecurityOff(){
626         return securityOff;
627     }
628 
629     /**
630      * Returns true if f is a File. <code>f</code> is found to be a file if
631      * it exists and is a file. If it does not exist, it is declared
632      * to be a file if it has the same extension as the DestinationType.
633      */
634     protected boolean isFile(File f){
635         if (f.exists()){
636             return f.isFile();
637         } else {
638             if (f.toString().toLowerCase().endsWith(destinationType.getExtension())){
639                 return true;
640             }
641         }
642 
643         return false;
644     }
645 
646     /**
647      * Starts the conversion process.
648      * @throws SVGConverterException thrown if parameters are not set correctly.
649      */
650     public void execute() throws SVGConverterException {
651         // Compute the set of SVGConverterSource from the source properties
652         // (srcDir and srcFile);
653         // This throws an exception if there is not at least one src file.
654         Vector sources = computeSources();
655 
656         // Compute the destination files from dest
657         Vector dstFiles = null;
658         if(sources.size() == 1 && dst != null && isFile(dst)){
659             dstFiles = new Vector();
660             dstFiles.addElement(dst);
661         }
662         else{
663             dstFiles = computeDstFiles(sources);
664         }
665 
666         // Now, get the transcoder to use for the operation
667         Transcoder transcoder = destinationType.getTranscoder();
668         if(transcoder == null) {
669             throw new SVGConverterException(ERROR_CANNOT_ACCESS_TRANSCODER,
670                                              new Object[]{destinationType.toString()},
671                                              true /* fatal error */);
672         }
673 
674         // Now, compute the set of transcoding hints to use
675         Map hints = computeTranscodingHints();
676         transcoder.setTranscodingHints(hints);
677 
678         // Notify listener that task has been computed
679         if(!controller.proceedWithComputedTask(transcoder,
680                                                hints,
681                                                sources,
682                                                dstFiles)){
683             return;
684         }
685 
686         // Convert files one by one
687         for(int i = 0 ; i < sources.size() ; i++) {
688             // Get the file from the vector.
689             SVGConverterSource currentFile 
690                 = (SVGConverterSource)sources.elementAt(i);
691             File outputFile  = (File)dstFiles.elementAt(i);
692 
693             createOutputDir(outputFile);
694             transcode(currentFile, outputFile, transcoder);
695         }
696     }
697     
698     /**
699      * Populates a vector with destination files names
700      * computed from the names of the files in the sources vector
701      * and the value of the dst property
702      */
703     protected Vector computeDstFiles(Vector sources) 
704     throws SVGConverterException {
705         Vector dstFiles = new Vector();
706         if (dst != null) {
707             if (dst.exists() && dst.isFile()) {
708                 throw new SVGConverterException(ERROR_CANNOT_USE_DST_FILE);
709             }
710 
711             //
712             // Either dst exist and is a directory or dst does not
713             // exist and we may fail later on in createOutputDir
714             //
715             int n = sources.size();
716             for(int i=0; i<n; i++){
717                 SVGConverterSource src = (SVGConverterSource)sources.elementAt(i);
718                 // Generate output filename from input filename.
719                 File outputName = new File(dst.getPath(), 
720                                            getDestinationFile(src.getName()));
721                 dstFiles.addElement(outputName);
722                 
723             }
724         } else {
725             //
726             // No destination directory has been specified.
727             // Try and create files in the same directory as the 
728             // sources. This only work if sources are files.
729             //
730             int n = sources.size();
731             for(int i=0; i<n; i++){
732                 SVGConverterSource src = (SVGConverterSource)sources.elementAt(i);
733                 if (!(src instanceof SVGConverterFileSource)) {
734                     throw new SVGConverterException(ERROR_CANNOT_COMPUTE_DESTINATION,
735                                                      new Object[]{src});
736                 }
737 
738                 // Generate output filename from input filename.
739                 SVGConverterFileSource fs = (SVGConverterFileSource)src;
740                 File outputName = new File(fs.getFile().getParent(),
741                                            getDestinationFile(src.getName()));
742                 dstFiles.addElement(outputName);
743             }
744             
745         }
746 
747         return dstFiles;
748     }
749 
750     /**
751      * Populates a vector with the set of SVG files from the 
752      * srcDir if it is not null and with the sources (files or URLs)
753      * if any.
754      */
755     protected Vector computeSources() throws SVGConverterException{
756         Vector sources = new Vector();
757 
758         // Check that at least one source has been specified.
759         if (this.sources == null){
760             throw new SVGConverterException(ERROR_NO_SOURCES_SPECIFIED);
761         }
762 
763         int n = this.sources.size();
764         for (int i=0; i<n; i++){
765             String sourceString = (String)(this.sources.elementAt(i));
766             File file = new File(sourceString);
767             if (file.exists()) {
768                 sources.addElement(new SVGConverterFileSource(file));
769             } else {
770                 String[] fileNRef = getFileNRef(sourceString);
771                 file = new File(fileNRef[0]);
772                 if (file.exists()){
773                     sources.addElement(new SVGConverterFileSource(file, fileNRef[1]));
774                 } else{
775                     sources.addElement(new SVGConverterURLSource(sourceString));
776                 }
777             }
778         }
779         
780         return sources;
781     }
782 
783     public String[] getFileNRef(String fileName){
784         int n = fileName.lastIndexOf("#");
785         String[] result = {fileName, ""};
786         if (n > -1){
787             result[0] = fileName.substring(0, n);
788             if (n+1 < fileName.length()){
789                 result[1] = fileName.substring(n+1);
790             }
791         }
792 
793         return result;
794     }
795 
796     // -----------------------------------------------------------------------
797     //   Internal methods
798     // -----------------------------------------------------------------------
799 
800     /**
801      * Computes the set of transcoding hints to use for the operation
802      */
803     protected Map computeTranscodingHints(){
804         HashMap map = new HashMap();
805 
806         // Set AOI. ----------------------------------------------------------
807         if (area != null) {
808             map.put(ImageTranscoder.KEY_AOI, area);           
809         }
810 
811         // Set image quality. ------------------------------------------------
812         if (quality > 0) {
813             map.put(JPEGTranscoder.KEY_QUALITY, new Float(this.quality));
814         } 
815 
816         // Set image indexed. ------------------------------------------------
817         if (indexed != -1) {
818             map.put(PNGTranscoder.KEY_INDEXED, new Integer(indexed));
819         } 
820 
821         // Set image background color -----------------------------------------
822         if (backgroundColor != null){
823             map.put(ImageTranscoder.KEY_BACKGROUND_COLOR, backgroundColor);
824         }
825 
826         // Set image height and width. ----------------------------------------
827         if (height > 0) {
828             map.put(ImageTranscoder.KEY_HEIGHT, new Float(this.height));
829         }
830         if (width > 0){
831             map.put(ImageTranscoder.KEY_WIDTH, new Float(this.width));
832         }
833 
834         // Set maximum height and width ---------------------------------------
835         if (maxHeight > 0) {
836             map.put(ImageTranscoder.KEY_MAX_HEIGHT, new Float(this.maxHeight));
837         }
838         if (maxWidth > 0){
839             map.put(ImageTranscoder.KEY_MAX_WIDTH, new Float(this.maxWidth));
840         }
841 
842         // Set CSS Media
843         if (mediaType != null){
844             map.put(ImageTranscoder.KEY_MEDIA, mediaType);
845         }
846 
847         // Set default font-family
848         if (defaultFontFamily != null) {
849             map.put(ImageTranscoder.KEY_DEFAULT_FONT_FAMILY, defaultFontFamily);
850         }
851 
852         // Set alternateStylesheet
853         if (alternateStylesheet != null){
854             map.put(ImageTranscoder.KEY_ALTERNATE_STYLESHEET, alternateStylesheet);
855         }
856 
857         // Set user stylesheet
858         if (userStylesheet != null){
859             map.put(ImageTranscoder.KEY_USER_STYLESHEET_URI, userStylesheet);
860         }
861 
862         // Set the user language
863         if (language != null){
864             map.put(ImageTranscoder.KEY_LANGUAGE, language);
865         }
866 
867         // Sets the millimeters per pixel
868         if (pixelUnitToMillimeter > 0){
869             map.put(ImageTranscoder.KEY_PIXEL_UNIT_TO_MILLIMETER, 
870                     new Float(pixelUnitToMillimeter));
871         }
872 
873         // Set validation
874         if (validate){
875             map.put(ImageTranscoder.KEY_XML_PARSER_VALIDATING,
876                     new Boolean(validate));
877         }
878 
879         // Set onload
880         if (executeOnload) {
881             map.put(ImageTranscoder.KEY_EXECUTE_ONLOAD, new Boolean(executeOnload));
882         }
883         
884         // Set allowed scripts
885         if (allowedScriptTypes != null) {
886             map.put(ImageTranscoder.KEY_ALLOWED_SCRIPT_TYPES, allowedScriptTypes);
887         }
888 
889         // Set constrain script origin
890         if (!constrainScriptOrigin) {
891             map.put(ImageTranscoder.KEY_CONSTRAIN_SCRIPT_ORIGIN, 
892                     new Boolean(constrainScriptOrigin));
893         }
894 
895         return map;
896     }
897 
898     /**
899      * Converts the input image to the result image.
900      * with the given transcoder. If a failure happens, the 
901      * controller is notified and decides whether to proceed
902      * or not. If it decides to proceed, the converter will
903      * continue processing other files. Otherwise, it will
904      * throw an exception.
905      */
906     protected void transcode(SVGConverterSource inputFile, 
907                              File outputFile,
908                              Transcoder transcoder)
909         throws SVGConverterException {
910         TranscoderInput input = null;
911         TranscoderOutput output = null;
912         OutputStream outputStream = null;
913 
914         if (!controller.proceedWithSourceTranscoding(inputFile, 
915                                                      outputFile)){
916             return;
917         }
918 
919         try {
920             if (inputFile.isSameAs(outputFile.getPath())) {
921                 throw new SVGConverterException(ERROR_SOURCE_SAME_AS_DESTINATION,
922                                                  true /* fatal error */);
923             }
924             
925             // Compute transcoder input.
926             if (!inputFile.isReadable()) {
927                 throw new SVGConverterException(ERROR_CANNOT_READ_SOURCE,
928                                                  new Object[]{inputFile.getName()});
929             }
930 
931             try {
932                 InputStream in = inputFile.openStream();
933                 in.close();
934             } catch(IOException ioe) {
935                 throw new SVGConverterException(ERROR_CANNOT_OPEN_SOURCE,
936                                                  new Object[] {inputFile.getName(),
937                                                                ioe.toString()});
938                                                                } 
939             
940             input = new TranscoderInput(inputFile.getURI());
941 
942             // Compute transcoder output.
943             if (!isWriteable(outputFile)) {
944                 throw new SVGConverterException(ERROR_OUTPUT_NOT_WRITEABLE,
945                                                  new Object[] {outputFile.getName()});
946             }
947             try {
948                 outputStream = new FileOutputStream(outputFile);
949             } catch(FileNotFoundException fnfe) {
950                 throw new SVGConverterException(ERROR_CANNOT_OPEN_OUTPUT_FILE,
951                                                  new Object[] {outputFile.getName()});
952             }
953             
954             output = new TranscoderOutput(outputStream);
955         } catch(SVGConverterException e){
956             boolean proceed = controller.proceedOnSourceTranscodingFailure
957                 (inputFile, outputFile, e.getErrorCode());
958             if (proceed){
959                 return;
960             } else {
961                 throw e;
962             }
963         }
964 
965         // Transcode now
966         boolean success = false;
967         try {
968             transcoder.transcode(input, output);
969             success = true;
970         } catch(Exception te) {
971             te.printStackTrace();
972             try {
973                 outputStream.flush();
974                 outputStream.close();
975             } catch(IOException ioe) {}
976             
977             // Report error to the controller. If controller decides
978             // to stop, throw an exception
979             boolean proceed = controller.proceedOnSourceTranscodingFailure
980                 (inputFile, outputFile, ERROR_WHILE_RASTERIZING_FILE);
981 
982             if (!proceed){
983                 throw new SVGConverterException(ERROR_WHILE_RASTERIZING_FILE,
984                                                  new Object[] {outputFile.getName(),
985                                                                te.getMessage()});
986             }
987         }
988 
989         // Close streams and clean up.
990         try {
991             outputStream.flush();
992             outputStream.close();
993         } catch(IOException ioe) {
994             return;
995         }
996 
997         if (success){
998             controller.onSourceTranscodingSuccess(inputFile, outputFile);
999         }
1000    }
1001
1002    /**
1003     * Get the name of the result image file.
1004     *
1005     * <P>This method modifies the result filename, it changes the existing 
1006     * suffix to correspong the result file type. It also adds the suffix 
1007     * if the file doesn't have one.</P>
1008     *
1009     * @param file Result file name as a String object.
1010     *
1011     * @return Name of the file. The directory of the file is not returned. 
1012     *         The returned string is empty if the parameter is not a file.
1013     */
1014    protected String getDestinationFile(String file) {
1015        int suffixStart;            // Location of the first char of 
1016                                    // the suffix in a String.
1017        String oldName;             // Existing filename.
1018        String newSuffix = destinationType.getExtension();
1019                                    // New suffix.
1020
1021        oldName = file;
1022        // Find the first char of the suffix.
1023        suffixStart = oldName.lastIndexOf(".");
1024        String dest = null;
1025        if (suffixStart != -1) {
1026            // Replace existing suffix.
1027            dest = new String(oldName.substring(0, suffixStart) + newSuffix);
1028        } else {
1029            // Add new suffix.
1030            dest = new String(oldName + newSuffix);
1031        }
1032
1033        return dest;
1034    }
1035
1036    /**
1037     * Creates directories for output files if needed.
1038     *
1039     * @param output Output file with path.
1040     *
1041     * @throws SVGConverterException Output directory doesn't exist and it can't be created.
1042     */
1043    protected void createOutputDir(File output)
1044        throws SVGConverterException {
1045
1046        File outputDir;             // Output directory object.
1047        boolean success = true;     // false if the output directory 
1048                                    // doesn't exist and it can't be created
1049                                    // true otherwise
1050
1051
1052        // Create object from output directory.
1053        String parentDir = output.getParent();
1054        if (parentDir != null){
1055            outputDir = new File(output.getParent());
1056            if (outputDir.exists() == false) {
1057                // Output directory doesn't exist, so create it.
1058                success = outputDir.mkdirs();
1059            } else {
1060                if (outputDir.isDirectory() == false) {
1061                    // File, which have a same name as the output directory, exists.
1062                    // Create output directory.
1063                    success = outputDir.mkdirs();
1064                }
1065            }
1066        }
1067
1068        if (!success) {
1069            throw new SVGConverterException(ERROR_UNABLE_TO_CREATE_OUTPUT_DIR);
1070        }
1071    }
1072
1073    /**
1074     * Checks if the application is allowed to write to the file.
1075     *
1076     * @param file File to be checked.
1077     *
1078     * @return <tt>true</tt> if the file is writeable and <tt>false</tt> otherwise.
1079     */
1080    protected boolean isWriteable(File file) {
1081        if (file.exists()) {
1082            // Check the existing file.
1083            if (!file.canWrite()) {
1084                return false;
1085            }
1086        } else {
1087            // Check the file that doesn't exist yet.
1088            // Create a new file. The file is writeable if 
1089            // the creation succeeds.
1090            try {
1091                file.createNewFile();
1092            } catch(IOException ioe) {
1093                return false;
1094            }
1095        }
1096        return true;
1097    }
1098
1099    // -----------------------------------------------------------------------
1100    //   Inner classes
1101    // -----------------------------------------------------------------------
1102
1103    /**
1104     * Convenience class to filter svg files
1105     */
1106    public static class SVGFileFilter implements FileFilter {
1107        public static final String SVG_EXTENSION = ".svg";
1108        
1109        public boolean accept(File file){
1110            if (file != null && file.getName().toLowerCase().endsWith(SVG_EXTENSION)){
1111                return true;
1112            }
1113            
1114            return false;
1115        }
1116    }
1117
1118}
1119