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