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