1 package org.apache.maven.doxia.docrenderer.itext;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
76
77
78
79
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
93
94 protected SiteModuleManager siteModuleManager;
95
96
97
98
99 protected Doxia doxia;
100
101 static
102 {
103 TRANSFORMER_FACTORY.setErrorListener( new DefaultErrorHandler() );
104 }
105
106
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
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
281
282
283
284
285
286
287 public abstract void generateOutput( File iTextFile, File iTextOutput )
288 throws DocumentRendererException, IOException;
289
290
291
292
293
294
295
296
297
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
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
351
352
353
354
355
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 ) );
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
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
405
406
407
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
439
440
441
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
482
483
484
485
486
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 }