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