Skip to content
carlosame edited this page Jul 24, 2024 · 6 revisions

The echosvg-svggen module can generate SVG images using an SVG-specific Graphics2D implementation. Assuming that you are familiar with Graphics2D, the following example shows the basic usage of svggen.

Setup

If your build process is based on Gradle, declare a dependency on echosvg-svggen:

dependencies {
    implementation "io.sf.carte:echosvg-svggen:${echosvgVersion}"
}

Or if you manage your classpath manually, clone this repository and create a fat jar with

./gradlew echosvg-svggen-jar-with-deps

The archive shall be available at echosvg-svggen/build/libs/echosvg-svggen-<version>-with-deps.jar.


Basic example

First, let's have an example class that writes to a Graphics2D. Please change the used fonts if you prefer others, or your system does not have them installed.

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.font.TextAttribute;
import java.util.HashMap;
import java.util.Map;

/**
 * Draw text with decoration attributes.
 */
public class FontDecorationPainter {

    public void paint(Graphics2D g) {
        // Set anti-aliasing
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        // Set a background color
        Color backgroundColor = new Color(0x08081a);
        g.setBackground(backgroundColor);

        // Set default font
        g.setFont(new Font("Arial", Font.BOLD, 12));

        // Create a font with the desired attributes, including STRIKETHROUGH
        Map<TextAttribute, Object> attributes = new HashMap<>();
        attributes.put(TextAttribute.FAMILY, "Helvetica");
        attributes.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_EXTRABOLD);
        attributes.put(TextAttribute.SIZE, 20);
        attributes.put(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON);
        Font fontST = new Font(attributes);

        // A similar font but with UNDERLINE instead of STRIKETHROUGH
        Map<TextAttribute, Object> attributes2 = new HashMap<>(attributes);
        attributes2.remove(TextAttribute.STRIKETHROUGH);
        attributes2.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);
        Font fontUL = new Font(attributes2);

        // Set the STRIKETHROUGH font and a color
        g.setFont(fontST);
        g.setPaint(new Color(0x666699));
        // Draw a string
        g.drawString("Strike Through", 10, 40);

        // Now draw with a different color and the UNDERLINE font
        g.setPaint(Color.black);
        g.setFont(fontUL);
        g.translate(0, 30);
        // Draw a new string
        g.drawString("Underline", 10, 70);
    }

}

Then we need a few svggen classes to produce the SVG.

import java.awt.Dimension;
import java.awt.Font;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.DocumentType;
import org.w3c.dom.Element;

import io.sf.carte.echosvg.svggen.SVGGeneratorContext;
import io.sf.carte.echosvg.svggen.SVGGeneratorContext.GraphicContextDefaults;
import io.sf.carte.echosvg.svggen.SVGGraphics2D;

public class SVGGraphics2DExample {

    /**
     * Generate SVG from the drawings of the <code>FontDecorationPainter</code> class.
     * 
     * @param filename the filename to write the SVG to.
     * @throws IOException in case of I/O error.
     */
    public static void generateSVG(String filename) throws IOException {
        FontDecorationPainter painter = new FontDecorationPainter();

        SVGGraphics2D g2d = createSVGGraphics2D();

        // Set some appropriate dimension
        g2d.setSVGCanvasSize(new Dimension(300, 400));

        try (FileWriter fw = new FileWriter(filename, StandardCharsets.UTF_8)) {
            painter.paint(g2d);
            g2d.stream(fw);
            fw.flush();
        }
    }

    /**
     * Creates a <code>SVGGraphics2D</code> with certain defaults.
     * 
     * @return the <code>SVGGraphics2D</code>.
     */
    static SVGGraphics2D createSVGGraphics2D() {
        // We need a Document that holds an SVG root element.
        // First obtain a DocumentBuilder as a way to get it.
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);

        DocumentBuilder builder;
        try {
            builder = factory.newDocumentBuilder();
        } catch (ParserConfigurationException e) {
            throw new IllegalStateException(e);
        }

        // Now the document which is what is needed
        Document doc = builder.newDocument();
        
        // Create a SVG DTD
        DocumentType dtd = builder.getDOMImplementation().createDocumentType("svg",
                "-//W3C//DTD SVG 1.1//EN",
                "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd");
        // And the root element in the SVG namespace
        Element svgRoot = doc.createElementNS("http://www.w3.org/2000/svg", "svg");

        // Append those to the document
        doc.appendChild(dtd);
        doc.appendChild(svgRoot);

        /*
         * Now the document is ready: let's create some context objects and
         * then the SVGGraphics2D.
         */

        // For simplicity, create a generator context with some defaults
        SVGGeneratorContext ctx = SVGGeneratorContext.createDefault(doc);
        // Set a comment to put in the SVG documents, overriding the default
        ctx.setComment("Generated by My Application");

        // Set a compression higher than the default
        ctx.setCompressionLevel(8);

        // Create the context defaults, with a default font just in case
        GraphicContextDefaults defaults = new GraphicContextDefaults();
        defaults.setFont(new Font("Arial", Font.PLAIN, 12));
        // Set the defaults
        ctx.setGraphicContextDefaults(defaults);

        return new SVGGraphics2D(ctx, false);
    }

}

and executing the generateSVG("filename.svg") method, the SVG would be written to the filename.svg file. Note that so far we haven't used EchoSVG's DOM, as the document was produced with the JDK's built-in DOM.


Color profiles

Since version 1.1 you can generate graphics in color spaces other than sRGB, for example:

import java.awt.color.ICC_ColorSpace;
import java.awt.color.ICC_Profile;
import java.io.InputStream;

[...]

Color pColor;

// Load a color profile from classpath
try (InputStream is = getClass().getResourceAsStream(
        "/my/color/profiles/Display P3.icc")) {
    ICC_Profile profile = ICC_Profile.getInstance(is);
    ICC_ColorSpace cs = new ICC_ColorSpace(profile);
    float[] comps = { .36f, .35f, .33f };
    pColor = new Color(cs, comps, 1f);
} catch (IOException e) {
    pColor = new Color(0x5c5954);
}

// Now draw with the new color
g.setPaint(pColor);

Configure the compression level for embedded PNG images

The default compression level for embedded rasterized PNG images (via drawImage()) is 4, but if the generated SVG is intended to be downloaded multiple times from the Internet, you may want to increase it.

// Set a compression higher than the default
ctx.setCompressionLevel(8);

See SVGGeneratorContext.setCompressionLevel().


Apache Batik documentation

The Batik website has detailed documentation about using svggen which you may want to read.