blob: 1d4413a1b732b07aa4bfc1403ac8016cc66d252d [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2004, 2008 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
****************************************************************************/
package org.eclipse.gmf.runtime.draw2d.ui.render.awt.internal.svg.metafile;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import org.apache.batik.dom.svg.SVGDOMImplementation;
import org.apache.batik.svggen.SVGGraphics2D;
import org.eclipse.gmf.runtime.common.core.util.Trace;
import org.eclipse.gmf.runtime.draw2d.ui.render.awt.internal.Draw2dRenderPlugin;
import org.eclipse.gmf.runtime.draw2d.ui.render.internal.Draw2dRenderDebugOptions;
import org.eclipse.gmf.runtime.draw2d.ui.render.awt.internal.svg.metafile.DeviceContext;
import org.eclipse.gmf.runtime.draw2d.ui.render.awt.internal.svg.metafile.IRenderToPath;
import org.eclipse.gmf.runtime.draw2d.ui.render.awt.internal.svg.metafile.IRenderableObject;
import org.eclipse.gmf.runtime.draw2d.ui.render.awt.internal.svg.metafile.ITraceMe;
import org.eclipse.gmf.runtime.draw2d.ui.render.awt.internal.svg.metafile.Record;
import org.eclipse.gmf.runtime.draw2d.ui.render.awt.internal.svg.metafile.TranscoderException;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.svg.SVGDocument;
/**
* Abstract base class for both the WMFTranscoder and the EMFTranscoder. Implements the common
* functionality. May be extended to provide a new type of transcoder.
*
* @author dhabib
*/
public abstract class AbstractTranscoder
{
private static boolean m_gDebugMode = false;
private Dimension m_size = new Dimension( 100, 100 );
/**
* Puts the transcoder in 'debug' mode so messages are written out using System.out
* instead of using the Trace/Debug stuff. This is so we can operate without
* eclipse.
*/
static void setDebugMode()
{
m_gDebugMode = true;
}
/**
* Logs the specified message. If we are in 'debug' mode it will log the message to
* the output console. If we are running as a plugin, it will log the
* message using the core
* @param message
*/
static void logMessage( String message )
{
if( m_gDebugMode )
{
System.out.println( message );
}
else
{
// We may not handle all types of selections.
Trace.trace( Draw2dRenderPlugin.getInstance(),
Draw2dRenderDebugOptions.DEBUG,
message );
}
}
/**
* Sets the size, in pixels, of the metafile output. In the case of the WMF/APM and the EMF
* transcoder, this information is contained in the header record, so after initializing that
* record they will call this method.
* @param size
*/
public void setSize( Dimension size )
{
m_size = size;
}
/**
* Retrieves the current size, in pixels, of the metafile output.
* @return
*/
public Dimension getSize()
{
return new Dimension( m_size );
}
/**
* Translate an input stream containing a metafile to an output stream containing SVG.
* @param input - Contains Metafile Data
* @param output - After successful transcoding, contains generated SVG output.
* @throws TranscoderException
*/
public void transcode( InputStream input, OutputStream output) throws TranscoderException
{
transcode(input, output, SVGDOMImplementation.SVG_NAMESPACE_URI);
}
/**
* Translate an input stream containing a metafile to an output stream containing SVG.
* @param input - Contains Metafile Data
* @param output - After successful transcoding, contains generated SVG output.
* @param url - document namespace url
* @throws TranscoderException
*/
public void transcode( InputStream input, OutputStream output, String url ) throws TranscoderException
{
if( input == null )
{
throw new IllegalArgumentException( "Parameter 'input' may not be null" ); //$NON-NLS-1$
}
else if( output == null )
{
throw new IllegalArgumentException( "Parameter 'output' may not be null" ); //$NON-NLS-1$
}
try
{
BufferedInputStream stream = new BufferedInputStream( input );
// Create the DOM
DOMImplementation impl = SVGDOMImplementation.getDOMImplementation();
String svgNS = url;
SVGDocument doc = (SVGDocument)impl.createDocument( svgNS, "svg", null ); //$NON-NLS-1$
// Create the SVG converter.
SVGGraphics2D svg = new SVGGraphics2D( doc );
// There seems to be a bug in the batik libraries. It appears that
// there are some variables that are not set properly at initialization
// time. This sets the variable so it works.
svg.getGeneratorContext().setPrecision( 3 );
// Iterate over the input stream and paint into the svg graphics object.
iterateRecords( stream, svg );
// Get the size from the header.
svg.setSVGCanvasSize( m_size );
Writer writer = new OutputStreamWriter( output, "UTF-8" );//$NON-NLS-1$
svg.stream( writer );
}
catch( IOException e )
{
throw new TranscoderException( e );
}
}
/**
* Parses the metafile records in the stream , rendering them into
* the provided Graphics object.
* @param stream
* @param g
* @throws TranscoderException
*/
private void iterateRecords( BufferedInputStream stream,
Graphics2D g ) throws TranscoderException, IOException
{
DeviceContext context = new DeviceContext();
int index = 0;
Record rec;
while( (rec = getNextRecord( stream, index )) != null )
{
index++;
try
{
String output = null;
IRenderableObject svgConverter = getConverter( rec );
if( svgConverter != null )
{
if( context.getGdiPath().isOpen() &&
svgConverter instanceof IRenderToPath )
{
// Rendering to a path and this converter handles paths.
IRenderToPath pathRender = (IRenderToPath) svgConverter;
pathRender.render( context );
if (m_gDebugMode) {
output = buildOutput(rec, "IRenderToPath", index); //$NON-NLS-1$
}
}
else
{
svgConverter.render( g, context );
if (m_gDebugMode) {
output = buildOutput(rec, "IRenderableObject", index); //$NON-NLS-1$
}
}
if (m_gDebugMode && svgConverter instanceof ITraceMe) {
output += ' ' + svgConverter.toString();
}
}
else
{
// Unconditionally trace this
output = buildOutput(rec, "Unhandled", index); //$NON-NLS-1$
}
logMessage( output );
}
catch (org.apache.batik.transcoder.TranscoderException e)
{
throw new TranscoderException(e);
}
catch (TranscoderException e)
{
throw e;
}
catch( Exception e )
{
String output = buildOutput(
rec,
"Exception while processing",//$NON-NLS-1$
index) + ":\n" + e.toString(); //$NON-NLS-1$
logMessage( output );
e.printStackTrace();
}
}
}
private String _thisClassName = null;
private String buildOutput(Record rec, String msg, int ix) {
if (_thisClassName == null) {
_thisClassName = getClass().getName();
_thisClassName = _thisClassName.substring(_thisClassName.lastIndexOf('.') + 1);
}
StringBuffer sb = new StringBuffer();
sb.append(_thisClassName);
sb.append(", record " + ix + ' ');//$NON-NLS-1$
sb.append(msg + " metafile record type "); //$NON-NLS-1$
sb.append(rec.getType() + " = "+ getUserFriendlyName( rec.getType() ));//$NON-NLS-1$
return sb.toString();
}
/**
* Reads the next metafile record from the provided stream. Returns 'null'
* if at the end of the set of records.
* @param stream Contains the data to read the record from.
* @param curRecord Contains the current record number.
* @return Next metafile record in the stream, or null if the last record has been read.
* @throws IOException
*/
protected abstract Record getNextRecord( BufferedInputStream stream,
int curRecord ) throws IOException;
/**
* This method takes a metafile record and instantiates a class
* implementing IRenderableObject to handle that type of record.
* This method will initialize the newly created converter
* with the data contained in the record.
* @param stream
* @return A handler for the specified record, or null if no handler exists.
* @throws TranscoderException
*/
protected abstract IRenderableObject getConverter( Record rec ) throws TranscoderException;
/**
* Returns the user friendly name for the specified record type.
* @param recordType
* @return The user friendly name for the specified record type.
*/
protected abstract String getUserFriendlyName( int recordType );
}