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 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
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
279
280
281
282
283
284
285 public abstract void generateOutput( File iTextFile, File iTextOutput )
286 throws DocumentRendererException, IOException;
287
288
289
290
291
292
293
294
295
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
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
349
350
351
352
353
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 ) );
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
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
403
404
405
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
437
438
439
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
480
481
482
483
484
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 }