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.module.site.SiteModule;
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 1345598 2012-06-02 22:26:49Z 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, SiteModule> 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, SiteModule> 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 ? 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, SiteModule> 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, SiteModule> filesToProcess, File outputDirectory,
194                                   DocumentRendererContext context )
195         throws DocumentRendererException, IOException
196     {
197         for ( Map.Entry<String, SiteModule> entry : filesToProcess.entrySet() )
198         {
199             String key = entry.getKey();
200             SiteModule module = entry.getValue();
201 
202             File fullDoc = new File( getBaseDir(), module.getSourceDirectory() + File.separator + key );
203 
204             String output = key;
205             String lowerCaseExtension = module.getExtension().toLowerCase( Locale.ENGLISH );
206             if ( output.toLowerCase( Locale.ENGLISH ).indexOf( "." + lowerCaseExtension ) != -1 )
207             {
208                 output =
209                     output.substring( 0, output.toLowerCase( Locale.ENGLISH ).indexOf( "." + lowerCaseExtension ) );
210             }
211 
212             File outputFOFile = new File( outputDirectory, output + ".fo" );
213             if ( !outputFOFile.getParentFile().exists() )
214             {
215                 outputFOFile.getParentFile().mkdirs();
216             }
217 
218             File pdfOutputFile = new File( outputDirectory, output + ".pdf" );
219             if ( !pdfOutputFile.getParentFile().exists() )
220             {
221                 pdfOutputFile.getParentFile().mkdirs();
222             }
223 
224             FoSink sink =
225                 (FoSink) new FoSinkFactory().createSink( outputFOFile.getParentFile(), outputFOFile.getName() );
226             sink.beginDocument();
227             parse( fullDoc.getAbsolutePath(), module.getParserId(), sink, context );
228             sink.endDocument();
229 
230             generatePdf( outputFOFile, pdfOutputFile, null );
231         }
232     }
233 
234     private void mergeAllSources( Map<String, SiteModule> filesToProcess, FoAggregateSink sink,
235                                   DocumentRendererContext context )
236         throws DocumentRendererException, IOException
237     {
238         for ( Map.Entry<String, SiteModule> entry : filesToProcess.entrySet() )
239         {
240             String key = entry.getKey();
241             SiteModule module = entry.getValue();
242             sink.setDocumentName( key );
243             File fullDoc = new File( getBaseDir(), module.getSourceDirectory() + File.separator + key );
244 
245             parse( fullDoc.getAbsolutePath(), module.getParserId(), sink, context );
246         }
247     }
248 
249     private void mergeSourcesFromTOC( DocumentTOC toc, FoAggregateSink sink, DocumentRendererContext context )
250         throws IOException, DocumentRendererException
251     {
252         parseTocItems( toc.getItems(), sink, context );
253     }
254 
255     private void parseTocItems( List<DocumentTOCItem> items, FoAggregateSink sink, DocumentRendererContext context )
256         throws IOException, DocumentRendererException
257     {
258         for ( DocumentTOCItem tocItem : items )
259         {
260             if ( tocItem.getRef() == null )
261             {
262                 if ( getLogger().isInfoEnabled() )
263                 {
264                     getLogger().info( "No ref defined for tocItem " + tocItem.getName() );
265                 }
266 
267                 continue;
268             }
269 
270             String href = StringUtils.replace( tocItem.getRef(), "\\", "/" );
271             if ( href.lastIndexOf( '.') != -1 )
272             {
273                 href = href.substring( 0, href.lastIndexOf( '.') );
274             }
275 
276             renderModules( href, sink, tocItem, context );
277 
278             if ( tocItem.getItems() != null )
279             {
280                 parseTocItems( tocItem.getItems(), sink, context );
281             }
282         }
283     }
284 
285     private void renderModules( String href, FoAggregateSink sink, DocumentTOCItem tocItem,
286                                 DocumentRendererContext context )
287         throws DocumentRendererException, IOException
288     {
289         Collection<SiteModule> modules = siteModuleManager.getSiteModules();
290         for ( SiteModule module : modules )
291         {
292             File moduleBasedir = new File( getBaseDir(), module.getSourceDirectory() );
293 
294             if ( moduleBasedir.exists() )
295             {
296                 String doc = href + "." + module.getExtension();
297                 File source = new File( moduleBasedir, doc );
298 
299                 // Velocity file?
300                 if ( !source.exists() )
301                 {
302                     if ( href.indexOf( "." + module.getExtension() ) != -1 )
303                     {
304                         doc = href + ".vm";
305                     }
306                     else
307                     {
308                         doc = href + "." + module.getExtension() + ".vm";
309                     }
310                     source = new File( moduleBasedir, doc );
311                 }
312 
313                 if ( source.exists() )
314                 {
315                     sink.setDocumentName( doc );
316                     sink.setDocumentTitle( tocItem.getName() );
317 
318                     parse( source.getPath(), module.getParserId(), sink, context );
319                 }
320             }
321         }
322     }
323 
324     /**
325      * @param inputFile
326      * @param pdfFile
327      * @param documentModel could be null
328      * @throws DocumentRendererException if any
329      * @since 1.1.1
330      */
331     private void generatePdf( File inputFile, File pdfFile, DocumentModel documentModel )
332         throws DocumentRendererException
333     {
334         if ( getLogger().isDebugEnabled() )
335         {
336             getLogger().debug( "Generating: " + pdfFile );
337         }
338 
339         try
340         {
341             FoUtils.convertFO2PDF( inputFile, pdfFile, null, documentModel );
342         }
343         catch ( TransformerException e )
344         {
345             if ( ( e.getCause() != null ) && ( e.getCause() instanceof SAXParseException ) )
346             {
347                 SAXParseException sax = (SAXParseException) e.getCause();
348 
349                 StringBuilder sb = new StringBuilder();
350                 sb.append( "Error creating PDF from " ).append( inputFile.getAbsolutePath() ).append( ":" )
351                   .append( sax.getLineNumber() ).append( ":" ).append( sax.getColumnNumber() ).append( "\n" );
352                 sb.append( e.getMessage() );
353 
354                 throw new DocumentRendererException( sb.toString() );
355             }
356 
357             throw new DocumentRendererException( "Error creating PDF from " + inputFile + ": " + e.getMessage() );
358         }
359     }
360 }