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