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