View Javadoc

1   package org.apache.maven.doxia.docrenderer.itext;
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.Reader;
25  import java.io.Writer;
26  import java.net.URL;
27  import java.net.URLClassLoader;
28  import java.util.Collection;
29  import java.util.Date;
30  import java.util.Iterator;
31  import java.util.LinkedList;
32  import java.util.List;
33  
34  import javax.xml.parsers.DocumentBuilderFactory;
35  import javax.xml.parsers.ParserConfigurationException;
36  import javax.xml.transform.OutputKeys;
37  import javax.xml.transform.Transformer;
38  import javax.xml.transform.TransformerConfigurationException;
39  import javax.xml.transform.TransformerException;
40  import javax.xml.transform.TransformerFactory;
41  import javax.xml.transform.dom.DOMSource;
42  import javax.xml.transform.stream.StreamResult;
43  import javax.xml.transform.stream.StreamSource;
44  
45  import org.apache.maven.doxia.Doxia;
46  import org.apache.maven.doxia.docrenderer.DocRenderer;
47  import org.apache.maven.doxia.docrenderer.DocumentRendererException;
48  import org.apache.maven.doxia.document.DocumentModel;
49  import org.apache.maven.doxia.document.DocumentTOCItem;
50  import org.apache.maven.doxia.document.io.xpp3.DocumentXpp3Reader;
51  import org.apache.maven.doxia.module.itext.ITextSink;
52  import org.apache.maven.doxia.module.itext.ITextSinkFactory;
53  import org.apache.maven.doxia.module.itext.ITextUtil;
54  import org.apache.maven.doxia.module.site.SiteModule;
55  import org.apache.maven.doxia.module.site.manager.SiteModuleManager;
56  import org.apache.maven.doxia.parser.ParseException;
57  import org.apache.maven.doxia.parser.manager.ParserNotFoundException;
58  import org.apache.xml.utils.DefaultErrorHandler;
59  import org.codehaus.plexus.logging.AbstractLogEnabled;
60  import org.codehaus.plexus.util.FileUtils;
61  import org.codehaus.plexus.util.IOUtil;
62  import org.codehaus.plexus.util.ReaderFactory;
63  import org.codehaus.plexus.util.StringUtils;
64  import org.codehaus.plexus.util.WriterFactory;
65  import org.codehaus.plexus.util.xml.XmlUtil;
66  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
67  import org.w3c.dom.DOMException;
68  import org.w3c.dom.Document;
69  import org.w3c.dom.Node;
70  import org.xml.sax.SAXException;
71  
72  import com.lowagie.text.ElementTags;
73  
74  /**
75   * Abstract <code>document</code> render with the <code>iText</code> framework
76   *
77   * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
78   * @version $Id: AbstractITextRender.java 1185508 2011-10-18 06:58:50Z ltheussl $
79   * @deprecated since 1.1, use an implementation of {@link org.apache.maven.doxia.docrenderer.DocumentRenderer}.
80   */
81  public abstract class AbstractITextRender
82      extends AbstractLogEnabled
83      implements DocRenderer
84  {
85      private static final String XSLT_RESOURCE = "org/apache/maven/doxia/docrenderer/pdf/itext/TOC.xslt";
86  
87      private static final TransformerFactory TRANSFORMER_FACTORY = TransformerFactory.newInstance();
88  
89      private static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory.newInstance();
90  
91      /**
92       * @plexus.requirement
93       */
94      protected SiteModuleManager siteModuleManager;
95  
96      /**
97       * @plexus.requirement
98       */
99      protected Doxia doxia;
100 
101     static
102     {
103         TRANSFORMER_FACTORY.setErrorListener( new DefaultErrorHandler() );
104     }
105 
106     /** {@inheritDoc} */
107     public void render( File siteDirectory, File outputDirectory )
108         throws DocumentRendererException, IOException
109     {
110         Collection<SiteModule> modules = siteModuleManager.getSiteModules();
111         for ( SiteModule module : modules )
112         {
113             File moduleBasedir = new File( siteDirectory, module.getSourceDirectory() );
114 
115             if ( moduleBasedir.exists() )
116             {
117                 @SuppressWarnings ( "unchecked" )
118                 List<String> docs =
119                     FileUtils.getFileNames( moduleBasedir, "**/*." + module.getExtension(), null, false );
120 
121                 for ( String doc : docs )
122                 {
123                     String fullPathDoc = new File( moduleBasedir, doc ).getPath();
124 
125                     String outputITextName = doc.substring( 0, doc.indexOf( '.') + 1 ) + "xml";
126                     File outputITextFile = new File( outputDirectory, outputITextName );
127                     if ( !outputITextFile.getParentFile().exists() )
128                     {
129                         outputITextFile.getParentFile().mkdirs();
130                     }
131                     String iTextOutputName = doc.substring( 0, doc.indexOf( '.') + 1 ) + getOutputExtension();
132                     File iTextOutputFile = new File( outputDirectory, iTextOutputName );
133                     if ( !iTextOutputFile.getParentFile().exists() )
134                     {
135                         iTextOutputFile.getParentFile().mkdirs();
136                     }
137 
138                     parse( fullPathDoc, module, outputITextFile );
139 
140                     generateOutput( outputITextFile, iTextOutputFile );
141                 }
142             }
143         }
144     }
145 
146     /** {@inheritDoc} */
147     public void render( File siteDirectory, File outputDirectory, File documentDescriptor )
148         throws DocumentRendererException, IOException
149     {
150         if ( ( documentDescriptor == null ) || ( !documentDescriptor.exists() ) )
151         {
152             if ( getLogger().isInfoEnabled() )
153             {
154                 getLogger().info( "No documentDescriptor is found. Generate all documents." );
155             }
156             render( siteDirectory, outputDirectory );
157             return;
158         }
159 
160         DocumentModel documentModel;
161         Reader reader = null;
162         try
163         {
164             reader = ReaderFactory.newXmlReader( documentDescriptor );
165             documentModel = new DocumentXpp3Reader().read( reader );
166         }
167         catch ( XmlPullParserException e )
168         {
169             throw new DocumentRendererException( "Error parsing document descriptor", e );
170         }
171         catch ( IOException e )
172         {
173             throw new DocumentRendererException( "Error reading document descriptor", e );
174         }
175         finally
176         {
177             IOUtil.close( reader );
178         }
179 
180         if ( documentModel.getOutputName() == null )
181         {
182             if ( getLogger().isInfoEnabled() )
183             {
184                 getLogger().info( "No outputName is defined in the document descriptor. Using 'generated_itext'" );
185             }
186             documentModel.setOutputName( "generated_itext" );
187         }
188 
189         if ( ( documentModel.getToc() == null ) || ( documentModel.getToc().getItems() == null ) )
190         {
191             if ( getLogger().isInfoEnabled() )
192             {
193                 getLogger().info( "No TOC is defined in the document descriptor. Merging all documents." );
194             }
195         }
196 
197         List<File> iTextFiles = new LinkedList<File>();
198         Collection<SiteModule> modules = siteModuleManager.getSiteModules();
199         for ( SiteModule module : modules )
200         {
201             File moduleBasedir = new File( siteDirectory, module.getSourceDirectory() );
202 
203             if ( moduleBasedir.exists() )
204             {
205                 @SuppressWarnings ( "unchecked" )
206                 List<String> docs =
207                     FileUtils.getFileNames( moduleBasedir, "**/*." + module.getExtension(), null, false );
208 
209                 for ( String doc : docs )
210                 {
211                     String fullPathDoc = new File( moduleBasedir, doc ).getPath();
212 
213                     String outputITextName = doc.substring( 0, doc.lastIndexOf( '.') + 1 ) + "xml";
214                     File outputITextFile = new File( outputDirectory, outputITextName );
215 
216                     if ( ( documentModel.getToc() == null ) || ( documentModel.getToc().getItems() == null ) )
217                     {
218                         iTextFiles.add( outputITextFile );
219 
220                         if ( !outputITextFile.getParentFile().exists() )
221                         {
222                             outputITextFile.getParentFile().mkdirs();
223                         }
224 
225                         parse( fullPathDoc, module, outputITextFile );
226                     }
227                     else
228                     {
229                         for ( Iterator<DocumentTOCItem> k = documentModel.getToc().getItems().iterator(); k.hasNext(); )
230                         {
231                             DocumentTOCItem tocItem = k.next();
232 
233                             if ( tocItem.getRef() == null )
234                             {
235                                 if ( getLogger().isInfoEnabled() )
236                                 {
237                                     getLogger().info( "No ref defined for an tocItem in the document descriptor." );
238                                 }
239                                 continue;
240                             }
241 
242                             String outTmp = StringUtils.replace( outputITextFile.getAbsolutePath(), "\\", "/" );
243                             outTmp = outTmp.substring( 0, outTmp.lastIndexOf( '.') );
244 
245                             String outRef = StringUtils.replace( tocItem.getRef(), "\\", "/" );
246                             if ( outRef.lastIndexOf( '.') != -1 )
247                             {
248                                 outRef = outRef.substring( 0, outRef.lastIndexOf( '.') );
249                             }
250                             else
251                             {
252                                 outRef = outRef.substring( 0, outRef.length() );
253                             }
254 
255                             if ( outTmp.indexOf( outRef ) != -1 )
256                             {
257                                 iTextFiles.add( outputITextFile );
258 
259                                 if ( !outputITextFile.getParentFile().exists() )
260                                 {
261                                     outputITextFile.getParentFile().mkdirs();
262                                 }
263 
264                                 parse( fullPathDoc, module, outputITextFile );
265                             }
266                         }
267                     }
268                 }
269             }
270         }
271 
272         File iTextFile = new File( outputDirectory, documentModel.getOutputName() + ".xml" );
273         File iTextOutput = new File( outputDirectory, documentModel.getOutputName() + "." + getOutputExtension() );
274         Document document = generateDocument( iTextFiles );
275         transform( documentModel, document, iTextFile );
276         generateOutput( iTextFile, iTextOutput );
277     }
278 
279     /**
280      * Generate an ouput file with the iText framework
281      *
282      * @param iTextFile
283      * @param iTextOutput
284      * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any
285      * @throws java.io.IOException if any
286      */
287     public abstract void generateOutput( File iTextFile, File iTextOutput )
288         throws DocumentRendererException, IOException;
289 
290     /**
291      * Parse a sink
292      *
293      * @param fullPathDoc
294      * @param module
295      * @param outputITextFile
296      * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException
297      * @throws java.io.IOException
298      */
299     private void parse( String fullPathDoc, SiteModule module, File outputITextFile )
300         throws DocumentRendererException, IOException
301     {
302         Writer writer = WriterFactory.newXmlWriter( outputITextFile );
303         ITextSink sink = (ITextSink) new ITextSinkFactory().createSink( writer );
304 
305         sink.setClassLoader( new URLClassLoader( new URL[] { outputITextFile.getParentFile().toURI().toURL() } ) );
306 
307         Reader reader = null;
308         try
309         {
310             File f = new File( fullPathDoc );
311             if ( XmlUtil.isXml( f ) )
312             {
313                 reader = ReaderFactory.newXmlReader( f );
314             }
315             else
316             {
317                 // TODO Platform dependent?
318                 reader = ReaderFactory.newPlatformReader( f );
319             }
320 
321             System.setProperty( "itext.basedir", outputITextFile.getParentFile().getAbsolutePath() );
322 
323             doxia.parse( reader, module.getParserId(), sink );
324         }
325         catch ( ParserNotFoundException e )
326         {
327             throw new DocumentRendererException( "Error getting a parser for '"
328                     + fullPathDoc + "': " + e.getMessage() );
329         }
330         catch ( ParseException e )
331         {
332             throw new DocumentRendererException( "Error parsing '"
333                     + fullPathDoc + "': line [" + e.getLineNumber() + "] " + e.getMessage(), e );
334         }
335         finally
336         {
337             IOUtil.close( reader );
338 
339             sink.flush();
340 
341             sink.close();
342 
343             IOUtil.close( writer );
344 
345             System.getProperties().remove( "itext.basedir" );
346         }
347     }
348 
349     /**
350      * Merge all iTextFiles to a single one
351      *
352      * @param iTextFiles
353      * @return a document
354      * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any
355      * @throws java.io.IOException if any
356      */
357     private Document generateDocument( List<File> iTextFiles )
358         throws DocumentRendererException, IOException
359     {
360         Document document;
361         try
362         {
363             document = DOCUMENT_BUILDER_FACTORY.newDocumentBuilder().newDocument();
364         }
365         catch ( ParserConfigurationException e )
366         {
367             throw new DocumentRendererException( "Error building document :" + e.getMessage() );
368         }
369         document.appendChild( document.createElement( ElementTags.ITEXT ) ); // Used only to set a root
370 
371         for ( File iTextFile : iTextFiles )
372         {
373             Document iTextDocument;
374             try
375             {
376                 iTextDocument = DOCUMENT_BUILDER_FACTORY.newDocumentBuilder().parse( iTextFile );
377             }
378             catch ( SAXException e )
379             {
380                 throw new DocumentRendererException( "SAX Error : " + e.getMessage() );
381             }
382             catch ( ParserConfigurationException e )
383             {
384                 throw new DocumentRendererException( "Error parsing configuration : " + e.getMessage() );
385             }
386 
387             // Only one chapter per doc
388             Node chapter = iTextDocument.getElementsByTagName( ElementTags.CHAPTER ).item( 0 );
389             try
390             {
391                 document.getDocumentElement().appendChild( document.importNode( chapter, true ) );
392             }
393             catch ( DOMException e )
394             {
395                 throw new DocumentRendererException( "Error appending chapter for "
396                         + iTextFile + " : " + e.getMessage() );
397             }
398         }
399 
400         return document;
401     }
402 
403     /**
404      * Init the transformer object
405      *
406      * @return an instanced transformer object
407      * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any
408      */
409     private Transformer initTransformer()
410         throws DocumentRendererException
411     {
412         try
413         {
414             Transformer transformer = TRANSFORMER_FACTORY.newTransformer( new StreamSource( DefaultPdfRenderer.class
415                 .getResourceAsStream( "/" + XSLT_RESOURCE ) ) );
416             transformer.setErrorListener( TRANSFORMER_FACTORY.getErrorListener() );
417 
418             transformer.setOutputProperty( OutputKeys.OMIT_XML_DECLARATION, "false" );
419             transformer.setOutputProperty( OutputKeys.INDENT, "yes" );
420             transformer.setOutputProperty( OutputKeys.METHOD, "xml" );
421             transformer.setOutputProperty( OutputKeys.ENCODING, "UTF-8" );
422 
423             return transformer;
424         }
425         catch ( TransformerConfigurationException e )
426         {
427             throw new DocumentRendererException( "Error configuring Transformer for " + XSLT_RESOURCE + ": "
428                 + e.getMessage() );
429         }
430         catch ( IllegalArgumentException e )
431         {
432             throw new DocumentRendererException( "Error configuring Transformer for " + XSLT_RESOURCE + ": "
433                 + e.getMessage() );
434         }
435     }
436 
437     /**
438      * Add transformer parameters
439      *
440      * @param transformer
441      * @param documentModel
442      */
443     private void addTransformerParameters( Transformer transformer, DocumentModel documentModel )
444     {
445         if ( documentModel.getMeta().getTitle() != null )
446         {
447             transformer.setParameter( "title", documentModel.getMeta().getTitle() );
448         }
449         if ( documentModel.getMeta().getAuthor() != null )
450         {
451             transformer.setParameter( "author", documentModel.getMeta().getAuthor() );
452         }
453         transformer.setParameter( "creationdate", new Date().toString() );
454         if ( documentModel.getMeta().getSubject() != null )
455         {
456             transformer.setParameter( "subject", documentModel.getMeta().getSubject() );
457         }
458         if ( documentModel.getMeta().getKeywords() != null )
459         {
460             transformer.setParameter( "keywords", documentModel.getMeta().getKeywords() );
461         }
462         transformer.setParameter( "producer", "Generated with Doxia by " + System.getProperty( "user.name" ) );
463         if ( ITextUtil.isPageSizeSupported( documentModel.getMeta().getTitle() ) )
464         {
465             transformer.setParameter( "pagesize", documentModel.getMeta().getPageSize() );
466         }
467         else
468         {
469             transformer.setParameter( "pagesize", "A4" );
470         }
471 
472         transformer.setParameter( "frontPageHeader", "" );
473         if ( documentModel.getMeta().getTitle() != null )
474         {
475             transformer.setParameter( "frontPageTitle", documentModel.getMeta().getTitle() );
476         }
477         transformer.setParameter( "frontPageFooter", "Generated date " + new Date().toString() );
478     }
479 
480     /**
481      * Transform a document to an iTextFile
482      *
483      * @param documentModel
484      * @param document
485      * @param iTextFile
486      * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any.
487      */
488     private void transform( DocumentModel documentModel, Document document, File iTextFile )
489         throws DocumentRendererException
490     {
491         Transformer transformer = initTransformer();
492 
493         addTransformerParameters( transformer, documentModel );
494 
495         try
496         {
497             transformer.transform( new DOMSource( document ), new StreamResult( iTextFile ) );
498         }
499         catch ( TransformerException e )
500         {
501             throw new DocumentRendererException( "Error transformer Document from "
502                     + document + ": " + e.getMessage() );
503         }
504     }
505 }