/*
* Copyright (C) 2011 Laurent Caillette
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.novelang.configuration;
import java.io.File;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.Map;
import com.google.common.base.Function;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableSet;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.DefaultConfiguration;
import org.apache.avalon.framework.configuration.DefaultConfigurationSerializer;
import org.apache.avalon.framework.configuration.MutableConfiguration;
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.FopFactory;
import org.apache.fop.fonts.EmbedFontInfo;
import org.apache.fop.fonts.FontCache;
import org.apache.fop.fonts.FontEventListener;
import org.apache.fop.fonts.FontResolver;
import org.apache.fop.render.DefaultFontResolver;
import org.apache.fop.render.pdf.PDFRendererConfigurator;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.fest.reflect.core.Reflection;
import org.fest.reflect.reference.TypeRef;
import org.novelang.logger.Logger;
import org.novelang.logger.LoggerFactory;
/**
* Utility class for generating FOP configuration with hyphenation files and custom fonts.
*
* @author Laurent Caillette
*/
public class FopTools {
private static final Logger LOGGER = LoggerFactory.getLogger( FopTools.class );
private static final String CONFIGURATION_NOT_SERIALIZED =
"Could not serialize configuration to string" ;
public static final Function< ? super EmbedFontInfo,? extends String >
EXTRACT_EMBEDFONTINFO_FUNCTION = new Function< EmbedFontInfo, String >() {
@Override
public String apply( final EmbedFontInfo embedFontInfo ) {
return embedFontInfo.getEmbedFile() ;
}
}
;
private FopTools() {}
/**
* Creates a {@code Configuration} object with {@code <renderer>} as element root,
* using font directories as set by system property.
*/
public static Configuration createPdfRendererConfiguration(
final Iterable < File > fontDirectories
) {
final MutableConfiguration renderer = new DefaultConfiguration( "renderer" ) ;
renderer.setAttribute( "mime", "application/pdf" ) ;
final MutableConfiguration fonts = new DefaultConfiguration( "fonts" ) ;
for( final File fontDirectory : fontDirectories ) {
final MutableConfiguration directory = new DefaultConfiguration( "directory" ) ;
directory.setAttribute( "recurse", "true" ) ;
directory.setValue( fontDirectory.getAbsolutePath() ) ;
fonts.addChild( directory ) ;
}
renderer.addChild( fonts ) ;
return renderer ;
}
/**
* Creates a {@code Configuration} object with {@code <renderers>} element as root.
*/
public static Configuration createRenderersConfiguration(
final Iterable< File > fontDirectories
) {
final MutableConfiguration renderers = new DefaultConfiguration( "renderers" ) ;
renderers.addChild( createPdfRendererConfiguration( fontDirectories ) ) ;
return renderers ;
}
public static Configuration createHyphenationConfiguration( final File hyphenationDirectory ) {
final URL hyphenationBaseUrl ;
try {
hyphenationBaseUrl = hyphenationDirectory.toURI().toURL() ;
} catch( MalformedURLException e ) {
throw new RuntimeException( e ) ;
}
final MutableConfiguration hyphenationBase = new DefaultConfiguration( "hyphenation-base" ) ;
hyphenationBase.setValue( hyphenationBaseUrl.toExternalForm() ) ;
return hyphenationBase ;
}
public static String configurationAsString( final Configuration configuration ) {
try {
final StringWriter stringWriter = new StringWriter() ;
final OutputFormat format = OutputFormat.createPrettyPrint() ;
final XMLWriter xmlWriter = new XMLWriter( stringWriter, format ) ;
new DefaultConfigurationSerializer().serialize( xmlWriter, configuration ) ;
xmlWriter.close() ;
return stringWriter.toString() ;
} catch( Exception e ) {
LOGGER.error( e, CONFIGURATION_NOT_SERIALIZED ) ;
throw Throwables.propagate( e ) ;
}
}
private static ImmutableSet< String > extractFailedFontMap( final FOUserAgent foUserAgent ) {
final FontCache fontCache = foUserAgent.getFactory().getFontManager().getFontCache() ;
final Map< String, Long > fieldValue = Reflection.field( "failedFontMap" )
.ofType( new TypeRef< Map< String, Long > >() {} ).in( fontCache ).get() ;
return fieldValue == null ? ImmutableSet.< String >of()
: ImmutableSet.copyOf( fieldValue.keySet() ) ;
}
public static FopFontStatus createGlobalFontStatus(
final FopFactory fopFactory,
final Iterable< File > fontDirectories
) throws FOPException {
final Configuration pdfRendererConfiguration =
createPdfRendererConfiguration( fontDirectories ) ;
final FOUserAgent foUserAgent = fopFactory.newFOUserAgent() ;
final FontResolver fontResolver = new DefaultFontResolver( foUserAgent ) ;
final FontCache fontCache = new FontCache() ;
@SuppressWarnings( { "unchecked" } )
final List< EmbedFontInfo > fontList = ( List< EmbedFontInfo > )
new PDFRendererConfigurator( foUserAgent ) {
@Override
protected List buildFontList(
final Configuration configuration,
final FontResolver resolver,
final FontEventListener listener
) throws FOPException {
return super.buildFontList( configuration, resolver, listener ) ;
}
}.buildFontList(
pdfRendererConfiguration,
fontResolver,
null
)
;
final ImmutableSet< String > failedFontMap = extractFailedFontMap( foUserAgent ) ;
return new FopFontStatus( fontList, failedFontMap ) ;
}
public static FopFactory createFopFactory(
final Iterable< File > fontsDirectories,
final File hyphenationDirectory
)
throws FOPException
{
final FopFactory fopFactory = FopFactory.newInstance() ;
Configuration renderers = null ;
Configuration hyphenationBase = null ;
boolean configure = false ;
if( fontsDirectories != null && fontsDirectories.iterator().hasNext() ) {
renderers = createRenderersConfiguration( fontsDirectories ) ;
configure = true ;
}
if( null != hyphenationDirectory ) {
hyphenationBase = createHyphenationConfiguration( hyphenationDirectory ) ;
configure = true ;
}
if( configure ) {
final MutableConfiguration configuration = new DefaultConfiguration( "fop" ) ;
configuration.setAttribute( "version", "1.0" ) ;
if( null != renderers ) {
configuration.addChild( renderers ) ;
}
if( null != hyphenationBase ) {
configuration.addChild( hyphenationBase ) ;
}
LOGGER.debug( "Created configuration: \n", configurationAsString( configuration ) ) ;
fopFactory.setUserConfig( configuration ) ;
}
// Hack until we get Barcode4J-2.1 available as Maven artifact.
// http://www.mail-archive.com/fop-dev@xmlgraphics.apache.org/msg10500.html
fopFactory.getRendererFactory().setRendererPreferred( true ) ;
return fopFactory ;
}
}