1 package org.apache.maven.doxia.docrenderer.pdf.fo;
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.Writer;
25 import java.util.Collection;
26 import java.util.List;
27 import java.util.Locale;
28 import java.util.Map;
29
30 import javax.xml.transform.TransformerException;
31
32 import org.apache.maven.doxia.docrenderer.DocumentRendererContext;
33 import org.apache.maven.doxia.docrenderer.DocumentRendererException;
34 import org.apache.maven.doxia.docrenderer.pdf.AbstractPdfRenderer;
35 import org.apache.maven.doxia.docrenderer.pdf.PdfRenderer;
36 import org.apache.maven.doxia.document.DocumentModel;
37 import org.apache.maven.doxia.document.DocumentTOC;
38 import org.apache.maven.doxia.document.DocumentTOCItem;
39 import org.apache.maven.doxia.module.fo.FoAggregateSink;
40 import org.apache.maven.doxia.module.fo.FoSink;
41 import org.apache.maven.doxia.module.fo.FoSinkFactory;
42 import org.apache.maven.doxia.module.fo.FoUtils;
43 import org.apache.maven.doxia.parser.module.ParserModule;
44
45 import org.codehaus.plexus.component.annotations.Component;
46 import org.codehaus.plexus.util.IOUtil;
47 import org.codehaus.plexus.util.StringUtils;
48 import org.codehaus.plexus.util.WriterFactory;
49
50 import org.xml.sax.SAXParseException;
51
52
53
54
55
56
57
58 @Component( role = PdfRenderer.class, hint = "fo" )
59 public class FoPdfRenderer
60 extends AbstractPdfRenderer
61 {
62
63
64
65
66 public void generatePdf( File inputFile, File pdfFile )
67 throws DocumentRendererException
68 {
69
70 generatePdf( inputFile, pdfFile, null );
71 }
72
73
74 @Override
75 public void render( Map<String, ParserModule> filesToProcess, File outputDirectory, DocumentModel documentModel )
76 throws DocumentRendererException, IOException
77 {
78 render( filesToProcess, outputDirectory, documentModel, null );
79 }
80
81
82 @Override
83 public void render( Map<String, ParserModule> filesToProcess, File outputDirectory, DocumentModel documentModel,
84 DocumentRendererContext context )
85 throws DocumentRendererException, IOException
86 {
87
88 copyResources( outputDirectory );
89
90 if ( documentModel == null )
91 {
92 getLogger().debug( "No document model, generating all documents individually." );
93
94 renderIndividual( filesToProcess, outputDirectory, context );
95 return;
96 }
97
98 String outputName = getOutputName( documentModel );
99
100 File outputFOFile = new File( outputDirectory, outputName + ".fo" );
101 if ( !outputFOFile.getParentFile().exists() )
102 {
103 outputFOFile.getParentFile().mkdirs();
104 }
105
106 File pdfOutputFile = new File( outputDirectory, outputName + ".pdf" );
107 if ( !pdfOutputFile.getParentFile().exists() )
108 {
109 pdfOutputFile.getParentFile().mkdirs();
110 }
111
112 Writer writer = null;
113 try
114 {
115 writer = WriterFactory.newXmlWriter( outputFOFile );
116
117 FoAggregateSink sink = new FoAggregateSink( writer );
118
119 File fOConfigFile = new File( outputDirectory, "pdf-config.xml" );
120
121 if ( fOConfigFile.exists() )
122 {
123 sink.load( fOConfigFile );
124 getLogger().debug( "Loaded pdf config file: " + fOConfigFile.getAbsolutePath() );
125 }
126
127 String generateTOC =
128 ( context != null && context.get( "generateTOC" ) != null )
129 ? context.get( "generateTOC" ).toString().trim()
130 : "start";
131 int tocPosition = 0;
132 if ( "start".equalsIgnoreCase( generateTOC ) )
133 {
134 tocPosition = FoAggregateSink.TOC_START;
135 }
136 else if ( "end".equalsIgnoreCase( generateTOC ) )
137 {
138 tocPosition = FoAggregateSink.TOC_END;
139 }
140 else
141 {
142 tocPosition = FoAggregateSink.TOC_NONE;
143 }
144 sink.setDocumentModel( documentModel, tocPosition );
145
146 sink.beginDocument();
147
148 sink.coverPage();
149
150 if ( tocPosition == FoAggregateSink.TOC_START )
151 {
152 sink.toc();
153 }
154
155 if ( ( documentModel.getToc() == null ) || ( documentModel.getToc().getItems() == null ) )
156 {
157 getLogger().info( "No TOC is defined in the document descriptor. Merging all documents." );
158
159 mergeAllSources( filesToProcess, sink, context );
160 }
161 else
162 {
163 getLogger().debug( "Using TOC defined in the document descriptor." );
164
165 mergeSourcesFromTOC( documentModel.getToc(), sink, context );
166 }
167
168 if ( tocPosition == FoAggregateSink.TOC_END )
169 {
170 sink.toc();
171 }
172
173 sink.endDocument();
174 }
175 finally
176 {
177 IOUtil.close( writer );
178 }
179
180 generatePdf( outputFOFile, pdfOutputFile, documentModel );
181 }
182
183
184 @Override
185 public void renderIndividual( Map<String, ParserModule> filesToProcess, File outputDirectory )
186 throws DocumentRendererException, IOException
187 {
188 renderIndividual( filesToProcess, outputDirectory, null );
189 }
190
191
192 @Override
193 public void renderIndividual( Map<String, ParserModule> filesToProcess, File outputDirectory,
194 DocumentRendererContext context )
195 throws DocumentRendererException, IOException
196 {
197 for ( Map.Entry<String, ParserModule> entry : filesToProcess.entrySet() )
198 {
199 String key = entry.getKey();
200 ParserModule module = entry.getValue();
201
202 File fullDoc = new File( getBaseDir(), module.getSourceDirectory() + File.separator + key );
203
204 String output = key;
205 for ( String extension : module.getExtensions() )
206 {
207 String lowerCaseExtension = extension.toLowerCase( Locale.ENGLISH );
208 if ( output.toLowerCase( Locale.ENGLISH ).indexOf( "." + lowerCaseExtension ) != -1 )
209 {
210 output =
211 output.substring( 0, output.toLowerCase( Locale.ENGLISH ).indexOf( "." + lowerCaseExtension ) );
212 }
213 }
214
215 File outputFOFile = new File( outputDirectory, output + ".fo" );
216 if ( !outputFOFile.getParentFile().exists() )
217 {
218 outputFOFile.getParentFile().mkdirs();
219 }
220
221 File pdfOutputFile = new File( outputDirectory, output + ".pdf" );
222 if ( !pdfOutputFile.getParentFile().exists() )
223 {
224 pdfOutputFile.getParentFile().mkdirs();
225 }
226
227 FoSink sink =
228 (FoSink) new FoSinkFactory().createSink( outputFOFile.getParentFile(), outputFOFile.getName() );
229 sink.beginDocument();
230 parse( fullDoc.getAbsolutePath(), module.getParserId(), sink, context );
231 sink.endDocument();
232
233 generatePdf( outputFOFile, pdfOutputFile, null );
234 }
235 }
236
237 private void mergeAllSources( Map<String, ParserModule> filesToProcess, FoAggregateSink sink,
238 DocumentRendererContext context )
239 throws DocumentRendererException, IOException
240 {
241 for ( Map.Entry<String, ParserModule> entry : filesToProcess.entrySet() )
242 {
243 String key = entry.getKey();
244 ParserModule module = entry.getValue();
245 sink.setDocumentName( key );
246 File fullDoc = new File( getBaseDir(), module.getSourceDirectory() + File.separator + key );
247
248 parse( fullDoc.getAbsolutePath(), module.getParserId(), sink, context );
249 }
250 }
251
252 private void mergeSourcesFromTOC( DocumentTOC toc, FoAggregateSink sink, DocumentRendererContext context )
253 throws IOException, DocumentRendererException
254 {
255 parseTocItems( toc.getItems(), sink, context );
256 }
257
258 private void parseTocItems( List<DocumentTOCItem> items, FoAggregateSink sink, DocumentRendererContext context )
259 throws IOException, DocumentRendererException
260 {
261 for ( DocumentTOCItem tocItem : items )
262 {
263 if ( tocItem.getRef() == null )
264 {
265 if ( getLogger().isInfoEnabled() )
266 {
267 getLogger().info( "No ref defined for tocItem " + tocItem.getName() );
268 }
269
270 continue;
271 }
272
273 String href = StringUtils.replace( tocItem.getRef(), "\\", "/" );
274 if ( href.lastIndexOf( '.' ) != -1 )
275 {
276 href = href.substring( 0, href.lastIndexOf( '.' ) );
277 }
278
279 renderModules( href, sink, tocItem, context );
280
281 if ( tocItem.getItems() != null )
282 {
283 parseTocItems( tocItem.getItems(), sink, context );
284 }
285 }
286 }
287
288 private void renderModules( String href, FoAggregateSink sink, DocumentTOCItem tocItem,
289 DocumentRendererContext context )
290 throws DocumentRendererException, IOException
291 {
292 Collection<ParserModule> modules = parserModuleManager.getParserModules();
293 for ( ParserModule module : modules )
294 {
295 File moduleBasedir = new File( getBaseDir(), module.getSourceDirectory() );
296
297 if ( moduleBasedir.exists() )
298 {
299 for ( String extension : module.getExtensions() )
300 {
301 String doc = href + "." + extension;
302 File source = new File( moduleBasedir, doc );
303
304
305 if ( !source.exists() )
306 {
307 if ( href.indexOf( "." + extension ) != -1 )
308 {
309 doc = href + ".vm";
310 }
311 else
312 {
313 doc = href + "." + extension + ".vm";
314 }
315 source = new File( moduleBasedir, doc );
316 }
317
318 if ( source.exists() )
319 {
320 sink.setDocumentName( doc );
321 sink.setDocumentTitle( tocItem.getName() );
322
323 parse( source.getPath(), module.getParserId(), sink, context );
324 }
325 }
326 }
327 }
328 }
329
330
331
332
333
334
335
336
337 private void generatePdf( File inputFile, File pdfFile, DocumentModel documentModel )
338 throws DocumentRendererException
339 {
340 if ( getLogger().isDebugEnabled() )
341 {
342 getLogger().debug( "Generating: " + pdfFile );
343 }
344
345 try
346 {
347 FoUtils.convertFO2PDF( inputFile, pdfFile, null, documentModel );
348 }
349 catch ( TransformerException e )
350 {
351 if ( ( e.getCause() != null ) && ( e.getCause() instanceof SAXParseException ) )
352 {
353 SAXParseException sax = (SAXParseException) e.getCause();
354
355 StringBuilder sb = new StringBuilder();
356 sb.append( "Error creating PDF from " ).append( inputFile.getAbsolutePath() ).append( ":" )
357 .append( sax.getLineNumber() ).append( ":" ).append( sax.getColumnNumber() ).append( "\n" );
358 sb.append( e.getMessage() );
359
360 throw new DocumentRendererException( sb.toString() );
361 }
362
363 throw new DocumentRendererException( "Error creating PDF from " + inputFile + ": " + e.getMessage() );
364 }
365 }
366 }