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