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