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