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 <script> 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