View Javadoc
1   package org.apache.maven.doxia.docrenderer.pdf.fo;
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.IOException;
24  import java.io.Writer;
25  import java.util.Collection;
26  import java.util.List;
27  import java.util.Locale;
28  import java.util.Map;
29  
30  import javax.xml.transform.TransformerException;
31  
32  import org.apache.maven.doxia.docrenderer.DocumentRendererContext;
33  import org.apache.maven.doxia.docrenderer.DocumentRendererException;
34  import org.apache.maven.doxia.docrenderer.pdf.AbstractPdfRenderer;
35  import org.apache.maven.doxia.docrenderer.pdf.PdfRenderer;
36  import org.apache.maven.doxia.document.DocumentModel;
37  import org.apache.maven.doxia.document.DocumentTOC;
38  import org.apache.maven.doxia.document.DocumentTOCItem;
39  import org.apache.maven.doxia.module.fo.FoAggregateSink;
40  import org.apache.maven.doxia.module.fo.FoSink;
41  import org.apache.maven.doxia.module.fo.FoSinkFactory;
42  import org.apache.maven.doxia.module.fo.FoUtils;
43  import org.apache.maven.doxia.parser.module.ParserModule;
44  
45  import org.codehaus.plexus.component.annotations.Component;
46  import org.codehaus.plexus.util.IOUtil;
47  import org.codehaus.plexus.util.StringUtils;
48  import org.codehaus.plexus.util.WriterFactory;
49  
50  import org.xml.sax.SAXParseException;
51  
52  /**
53   * PDF renderer that uses Doxia's FO module.
54   *
55   * @author ltheussl
56   * @since 1.1
57   */
58  @Component( role = PdfRenderer.class, hint = "fo" )
59  public class FoPdfRenderer
60      extends AbstractPdfRenderer
61  {
62      /**
63       * {@inheritDoc}
64       * @see org.apache.maven.doxia.module.fo.FoUtils#convertFO2PDF(File, File, String)
65       */
66      public void generatePdf( File inputFile, File pdfFile )
67          throws DocumentRendererException
68      {
69          // Should take care of the document model for the metadata...
70          generatePdf( inputFile, pdfFile, null );
71      }
72  
73      /** {@inheritDoc} */
74      @Override
75      public void render( Map<String, ParserModule> filesToProcess, File outputDirectory, DocumentModel documentModel )
76          throws DocumentRendererException, IOException
77      {
78          render( filesToProcess, outputDirectory, documentModel, null );
79      }
80  
81      /** {@inheritDoc} */
82      @Override
83      public void render( Map<String, ParserModule> filesToProcess, File outputDirectory, DocumentModel documentModel,
84                          DocumentRendererContext context )
85          throws DocumentRendererException, IOException
86      {
87          // copy resources, images, etc.
88          copyResources( outputDirectory );
89  
90          if ( documentModel == null )
91          {
92              getLogger().debug( "No document model, generating all documents individually." );
93  
94              renderIndividual( filesToProcess, outputDirectory, context );
95              return;
96          }
97  
98          String outputName = getOutputName( documentModel );
99  
100         File outputFOFile = new File( outputDirectory, outputName + ".fo" );
101         if ( !outputFOFile.getParentFile().exists() )
102         {
103             outputFOFile.getParentFile().mkdirs();
104         }
105 
106         File pdfOutputFile = new File( outputDirectory, outputName + ".pdf" );
107         if ( !pdfOutputFile.getParentFile().exists() )
108         {
109             pdfOutputFile.getParentFile().mkdirs();
110         }
111 
112         Writer writer = null;
113         try
114         {
115             writer = WriterFactory.newXmlWriter( outputFOFile );
116 
117             FoAggregateSink sink = new FoAggregateSink( writer );
118 
119             File fOConfigFile = new File( outputDirectory, "pdf-config.xml" );
120 
121             if ( fOConfigFile.exists() )
122             {
123                 sink.load( fOConfigFile );
124                 getLogger().debug( "Loaded pdf config file: " + fOConfigFile.getAbsolutePath() );
125             }
126 
127             String generateTOC =
128                 ( context != null && context.get( "generateTOC" ) != null )
129                         ? context.get( "generateTOC" ).toString().trim()
130                         : "start";
131             int tocPosition = 0;
132             if ( "start".equalsIgnoreCase( generateTOC ) )
133             {
134                 tocPosition = FoAggregateSink.TOC_START;
135             }
136             else if ( "end".equalsIgnoreCase( generateTOC ) )
137             {
138                 tocPosition = FoAggregateSink.TOC_END;
139             }
140             else
141             {
142                 tocPosition = FoAggregateSink.TOC_NONE;
143             }
144             sink.setDocumentModel( documentModel, tocPosition );
145 
146             sink.beginDocument();
147 
148             sink.coverPage();
149 
150             if ( tocPosition == FoAggregateSink.TOC_START )
151             {
152                 sink.toc();
153             }
154 
155             if ( ( documentModel.getToc() == null ) || ( documentModel.getToc().getItems() == null ) )
156             {
157                 getLogger().info( "No TOC is defined in the document descriptor. Merging all documents." );
158 
159                 mergeAllSources( filesToProcess, sink, context );
160             }
161             else
162             {
163                 getLogger().debug( "Using TOC defined in the document descriptor." );
164 
165                 mergeSourcesFromTOC( documentModel.getToc(), sink, context );
166             }
167 
168             if ( tocPosition == FoAggregateSink.TOC_END )
169             {
170                 sink.toc();
171             }
172 
173             sink.endDocument();
174         }
175         finally
176         {
177             IOUtil.close( writer );
178         }
179 
180         generatePdf( outputFOFile, pdfOutputFile, documentModel );
181     }
182 
183     /** {@inheritDoc} */
184     @Override
185     public void renderIndividual( Map<String, ParserModule> filesToProcess, File outputDirectory )
186         throws DocumentRendererException, IOException
187     {
188         renderIndividual( filesToProcess, outputDirectory, null );
189     }
190 
191     /** {@inheritDoc} */
192     @Override
193     public void renderIndividual( Map<String, ParserModule> filesToProcess, File outputDirectory,
194                                   DocumentRendererContext context )
195         throws DocumentRendererException, IOException
196     {
197         for ( Map.Entry<String, ParserModule> entry : filesToProcess.entrySet() )
198         {
199             String key = entry.getKey();
200             ParserModule module = entry.getValue();
201 
202             File fullDoc = new File( getBaseDir(), module.getSourceDirectory() + File.separator + key );
203 
204             String output = key;
205             for ( String extension : module.getExtensions() )
206             {
207                 String lowerCaseExtension = extension.toLowerCase( Locale.ENGLISH );
208                 if ( output.toLowerCase( Locale.ENGLISH ).indexOf( "." + lowerCaseExtension ) != -1 )
209                 {
210                     output =
211                         output.substring( 0, output.toLowerCase( Locale.ENGLISH ).indexOf( "." + lowerCaseExtension ) );
212                 }
213             }
214 
215             File outputFOFile = new File( outputDirectory, output + ".fo" );
216             if ( !outputFOFile.getParentFile().exists() )
217             {
218                 outputFOFile.getParentFile().mkdirs();
219             }
220 
221             File pdfOutputFile = new File( outputDirectory, output + ".pdf" );
222             if ( !pdfOutputFile.getParentFile().exists() )
223             {
224                 pdfOutputFile.getParentFile().mkdirs();
225             }
226 
227             FoSink sink =
228                 (FoSink) new FoSinkFactory().createSink( outputFOFile.getParentFile(), outputFOFile.getName() );
229             sink.beginDocument();
230             parse( fullDoc.getAbsolutePath(), module.getParserId(), sink, context );
231             sink.endDocument();
232 
233             generatePdf( outputFOFile, pdfOutputFile, null );
234         }
235     }
236 
237     private void mergeAllSources( Map<String, ParserModule> filesToProcess, FoAggregateSink sink,
238                                   DocumentRendererContext context )
239         throws DocumentRendererException, IOException
240     {
241         for ( Map.Entry<String, ParserModule> entry : filesToProcess.entrySet() )
242         {
243             String key = entry.getKey();
244             ParserModule module = entry.getValue();
245             sink.setDocumentName( key );
246             File fullDoc = new File( getBaseDir(), module.getSourceDirectory() + File.separator + key );
247 
248             parse( fullDoc.getAbsolutePath(), module.getParserId(), sink, context );
249         }
250     }
251 
252     private void mergeSourcesFromTOC( DocumentTOC toc, FoAggregateSink sink, DocumentRendererContext context )
253         throws IOException, DocumentRendererException
254     {
255         parseTocItems( toc.getItems(), sink, context );
256     }
257 
258     private void parseTocItems( List<DocumentTOCItem> items, FoAggregateSink sink, DocumentRendererContext context )
259         throws IOException, DocumentRendererException
260     {
261         for ( DocumentTOCItem tocItem : items )
262         {
263             if ( tocItem.getRef() == null )
264             {
265                 if ( getLogger().isInfoEnabled() )
266                 {
267                     getLogger().info( "No ref defined for tocItem " + tocItem.getName() );
268                 }
269 
270                 continue;
271             }
272 
273             String href = StringUtils.replace( tocItem.getRef(), "\\", "/" );
274             if ( href.lastIndexOf( '.' ) != -1 )
275             {
276                 href = href.substring( 0, href.lastIndexOf( '.' ) );
277             }
278 
279             renderModules( href, sink, tocItem, context );
280 
281             if ( tocItem.getItems() != null )
282             {
283                 parseTocItems( tocItem.getItems(), sink, context );
284             }
285         }
286     }
287 
288     private void renderModules( String href, FoAggregateSink sink, DocumentTOCItem tocItem,
289                                 DocumentRendererContext context )
290         throws DocumentRendererException, IOException
291     {
292         Collection<ParserModule> modules = parserModuleManager.getParserModules();
293         for ( ParserModule module : modules )
294         {
295             File moduleBasedir = new File( getBaseDir(), module.getSourceDirectory() );
296 
297             if ( moduleBasedir.exists() )
298             {
299                 for ( String extension : module.getExtensions() )
300                 {
301                     String doc = href + "." + extension;
302                     File source = new File( moduleBasedir, doc );
303     
304                     // Velocity file?
305                     if ( !source.exists() )
306                     {
307                         if ( href.indexOf( "." + extension ) != -1 )
308                         {
309                             doc = href + ".vm";
310                         }
311                         else
312                         {
313                             doc = href + "." + extension + ".vm";
314                         }
315                         source = new File( moduleBasedir, doc );
316                     }
317     
318                     if ( source.exists() )
319                     {
320                         sink.setDocumentName( doc );
321                         sink.setDocumentTitle( tocItem.getName() );
322     
323                         parse( source.getPath(), module.getParserId(), sink, context );
324                     }
325                 }
326             }
327         }
328     }
329 
330     /**
331      * @param inputFile
332      * @param pdfFile
333      * @param documentModel could be null
334      * @throws DocumentRendererException if any
335      * @since 1.1.1
336      */
337     private void generatePdf( File inputFile, File pdfFile, DocumentModel documentModel )
338         throws DocumentRendererException
339     {
340         if ( getLogger().isDebugEnabled() )
341         {
342             getLogger().debug( "Generating: " + pdfFile );
343         }
344 
345         try
346         {
347             FoUtils.convertFO2PDF( inputFile, pdfFile, null, documentModel );
348         }
349         catch ( TransformerException e )
350         {
351             if ( ( e.getCause() != null ) && ( e.getCause() instanceof SAXParseException ) )
352             {
353                 SAXParseException sax = (SAXParseException) e.getCause();
354 
355                 StringBuilder sb = new StringBuilder();
356                 sb.append( "Error creating PDF from " ).append( inputFile.getAbsolutePath() ).append( ":" )
357                   .append( sax.getLineNumber() ).append( ":" ).append( sax.getColumnNumber() ).append( "\n" );
358                 sb.append( e.getMessage() );
359 
360                 throw new DocumentRendererException( sb.toString() );
361             }
362 
363             throw new DocumentRendererException( "Error creating PDF from " + inputFile + ": " + e.getMessage() );
364         }
365     }
366 }