View Javadoc
1   package org.apache.maven.doxia.docrenderer.pdf.itext;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.io.FileInputStream;
24  import java.io.FileOutputStream;
25  import java.io.IOException;
26  import java.io.Writer;
27  import java.net.MalformedURLException;
28  import java.net.URL;
29  import java.net.URLClassLoader;
30  import java.text.SimpleDateFormat;
31  import java.util.Collection;
32  import java.util.Date;
33  import java.util.Iterator;
34  import java.util.LinkedList;
35  import java.util.List;
36  import java.util.Locale;
37  import java.util.Map;
38  
39  import javax.xml.parsers.DocumentBuilder;
40  import javax.xml.parsers.DocumentBuilderFactory;
41  import javax.xml.parsers.ParserConfigurationException;
42  import javax.xml.transform.OutputKeys;
43  import javax.xml.transform.Transformer;
44  import javax.xml.transform.TransformerConfigurationException;
45  import javax.xml.transform.TransformerException;
46  import javax.xml.transform.TransformerFactory;
47  import javax.xml.transform.dom.DOMSource;
48  import javax.xml.transform.stream.StreamResult;
49  import javax.xml.transform.stream.StreamSource;
50  
51  import org.apache.maven.doxia.docrenderer.DocumentRendererContext;
52  import org.apache.maven.doxia.docrenderer.DocumentRendererException;
53  import org.apache.maven.doxia.docrenderer.pdf.AbstractPdfRenderer;
54  import org.apache.maven.doxia.docrenderer.pdf.PdfRenderer;
55  import org.apache.maven.doxia.document.DocumentCover;
56  import org.apache.maven.doxia.document.DocumentMeta;
57  import org.apache.maven.doxia.document.DocumentModel;
58  import org.apache.maven.doxia.document.DocumentTOCItem;
59  import org.apache.maven.doxia.module.itext.ITextSink;
60  import org.apache.maven.doxia.module.itext.ITextSinkFactory;
61  import org.apache.maven.doxia.module.itext.ITextUtil;
62  import org.apache.maven.doxia.parser.module.ParserModule;
63  import org.apache.xml.utils.DefaultErrorHandler;
64  import org.codehaus.plexus.component.annotations.Component;
65  import org.codehaus.plexus.util.IOUtil;
66  import org.codehaus.plexus.util.StringUtils;
67  import org.codehaus.plexus.util.WriterFactory;
68  import org.w3c.dom.DOMException;
69  import org.w3c.dom.Document;
70  import org.w3c.dom.Node;
71  import org.xml.sax.SAXException;
72  
73  import com.lowagie.text.ElementTags;
74  
75  /**
76   * Abstract <code>document</code> render with the <code>iText</code> framework
77   *
78   * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
79   * @author ltheussl
80   * @since 1.1
81   */
82  @Component( role = PdfRenderer.class, hint = "itext" )
83  public class ITextPdfRenderer
84      extends AbstractPdfRenderer
85  {
86      /** The xslt style sheet used to transform a Document to an iText file. */
87      private static final String XSLT_RESOURCE = "TOC.xslt";
88  
89      /** The TransformerFactory. */
90      private static final TransformerFactory TRANSFORMER_FACTORY = TransformerFactory.newInstance();
91  
92      /** The DocumentBuilderFactory. */
93      private static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory.newInstance();
94  
95      /** The DocumentBuilder. */
96      private static final DocumentBuilder DOCUMENT_BUILDER;
97  
98      static
99      {
100         TRANSFORMER_FACTORY.setErrorListener( new DefaultErrorHandler() );
101 
102         try
103         {
104             DOCUMENT_BUILDER = DOCUMENT_BUILDER_FACTORY.newDocumentBuilder();
105         }
106         catch ( ParserConfigurationException e )
107         {
108             throw new RuntimeException( "Error building document :" + e.getMessage() );
109         }
110     }
111 
112     /** {@inheritDoc} */
113     public void generatePdf( File inputFile, File pdfFile )
114         throws DocumentRendererException
115     {
116         if ( getLogger().isDebugEnabled() )
117         {
118             getLogger().debug( "Generating : " + pdfFile );
119         }
120 
121         try
122         {
123             ITextUtil.writePdf( new FileInputStream( inputFile ), new FileOutputStream( pdfFile ) );
124         }
125         catch ( IOException e )
126         {
127             throw new DocumentRendererException( "Cannot create PDF from " + inputFile + ": " + e.getMessage(), e );
128         }
129         catch ( RuntimeException e )
130         {
131             throw new DocumentRendererException( "Error creating PDF from " + inputFile + ": " + e.getMessage(), e );
132         }
133     }
134 
135     /** {@inheritDoc} */
136     @Override
137     public void render( Map<String, ParserModule> filesToProcess, File outputDirectory, DocumentModel documentModel )
138         throws DocumentRendererException, IOException
139     {
140         render( filesToProcess, outputDirectory, documentModel, null );
141     }
142 
143     /** {@inheritDoc} */
144     @Override
145     public void render( Map<String, ParserModule> filesToProcess, File outputDirectory, DocumentModel documentModel,
146                         DocumentRendererContext context )
147         throws DocumentRendererException, IOException
148     {
149         // copy resources, images, etc.
150         copyResources( outputDirectory );
151 
152         if ( documentModel == null )
153         {
154             getLogger().debug( "No document model, generating all documents individually." );
155 
156             renderIndividual( filesToProcess, outputDirectory, context );
157             return;
158         }
159 
160         String outputName = getOutputName( documentModel );
161 
162         File outputITextFile = new File( outputDirectory, outputName + ".xml" );
163         if ( !outputITextFile.getParentFile().exists() )
164         {
165             outputITextFile.getParentFile().mkdirs();
166         }
167 
168         File pdfOutputFile = new File( outputDirectory, outputName + ".pdf" );
169         if ( !pdfOutputFile.getParentFile().exists() )
170         {
171             pdfOutputFile.getParentFile().mkdirs();
172         }
173 
174         List<File> iTextFiles;
175         if ( ( documentModel.getToc() == null ) || ( documentModel.getToc().getItems() == null ) )
176         {
177             getLogger().info( "No TOC is defined in the document descriptor. Merging all documents." );
178 
179             iTextFiles = parseAllFiles( filesToProcess, outputDirectory, context );
180         }
181         else
182         {
183             getLogger().debug( "Using TOC defined in the document descriptor." );
184 
185             iTextFiles = parseTOCFiles( outputDirectory, documentModel, context );
186         }
187 
188         String generateTOC =
189             ( context != null && context.get( "generateTOC" ) != null ? context.get( "generateTOC" ).toString()
190                             : "start" );
191 
192         File iTextFile = new File( outputDirectory, outputName + ".xml" );
193         File iTextOutput = new File( outputDirectory, outputName + "." + getOutputExtension() );
194         Document document = generateDocument( iTextFiles );
195         transform( documentModel, document, iTextFile, generateTOC );
196         generatePdf( iTextFile, iTextOutput );
197     }
198 
199     /** {@inheritDoc} */
200     @Override
201     public void renderIndividual( Map<String, ParserModule> filesToProcess, File outputDirectory )
202         throws DocumentRendererException, IOException
203     {
204         renderIndividual( filesToProcess, outputDirectory, null );
205     }
206 
207     /** {@inheritDoc} */
208     @Override
209     public void renderIndividual( Map<String, ParserModule> filesToProcess, File outputDirectory,
210                                   DocumentRendererContext context )
211         throws DocumentRendererException, IOException
212     {
213         for ( Map.Entry<String, ParserModule> entry : filesToProcess.entrySet() )
214         {
215             String key = entry.getKey();
216             ParserModule module = entry.getValue();
217             File fullDoc = new File( getBaseDir(), module.getSourceDirectory() + File.separator + key );
218 
219             String output = key;
220             for ( String extension : module.getExtensions() )
221             {
222                 String lowerCaseExtension = extension.toLowerCase( Locale.ENGLISH );
223                 if ( output.toLowerCase( Locale.ENGLISH ).indexOf( "." + lowerCaseExtension ) != -1 )
224                 {
225                     output =
226                         output.substring( 0, output.toLowerCase( Locale.ENGLISH ).indexOf( "." + lowerCaseExtension ) );
227                 }
228             }
229 
230             File outputITextFile = new File( outputDirectory, output + ".xml" );
231             if ( !outputITextFile.getParentFile().exists() )
232             {
233                 outputITextFile.getParentFile().mkdirs();
234             }
235 
236             File pdfOutputFile = new File( outputDirectory, output + ".pdf" );
237             if ( !pdfOutputFile.getParentFile().exists() )
238             {
239                 pdfOutputFile.getParentFile().mkdirs();
240             }
241 
242             parse( fullDoc, module, outputITextFile, context );
243 
244             generatePdf( outputITextFile, pdfOutputFile );
245         }
246     }
247 
248       //--------------------------------------------
249      //
250     //--------------------------------------------
251 
252 
253     /**
254      * Parse a source document and emit results into a sink.
255      *
256      * @param fullDocPath file to the source document.
257      * @param module the site module associated with the source document (determines the parser to use).
258      * @param iTextFile the resulting iText xml file.
259      * @throws DocumentRendererException in case of a parsing problem.
260      * @throws IOException if the source and/or target document cannot be opened.
261      */
262     private void parse( File fullDoc, ParserModule module, File iTextFile, DocumentRendererContext context )
263         throws DocumentRendererException, IOException
264     {
265         if ( getLogger().isDebugEnabled() )
266         {
267             getLogger().debug( "Parsing file " + fullDoc.getAbsolutePath() );
268         }
269 
270         System.setProperty( "itext.basedir", iTextFile.getParentFile().getAbsolutePath() );
271 
272         Writer writer = null;
273         ITextSink sink = null;
274         try
275         {
276             writer = WriterFactory.newXmlWriter( iTextFile );
277             sink = (ITextSink) new ITextSinkFactory().createSink( writer );
278 
279             sink.setClassLoader( new URLClassLoader( new URL[] { iTextFile.getParentFile().toURI().toURL() } ) );
280 
281             parse( fullDoc.getAbsolutePath(), module.getParserId(), sink, context );
282         }
283         finally
284         {
285             if ( sink != null )
286             {
287                 sink.flush();
288                 sink.close();
289             }
290             IOUtil.close( writer );
291             System.getProperties().remove( "itext.basedir" );
292         }
293     }
294 
295     /**
296      * Merge all iTextFiles to a single one.
297      *
298      * @param iTextFiles list of iText xml files.
299      * @return Document.
300      * @throws DocumentRendererException if any.
301      * @throws IOException if any.
302      */
303     private Document generateDocument( List<File> iTextFiles )
304         throws DocumentRendererException, IOException
305     {
306         Document document = DOCUMENT_BUILDER.newDocument();
307         document.appendChild( document.createElement( ElementTags.ITEXT ) ); // Used only to set a root
308 
309         for ( File iTextFile : iTextFiles )
310         {
311             Document iTextDocument;
312 
313             try
314             {
315                 iTextDocument = DOCUMENT_BUILDER.parse( iTextFile );
316             }
317             catch ( SAXException e )
318             {
319                 throw new DocumentRendererException( "SAX Error : " + e.getMessage() );
320             }
321 
322             // Only one chapter per doc
323             Node chapter = iTextDocument.getElementsByTagName( ElementTags.CHAPTER ).item( 0 );
324 
325             try
326             {
327                 document.getDocumentElement().appendChild( document.importNode( chapter, true ) );
328             }
329             catch ( DOMException e )
330             {
331                 throw new DocumentRendererException( "Error appending chapter for "
332                         + iTextFile + " : " + e.getMessage() );
333             }
334         }
335 
336         return document;
337     }
338 
339     /**
340      * Initialize the transformer object.
341      *
342      * @return an instance of a transformer object.
343      * @throws DocumentRendererException if any.
344      */
345     private Transformer initTransformer()
346         throws DocumentRendererException
347     {
348         try
349         {
350             Transformer transformer = TRANSFORMER_FACTORY.newTransformer( new StreamSource( ITextPdfRenderer.class
351                 .getResourceAsStream( XSLT_RESOURCE ) ) );
352 
353             transformer.setErrorListener( TRANSFORMER_FACTORY.getErrorListener() );
354 
355             transformer.setOutputProperty( OutputKeys.OMIT_XML_DECLARATION, "false" );
356 
357             transformer.setOutputProperty( OutputKeys.INDENT, "yes" );
358 
359             transformer.setOutputProperty( OutputKeys.METHOD, "xml" );
360 
361             transformer.setOutputProperty( OutputKeys.ENCODING, "UTF-8" );
362 
363             // No doctype since itext doctype is not up to date!
364 
365             return transformer;
366         }
367         catch ( TransformerConfigurationException e )
368         {
369             throw new DocumentRendererException( "Error configuring Transformer for " + XSLT_RESOURCE + ": "
370                 + e.getMessage() );
371         }
372         catch ( IllegalArgumentException e )
373         {
374             throw new DocumentRendererException( "Error configuring Transformer for " + XSLT_RESOURCE + ": "
375                 + e.getMessage() );
376         }
377     }
378 
379     /**
380      * Add transformer parameters from a DocumentModel.
381      *
382      * @param transformer the Transformer to set the parameters.
383      * @param documentModel the DocumentModel to take the parameters from, could be null.
384      * @param iTextFile the iTextFile not null for the relative paths.
385      * @param generateTOC not null, possible values are: 'none', 'start' and 'end'.
386      */
387     private void addTransformerParameters( Transformer transformer, DocumentModel documentModel, File iTextFile,
388                                            String generateTOC )
389     {
390         if ( documentModel == null )
391         {
392             return;
393         }
394 
395         // TOC
396         addTransformerParameter( transformer, "toc.position", generateTOC );
397 
398         // Meta parameters
399         boolean hasNullMeta = false;
400         if ( documentModel.getMeta() == null )
401         {
402             hasNullMeta = true;
403             documentModel.setMeta( new DocumentMeta() );
404         }
405         addTransformerParameter( transformer, "meta.author", documentModel.getMeta().getAllAuthorNames(),
406                                  System.getProperty( "user.name", "null" ) );
407         addTransformerParameter( transformer, "meta.creator", documentModel.getMeta().getCreator(),
408                                  System.getProperty( "user.name", "null" ) );
409         // see com.lowagie.text.Document#addCreationDate()
410         SimpleDateFormat sdf = new SimpleDateFormat( "EEE MMM dd HH:mm:ss zzz yyyy" );
411         addTransformerParameter( transformer, "meta.creationdate", documentModel.getMeta().getCreationdate(),
412                                  sdf.format( new Date() ) );
413         addTransformerParameter( transformer, "meta.keywords", documentModel.getMeta().getAllKeyWords() );
414         addTransformerParameter( transformer, "meta.pagesize", documentModel.getMeta().getPageSize(),
415                                  ITextUtil.getPageSize( ITextUtil.getDefaultPageSize() ) );
416         addTransformerParameter( transformer, "meta.producer", documentModel.getMeta().getGenerator(),
417                                  "Apache Doxia iText" );
418         addTransformerParameter( transformer, "meta.subject", documentModel.getMeta().getSubject(),
419                                  ( documentModel.getMeta().getTitle() != null ? documentModel.getMeta().getTitle()
420                                                  : "" ) );
421         addTransformerParameter( transformer, "meta.title", documentModel.getMeta().getTitle() );
422         if ( hasNullMeta )
423         {
424             documentModel.setMeta( null );
425         }
426 
427         // cover parameter
428         boolean hasNullCover = false;
429         if ( documentModel.getCover() == null )
430         {
431             hasNullCover = true;
432             documentModel.setCover( new DocumentCover() );
433         }
434         addTransformerParameter( transformer, "cover.author", documentModel.getCover().getAllAuthorNames(),
435                                  System.getProperty( "user.name", "null" ) );
436         String companyLogo = getLogoURL( documentModel.getCover().getCompanyLogo(), iTextFile.getParentFile() );
437         addTransformerParameter( transformer, "cover.companyLogo", companyLogo );
438         addTransformerParameter( transformer, "cover.companyName", documentModel.getCover().getCompanyName() );
439         if ( documentModel.getCover().getCoverdate() == null )
440         {
441             documentModel.getCover().setCoverDate( new Date() );
442             addTransformerParameter( transformer, "cover.date", documentModel.getCover().getCoverdate() );
443             documentModel.getCover().setCoverDate( null );
444         }
445         else
446         {
447             addTransformerParameter( transformer, "cover.date", documentModel.getCover().getCoverdate() );
448         }
449         addTransformerParameter( transformer, "cover.subtitle", documentModel.getCover().getCoverSubTitle() );
450         addTransformerParameter( transformer, "cover.title", documentModel.getCover().getCoverTitle() );
451         addTransformerParameter( transformer, "cover.type", documentModel.getCover().getCoverType() );
452         addTransformerParameter( transformer, "cover.version", documentModel.getCover().getCoverVersion() );
453         String projectLogo = getLogoURL( documentModel.getCover().getProjectLogo(), iTextFile.getParentFile() );
454         addTransformerParameter( transformer, "cover.projectLogo", projectLogo );
455         addTransformerParameter( transformer, "cover.projectName", documentModel.getCover().getProjectName() );
456         if ( hasNullCover )
457         {
458             documentModel.setCover( null );
459         }
460     }
461 
462     /**
463      * @param transformer not null
464      * @param name not null
465      * @param value could be empty
466      * @param defaultValue could be empty
467      * @since 1.1.1
468      */
469     private void addTransformerParameter( Transformer transformer, String name, String value, String defaultValue )
470     {
471         if ( StringUtils.isEmpty( value ) )
472         {
473             addTransformerParameter( transformer, name, defaultValue );
474         }
475         else
476         {
477             addTransformerParameter( transformer, name, value );
478         }
479     }
480 
481     /**
482      * @param transformer not null
483      * @param name not null
484      * @param value could be empty
485      * @since 1.1.1
486      */
487     private void addTransformerParameter( Transformer transformer, String name, String value )
488     {
489         if ( StringUtils.isEmpty( value ) )
490         {
491             return;
492         }
493 
494         transformer.setParameter( name, value );
495     }
496 
497     /**
498      * Transform a document to an iTextFile.
499      *
500      * @param documentModel the DocumentModel to take the parameters from, could be null.
501      * @param document the Document to transform.
502      * @param iTextFile the resulting iText xml file.
503      * @param generateTOC not null, possible values are: 'none', 'start' and 'end'.
504      * @throws DocumentRendererException in case of a transformation error.
505      */
506     private void transform( DocumentModel documentModel, Document document, File iTextFile, String generateTOC )
507         throws DocumentRendererException
508     {
509         Transformer transformer = initTransformer();
510 
511         addTransformerParameters( transformer, documentModel, iTextFile, generateTOC );
512 
513         // need a writer for StreamResult to prevent FileNotFoundException when iTextFile contains spaces
514         Writer writer = null;
515         try
516         {
517             writer = WriterFactory.newXmlWriter( iTextFile );
518             transformer.transform( new DOMSource( document ), new StreamResult( writer ) );
519         }
520         catch ( TransformerException e )
521         {
522             throw new DocumentRendererException(
523                                                  "Error transforming Document " + document + ": " + e.getMessage(),
524                                                  e );
525         }
526         catch ( IOException e )
527         {
528             throw new DocumentRendererException(
529                                                  "Error transforming Document " + document + ": " + e.getMessage(),
530                                                  e );
531         }
532         finally
533         {
534             IOUtil.close( writer );
535         }
536     }
537 
538     /**
539      * @param filesToProcess not null
540      * @param outputDirectory not null
541      * @return a list of all parsed files.
542      * @throws DocumentRendererException if any
543      * @throws IOException if any
544      * @since 1.1.1
545      */
546     private List<File> parseAllFiles( Map<String, ParserModule> filesToProcess, File outputDirectory,
547                                       DocumentRendererContext context )
548         throws DocumentRendererException, IOException
549     {
550         List<File> iTextFiles = new LinkedList<File>();
551         for ( Map.Entry<String, ParserModule> entry : filesToProcess.entrySet() )
552         {
553             String key = entry.getKey();
554             ParserModule module = entry.getValue();
555             File fullDoc = new File( getBaseDir(), module.getSourceDirectory() + File.separator + key );
556 
557             String outputITextName = key.substring( 0, key.lastIndexOf( '.' ) + 1 ) + "xml";
558             File outputITextFileTmp = new File( outputDirectory, outputITextName );
559             outputITextFileTmp.deleteOnExit();
560             if ( !outputITextFileTmp.getParentFile().exists() )
561             {
562                 outputITextFileTmp.getParentFile().mkdirs();
563             }
564 
565             iTextFiles.add( outputITextFileTmp );
566             parse( fullDoc, module, outputITextFileTmp, context );
567         }
568 
569         return iTextFiles;
570     }
571 
572     /**
573      * @param filesToProcess not null
574      * @param outputDirectory not null
575      * @return a list of all parsed files.
576      * @throws DocumentRendererException if any
577      * @throws IOException if any
578      * @since 1.1.1
579      */
580     private List<File> parseTOCFiles( File outputDirectory, DocumentModel documentModel,
581                                       DocumentRendererContext context )
582         throws DocumentRendererException, IOException
583     {
584         List<File> iTextFiles = new LinkedList<File>();
585         for ( Iterator<DocumentTOCItem> it = documentModel.getToc().getItems().iterator(); it.hasNext(); )
586         {
587             DocumentTOCItem tocItem = it.next();
588 
589             if ( tocItem.getRef() == null )
590             {
591                 getLogger().debug(
592                                    "No ref defined for the tocItem '" + tocItem.getName()
593                                        + "' in the document descriptor. IGNORING" );
594                 continue;
595             }
596 
597             String href = StringUtils.replace( tocItem.getRef(), "\\", "/" );
598             if ( href.lastIndexOf( '.' ) != -1 )
599             {
600                 href = href.substring( 0, href.lastIndexOf( '.' ) );
601             }
602 
603             Collection<ParserModule> modules = parserModuleManager.getParserModules();
604             for ( ParserModule module : modules )
605             {
606                 File moduleBasedir = new File( getBaseDir(), module.getSourceDirectory() );
607 
608                 if ( moduleBasedir.exists() )
609                 {
610                     for ( String extension : module.getExtensions() )
611                     {
612                         String doc = href + "." + extension;
613                         File source = new File( moduleBasedir, doc );
614     
615                         // Velocity file?
616                         if ( !source.exists() )
617                         {
618                             if ( href.indexOf( "." + extension ) != -1 )
619                             {
620                                 doc = href + ".vm";
621                             }
622                             else
623                             {
624                                 doc = href + "." + extension + ".vm";
625                             }
626                             source = new File( moduleBasedir, doc );
627                         }
628     
629                         if ( source.exists() )
630                         {
631                             String outputITextName = doc.substring( 0, doc.lastIndexOf( '.' ) + 1 ) + "xml";
632                             File outputITextFileTmp = new File( outputDirectory, outputITextName );
633                             outputITextFileTmp.deleteOnExit();
634                             if ( !outputITextFileTmp.getParentFile().exists() )
635                             {
636                                 outputITextFileTmp.getParentFile().mkdirs();
637                             }
638     
639                             iTextFiles.add( outputITextFileTmp );
640                             parse( source, module, outputITextFileTmp, context );
641                         }
642                     }
643                 }
644             }
645         }
646 
647         return iTextFiles;
648     }
649 
650     /**
651      * @param logo
652      * @param parentFile
653      * @return the logo url or null if unable to create it.
654      * @since 1.1.1
655      */
656     private String getLogoURL( String logo, File parentFile )
657     {
658         if ( logo == null )
659         {
660             return null;
661         }
662 
663         try
664         {
665             return new URL( logo ).toString();
666         }
667         catch ( MalformedURLException e )
668         {
669             try
670             {
671                 File f = new File( parentFile, logo );
672                 if ( !f.exists() )
673                 {
674                     getLogger().warn( "The logo " + f.getAbsolutePath() + " doesnt exist. IGNORING" );
675                 }
676                 else
677                 {
678                     return f.toURI().toURL().toString();
679                 }
680             }
681             catch ( MalformedURLException e1 )
682             {
683                 getLogger().debug( "Failed to convert to URL: " + logo, e1 );
684             }
685         }
686 
687         return null;
688     }
689 }