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