blob: 50151e0ba43dd26e28ea24a0abd2ed25bec840c0 [file] [log] [blame]
package org.apache.maven.doxia.docrenderer.itext;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.apache.maven.doxia.Doxia;
import org.apache.maven.doxia.docrenderer.DocRenderer;
import org.apache.maven.doxia.docrenderer.DocumentRendererException;
import org.apache.maven.doxia.document.DocumentModel;
import org.apache.maven.doxia.document.DocumentTOCItem;
import org.apache.maven.doxia.document.io.xpp3.DocumentXpp3Reader;
import org.apache.maven.doxia.module.itext.ITextSink;
import org.apache.maven.doxia.module.itext.ITextSinkFactory;
import org.apache.maven.doxia.module.itext.ITextUtil;
import org.apache.maven.doxia.parser.module.ParserModule;
import org.apache.maven.doxia.parser.module.ParserModuleManager;
import org.apache.maven.doxia.parser.ParseException;
import org.apache.maven.doxia.parser.manager.ParserNotFoundException;
import org.apache.xml.utils.DefaultErrorHandler;
import org.codehaus.plexus.component.annotations.Requirement;
import org.codehaus.plexus.logging.AbstractLogEnabled;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.ReaderFactory;
import org.codehaus.plexus.util.StringUtils;
import org.codehaus.plexus.util.WriterFactory;
import org.codehaus.plexus.util.xml.XmlUtil;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
import com.lowagie.text.ElementTags;
/**
* Abstract <code>document</code> render with the <code>iText</code> framework
*
* @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
* @deprecated since 1.1, use an implementation of {@link org.apache.maven.doxia.docrenderer.DocumentRenderer}.
*/
public abstract class AbstractITextRender
extends AbstractLogEnabled
implements DocRenderer
{
private static final String XSLT_RESOURCE = "org/apache/maven/doxia/docrenderer/pdf/itext/TOC.xslt";
private static final TransformerFactory TRANSFORMER_FACTORY = TransformerFactory.newInstance();
private static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory.newInstance();
@Requirement
protected ParserModuleManager parserModuleManager;
@Requirement
protected Doxia doxia;
static
{
TRANSFORMER_FACTORY.setErrorListener( new DefaultErrorHandler() );
}
private List<String> getModuleFileNames( ParserModule module, File moduleBasedir )
throws IOException
{
StringBuilder includes = new StringBuilder();
for ( String extension: module.getExtensions() )
{
if ( includes.length() > 0 )
{
includes.append( ',' );
}
includes.append( "**/*." );
includes.append( extension );
}
return FileUtils.getFileNames( moduleBasedir, includes.toString(), null, false );
}
/** {@inheritDoc} */
public void render( File siteDirectory, File outputDirectory )
throws DocumentRendererException, IOException
{
Collection<ParserModule> modules = parserModuleManager.getParserModules();
for ( ParserModule module : modules )
{
File moduleBasedir = new File( siteDirectory, module.getSourceDirectory() );
if ( moduleBasedir.exists() )
{
List<String> docs = getModuleFileNames( module, moduleBasedir );
for ( String doc : docs )
{
String fullPathDoc = new File( moduleBasedir, doc ).getPath();
String outputITextName = doc.substring( 0, doc.indexOf( '.' ) + 1 ) + "xml";
File outputITextFile = new File( outputDirectory, outputITextName );
if ( !outputITextFile.getParentFile().exists() )
{
outputITextFile.getParentFile().mkdirs();
}
String iTextOutputName = doc.substring( 0, doc.indexOf( '.' ) + 1 ) + getOutputExtension();
File iTextOutputFile = new File( outputDirectory, iTextOutputName );
if ( !iTextOutputFile.getParentFile().exists() )
{
iTextOutputFile.getParentFile().mkdirs();
}
parse( fullPathDoc, module, outputITextFile );
generateOutput( outputITextFile, iTextOutputFile );
}
}
}
}
/** {@inheritDoc} */
public void render( File siteDirectory, File outputDirectory, File documentDescriptor )
throws DocumentRendererException, IOException
{
if ( ( documentDescriptor == null ) || ( !documentDescriptor.exists() ) )
{
if ( getLogger().isInfoEnabled() )
{
getLogger().info( "No documentDescriptor is found. Generate all documents." );
}
render( siteDirectory, outputDirectory );
return;
}
DocumentModel documentModel;
Reader reader = null;
try
{
reader = ReaderFactory.newXmlReader( documentDescriptor );
documentModel = new DocumentXpp3Reader().read( reader );
}
catch ( XmlPullParserException e )
{
throw new DocumentRendererException( "Error parsing document descriptor", e );
}
catch ( IOException e )
{
throw new DocumentRendererException( "Error reading document descriptor", e );
}
finally
{
IOUtil.close( reader );
}
if ( documentModel.getOutputName() == null )
{
if ( getLogger().isInfoEnabled() )
{
getLogger().info( "No outputName is defined in the document descriptor. Using 'generated_itext'" );
}
documentModel.setOutputName( "generated_itext" );
}
if ( ( documentModel.getToc() == null ) || ( documentModel.getToc().getItems() == null ) )
{
if ( getLogger().isInfoEnabled() )
{
getLogger().info( "No TOC is defined in the document descriptor. Merging all documents." );
}
}
List<File> iTextFiles = new LinkedList<File>();
Collection<ParserModule> modules = parserModuleManager.getParserModules();
for ( ParserModule module : modules )
{
File moduleBasedir = new File( siteDirectory, module.getSourceDirectory() );
if ( moduleBasedir.exists() )
{
@SuppressWarnings ( "unchecked" )
List<String> docs = getModuleFileNames( module, moduleBasedir );
for ( String doc : docs )
{
String fullPathDoc = new File( moduleBasedir, doc ).getPath();
String outputITextName = doc.substring( 0, doc.lastIndexOf( '.' ) + 1 ) + "xml";
File outputITextFile = new File( outputDirectory, outputITextName );
if ( ( documentModel.getToc() == null ) || ( documentModel.getToc().getItems() == null ) )
{
iTextFiles.add( outputITextFile );
if ( !outputITextFile.getParentFile().exists() )
{
outputITextFile.getParentFile().mkdirs();
}
parse( fullPathDoc, module, outputITextFile );
}
else
{
for ( Iterator<DocumentTOCItem> k = documentModel.getToc().getItems().iterator(); k.hasNext(); )
{
DocumentTOCItem tocItem = k.next();
if ( tocItem.getRef() == null )
{
if ( getLogger().isInfoEnabled() )
{
getLogger().info( "No ref defined for an tocItem in the document descriptor." );
}
continue;
}
String outTmp = StringUtils.replace( outputITextFile.getAbsolutePath(), "\\", "/" );
outTmp = outTmp.substring( 0, outTmp.lastIndexOf( '.' ) );
String outRef = StringUtils.replace( tocItem.getRef(), "\\", "/" );
if ( outRef.lastIndexOf( '.' ) != -1 )
{
outRef = outRef.substring( 0, outRef.lastIndexOf( '.' ) );
}
else
{
outRef = outRef.substring( 0, outRef.length() );
}
if ( outTmp.indexOf( outRef ) != -1 )
{
iTextFiles.add( outputITextFile );
if ( !outputITextFile.getParentFile().exists() )
{
outputITextFile.getParentFile().mkdirs();
}
parse( fullPathDoc, module, outputITextFile );
}
}
}
}
}
}
File iTextFile = new File( outputDirectory, documentModel.getOutputName() + ".xml" );
File iTextOutput = new File( outputDirectory, documentModel.getOutputName() + "." + getOutputExtension() );
Document document = generateDocument( iTextFiles );
transform( documentModel, document, iTextFile );
generateOutput( iTextFile, iTextOutput );
}
/**
* Generate an output file from the contents of an input with the iText framework
*
* @param iTextFile input file
* @param iTextOutput output file
* @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any
* @throws java.io.IOException if any
*/
public abstract void generateOutput( File iTextFile, File iTextOutput )
throws DocumentRendererException, IOException;
/**
* Parse a sink
*
* @param fullPathDoc
* @param module
* @param outputITextFile
* @throws org.apache.maven.doxia.docrenderer.DocumentRendererException
* @throws java.io.IOException
*/
private void parse( String fullPathDoc, ParserModule module, File outputITextFile )
throws DocumentRendererException, IOException
{
Writer writer = WriterFactory.newXmlWriter( outputITextFile );
ITextSink sink = (ITextSink) new ITextSinkFactory().createSink( writer );
sink.setClassLoader( new URLClassLoader( new URL[] { outputITextFile.getParentFile().toURI().toURL() } ) );
Reader reader = null;
try
{
File f = new File( fullPathDoc );
if ( XmlUtil.isXml( f ) )
{
reader = ReaderFactory.newXmlReader( f );
}
else
{
// TODO Platform dependent?
reader = ReaderFactory.newPlatformReader( f );
}
System.setProperty( "itext.basedir", outputITextFile.getParentFile().getAbsolutePath() );
doxia.parse( reader, module.getParserId(), sink );
}
catch ( ParserNotFoundException e )
{
throw new DocumentRendererException( "Error getting a parser for '"
+ fullPathDoc + "': " + e.getMessage() );
}
catch ( ParseException e )
{
throw new DocumentRendererException( "Error parsing '"
+ fullPathDoc + "': line [" + e.getLineNumber() + "] " + e.getMessage(), e );
}
finally
{
IOUtil.close( reader );
sink.flush();
sink.close();
IOUtil.close( writer );
System.getProperties().remove( "itext.basedir" );
}
}
/**
* Merge all iTextFiles to a single one
*
* @param iTextFiles
* @return a document
* @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any
* @throws java.io.IOException if any
*/
private Document generateDocument( List<File> iTextFiles )
throws DocumentRendererException, IOException
{
Document document;
try
{
document = DOCUMENT_BUILDER_FACTORY.newDocumentBuilder().newDocument();
}
catch ( ParserConfigurationException e )
{
throw new DocumentRendererException( "Error building document :" + e.getMessage() );
}
document.appendChild( document.createElement( ElementTags.ITEXT ) ); // Used only to set a root
for ( File iTextFile : iTextFiles )
{
Document iTextDocument;
try
{
iTextDocument = DOCUMENT_BUILDER_FACTORY.newDocumentBuilder().parse( iTextFile );
}
catch ( SAXException e )
{
throw new DocumentRendererException( "SAX Error : " + e.getMessage() );
}
catch ( ParserConfigurationException e )
{
throw new DocumentRendererException( "Error parsing configuration : " + e.getMessage() );
}
// Only one chapter per doc
Node chapter = iTextDocument.getElementsByTagName( ElementTags.CHAPTER ).item( 0 );
try
{
document.getDocumentElement().appendChild( document.importNode( chapter, true ) );
}
catch ( DOMException e )
{
throw new DocumentRendererException( "Error appending chapter for "
+ iTextFile + " : " + e.getMessage() );
}
}
return document;
}
/**
* Init the transformer object
*
* @return an instanced transformer object
* @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any
*/
private Transformer initTransformer()
throws DocumentRendererException
{
try
{
Transformer transformer = TRANSFORMER_FACTORY.newTransformer( new StreamSource( DefaultPdfRenderer.class
.getResourceAsStream( "/" + XSLT_RESOURCE ) ) );
transformer.setErrorListener( TRANSFORMER_FACTORY.getErrorListener() );
transformer.setOutputProperty( OutputKeys.OMIT_XML_DECLARATION, "false" );
transformer.setOutputProperty( OutputKeys.INDENT, "yes" );
transformer.setOutputProperty( OutputKeys.METHOD, "xml" );
transformer.setOutputProperty( OutputKeys.ENCODING, "UTF-8" );
return transformer;
}
catch ( TransformerConfigurationException e )
{
throw new DocumentRendererException( "Error configuring Transformer for " + XSLT_RESOURCE + ": "
+ e.getMessage() );
}
catch ( IllegalArgumentException e )
{
throw new DocumentRendererException( "Error configuring Transformer for " + XSLT_RESOURCE + ": "
+ e.getMessage() );
}
}
/**
* Add transformer parameters
*
* @param transformer
* @param documentModel
*/
private void addTransformerParameters( Transformer transformer, DocumentModel documentModel )
{
if ( documentModel.getMeta().getTitle() != null )
{
transformer.setParameter( "title", documentModel.getMeta().getTitle() );
}
if ( documentModel.getMeta().getAuthor() != null )
{
transformer.setParameter( "author", documentModel.getMeta().getAuthor() );
}
transformer.setParameter( "creationdate", new Date().toString() );
if ( documentModel.getMeta().getSubject() != null )
{
transformer.setParameter( "subject", documentModel.getMeta().getSubject() );
}
if ( documentModel.getMeta().getKeywords() != null )
{
transformer.setParameter( "keywords", documentModel.getMeta().getKeywords() );
}
transformer.setParameter( "producer", "Generated with Doxia by " + System.getProperty( "user.name" ) );
if ( ITextUtil.isPageSizeSupported( documentModel.getMeta().getTitle() ) )
{
transformer.setParameter( "pagesize", documentModel.getMeta().getPageSize() );
}
else
{
transformer.setParameter( "pagesize", "A4" );
}
transformer.setParameter( "frontPageHeader", "" );
if ( documentModel.getMeta().getTitle() != null )
{
transformer.setParameter( "frontPageTitle", documentModel.getMeta().getTitle() );
}
transformer.setParameter( "frontPageFooter", "Generated date " + new Date().toString() );
}
/**
* Transform a document to an iTextFile
*
* @param documentModel
* @param document
* @param iTextFile
* @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any.
*/
private void transform( DocumentModel documentModel, Document document, File iTextFile )
throws DocumentRendererException
{
Transformer transformer = initTransformer();
addTransformerParameters( transformer, documentModel );
try
{
transformer.transform( new DOMSource( document ), new StreamResult( iTextFile ) );
}
catch ( TransformerException e )
{
throw new DocumentRendererException( "Error transformer Document from "
+ document + ": " + e.getMessage() );
}
}
}