Skip to content

Instantly share code, notes, and snippets.

@msteiger
Created January 11, 2013 09:05
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save msteiger/4509119 to your computer and use it in GitHub Desktop.
Save msteiger/4509119 to your computer and use it in GitHub Desktop.
An ExtensionHandler for Apache Batik's svggen part that uses SVGGraphics2D to convert Swing drawing routines to SVG elements. Currently supports LinearGradientPaint and RadialGradientPaint. Feel free to comment and extend ..
import static org.apache.batik.util.SVGConstants.*;
import java.awt.Color;
import java.awt.LinearGradientPaint;
import java.awt.MultipleGradientPaint;
import java.awt.Paint;
import java.awt.RadialGradientPaint;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import org.apache.batik.svggen.DefaultExtensionHandler;
import org.apache.batik.svggen.SVGColor;
import org.apache.batik.svggen.SVGGeneratorContext;
import org.apache.batik.svggen.SVGPaintDescriptor;
import org.w3c.dom.Element;
/**
* Extension of Batik's {@link DefaultExtensionHandler} which
* handles different kinds of Paint objects
*
* I wonder why this is not part of the svggen library.
* @author Martin Steiger
*/
public class GradientExtensionHandler extends DefaultExtensionHandler
{
@Override
public SVGPaintDescriptor handlePaint(Paint paint, SVGGeneratorContext genCtx)
{
// Handle LinearGradientPaint
if (paint instanceof LinearGradientPaint)
return getLgpDescriptor((LinearGradientPaint) paint, genCtx);
// Handle RadialGradientPaint
if (paint instanceof RadialGradientPaint)
return getRgpDescriptor((RadialGradientPaint) paint, genCtx);
return super.handlePaint(paint, genCtx);
}
private SVGPaintDescriptor getRgpDescriptor(RadialGradientPaint gradient, SVGGeneratorContext genCtx)
{
Element gradElem = genCtx.getDOMFactory().createElementNS(SVG_NAMESPACE_URI, SVG_RADIAL_GRADIENT_TAG);
// Create and set unique XML id
String id = genCtx.getIDGenerator().generateID("gradient");
gradElem.setAttribute(SVG_ID_ATTRIBUTE, id);
// Set x,y pairs
Point2D centerPt = gradient.getCenterPoint();
gradElem.setAttribute("cx", String.valueOf(centerPt.getX()));
gradElem.setAttribute("cy", String.valueOf(centerPt.getY()));
Point2D focusPt = gradient.getFocusPoint();
gradElem.setAttribute("fx", String.valueOf(focusPt.getX()));
gradElem.setAttribute("fy", String.valueOf(focusPt.getY()));
gradElem.setAttribute("r", String.valueOf(gradient.getRadius()));
addMgpAttributes(gradElem, genCtx, gradient);
return new SVGPaintDescriptor("url(#" + id + ")", SVG_OPAQUE_VALUE, gradElem);
}
private SVGPaintDescriptor getLgpDescriptor(LinearGradientPaint gradient, SVGGeneratorContext genCtx)
{
Element gradElem = genCtx.getDOMFactory().createElementNS(SVG_NAMESPACE_URI, SVG_LINEAR_GRADIENT_TAG);
// Create and set unique XML id
String id = genCtx.getIDGenerator().generateID("gradient");
gradElem.setAttribute(SVG_ID_ATTRIBUTE, id);
// Set x,y pairs
Point2D startPt = gradient.getStartPoint();
gradElem.setAttribute("x1", String.valueOf(startPt.getX()));
gradElem.setAttribute("y1", String.valueOf(startPt.getY()));
Point2D endPt = gradient.getEndPoint();
gradElem.setAttribute("x2", String.valueOf(endPt.getX()));
gradElem.setAttribute("y2", String.valueOf(endPt.getY()));
addMgpAttributes(gradElem, genCtx, gradient);
return new SVGPaintDescriptor("url(#" + id + ")", SVG_OPAQUE_VALUE, gradElem);
}
private void addMgpAttributes(Element gradElem, SVGGeneratorContext genCtx, MultipleGradientPaint gradient)
{
gradElem.setAttribute(SVG_GRADIENT_UNITS_ATTRIBUTE, SVG_USER_SPACE_ON_USE_VALUE);
// Set cycle method
switch (gradient.getCycleMethod())
{
case REFLECT:
gradElem.setAttribute(SVG_SPREAD_METHOD_ATTRIBUTE, SVG_REFLECT_VALUE);
break;
case REPEAT:
gradElem.setAttribute(SVG_SPREAD_METHOD_ATTRIBUTE, SVG_REPEAT_VALUE);
break;
case NO_CYCLE:
gradElem.setAttribute(SVG_SPREAD_METHOD_ATTRIBUTE, SVG_PAD_VALUE); // this is the default
break;
}
// Set color space
switch (gradient.getColorSpace())
{
case LINEAR_RGB:
gradElem.setAttribute(SVG_COLOR_INTERPOLATION_ATTRIBUTE, SVG_LINEAR_RGB_VALUE);
break;
case SRGB:
gradElem.setAttribute(SVG_COLOR_INTERPOLATION_ATTRIBUTE, SVG_SRGB_VALUE);
break;
}
// Set transform matrix if not identity
AffineTransform tf = gradient.getTransform();
if (!tf.isIdentity())
{
String matrix = "matrix(" +
tf.getScaleX() + " " + tf.getShearX() + " " + tf.getTranslateX() + " " +
tf.getScaleY() + " " + tf.getShearY() + " " + tf.getTranslateY() + ")";
gradElem.setAttribute(SVG_TRANSFORM_ATTRIBUTE, matrix);
}
// Convert gradient stops
Color[] colors = gradient.getColors();
float[] fracs = gradient.getFractions();
for (int i = 0; i < colors.length; i++)
{
Element stop = genCtx.getDOMFactory().createElementNS(SVG_NAMESPACE_URI, SVG_STOP_TAG);
SVGPaintDescriptor pd = SVGColor.toSVG(colors[i], genCtx);
stop.setAttribute(SVG_OFFSET_ATTRIBUTE, (int) (fracs[i] * 100.0f) + "%");
stop.setAttribute(SVG_STOP_COLOR_ATTRIBUTE, pd.getPaintValue());
if (colors[i].getAlpha() != 255)
{
stop.setAttribute(SVG_STOP_OPACITY_ATTRIBUTE, pd.getOpacityValue());
}
gradElem.appendChild(stop);
}
}
}
@fnatter
Copy link

fnatter commented Oct 13, 2017

hello Martin,

I find your batik ExtensionHandler for java.awt.(Linear|Radial)GradientPaint very useful, as I am part of the
freeplane project where we render SVGs using svgSalamander which generates these "Multiple" GradientPaints.

Setting your "GradientExtensionHandler" as an ExtensionHandler does improve things
(better color rendering), but gradients are still not rendered completely.

Here is a simple example (including use of "GradientExtensionHandler"):
https://github.com/fnatter/svgtest

In order to test this, please do:

  • clone
  • gradle build cleanEclipse eclipse
  • import project into eclipse
  • Start org.freeplane.svgtest.TestBatik
  • choose a file to save the PDF to
  • notice that gradient rendering is incomplete in the PDF

Could you help me fix this?

Many Thanks and Best Regards,
Felix

@fnatter
Copy link

fnatter commented Oct 14, 2017

hello Martin,

I fixed my problem with this change in the transform (transform->gradientTransform, change order of parameters):
fnatter/svgtest@bb5b87a

Could you review this?

Cheers and Best Regards,
Felix

@fnatter
Copy link

fnatter commented Oct 14, 2017

hi Martin,

with the above change it seems to work fine :-)
Can I use this code in the Freeplane project under the GPL (or less restrictive, like Apache/Mozilla/...) conditions?

Cheers and Best Regards,
Felix

@msteiger
Copy link
Author

Hello, sorry, I didn't see your comments until now. You can use the code under the MIT license if it is still relevant for you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment