graphviz-java

repository·master·Indexed 21 days ago

https://github.com/nidi3/graphviz-java

A pure Java library for creating and manipulating Graphviz models, allowing developers to generate graphics using Java, Kotlin, or imperative styles. It supports multiple execution engines including Local Dot Process, J2V8, and JDK JavaScript engines (Nashorn or GraalVM). The library provides Immutable, Mutable, and Imperative APIs, an experimental Kotlin DSL, and a Parser for .dot files. It includes features for rendering to PNG, SVG, DOT, and JSON, as well as a Roughifyer processor for hand-drawn styles via the graphviz-rough module.

Tokens
4.8K
Snippets
14
Records
15
Agent score
26%

What's inside graphviz-java

  1. Use Pre-processors and Post-processors

    master

    Processors allow you to intercept and modify the graph data or the resulting output:

    • Pre-processors: Modify the DOT source string before it is sent to the Graphviz engine. This is useful for string replacements or automated graph transformations.
    • Post-processors: Modify the engine's output (e.g., an SVG string) after rendering. This allows for programmatic manipulation of the resulting graphic elements (like changing CSS classes or attributes via an SVG finder).
    Graph graph = graph().with(node("bad word").link("good word"));
    Graphviz g = Graphviz.fromGraph(graph)
            .preProcessor((source, options, processOptions) -> source.replace("bad word", "unicorn"))
            .postProcessor((result, options, processOptions) ->
                    result.mapString(svg ->
                            SvgElementFinder.use(svg, finder -> {
                                finder.findNode("unicorn").setAttribute("class", "pink");
                            })));
    g.render(Format.PNG).toFile(new File("example/ex9.png"));
  2. How graphviz-java works

    master

    graphviz-java executes the graphviz layout engine using one of three methods:

    1. Local Dot Process: If the dot command is available on the machine's PATH, it spawns a new process to run it.
    2. J2V8 (JavaScript): Executes a JavaScript version of graphviz (viz.js) using the bundled J2V8 library.
    3. JDK JavaScript Engines: Executes JavaScript using Nashorn or GraalVM (GraalVM is preferred if available).

    You can select the engine using the Graphviz.useEngine() method.

  3. Apply a sketchy/hand-drawn style with Roughifyer

    master

    To give your graphs a hand-drawn or 'sketchy' appearance, use the graphviz-rough module and apply a Roughifyer processor.

    Setup: Add the following dependency:

    <dependency>
        <groupId>guru.nidi</groupId>
        <artifactId>graphviz-rough</artifactId>
        <version>0.18.1</version>
    </dependency>

    Usage: Apply the Roughifyer to your Graphviz instance. You can customize parameters like bowing, curveStepCount, roughness, fillStyle (using FillStyle.hachure()), and font.

    final Graph g = graph("ex1").directed().with(
            graph().cluster()
                    .nodeAttr().with(Style.FILLED, Color.WHITE)
                    .graphAttr().with(Style.FILLED, Color.LIGHTGREY, Label.of("process #1"))
                    .with(node("a0").link(node("a1").link(node("a2")))),
            graph("x").cluster()
                    .nodeAttr().with(Style.FILLED)
                    .graphAttr().with(Color.BLUE, Label.of("process #2"))
                    .with(node("b0").link(node("b1").link(node("b2")))),
            node("start").with(Shape.M_DIAMOND).link("a0", "b0"),
            node("a0").with(Style.FILLED, Color.RED.gradient(Color.BLUE)).link("b1"),
            node("b1").link("a2"),
            node("a2").link("end"),
            node("b2").link("end"),
            node("end").with(Shape.M_SQUARE)
    );
    
    Graphviz.fromGraph(g)
            .processor(new Roughifyer()
                    .bowing(2)
                    .curveStepCount(6)
                    .roughness(1)
                    .fillStyle(FillStyle.hachure().width(2).gap(5).angle(0))
                    .font("*serif", "Comic Sans MS"))
            .render(Format.PNG)
            .toFile(new File("example/ex1-rough.png"));
  4. Configure rendering output and rasterization

    master

    The Graphviz instance allows you to configure the output format, image width, and rasterization settings:

    • Format: Use .render(Format.XXX) to specify output like PNG, SVG, DOT, or JSON.
    • Width: Use .width(int) to set the output width.
    • Rasterization: Use .rasterize(Rasterizer.XXX) to convert vector outputs to raster images. Supported rasterizers include BATIK, SALAMANDER, and built-in PDF engines.
    • Engine: Use .engine(Engine.XXX) to specify a specific Graphviz engine (e.g., NEATO).

    To use Rasterizer.BATIK, you must include the batik-rasterizer dependency in your project.

    Graphviz.useEngine(new GraphvizCmdLineEngine());
    Graph g = graph("example5").directed().with(node("abc").link(node("xyz")));
    Graphviz viz = Graphviz.fromGraph(g);
    
    viz.width(200).render(Format.SVG).toFile(new File("example/ex5.svg"));
    viz.width(200).rasterize(Rasterizer.BATIK).toFile(new File("example/ex5b.png"));
    viz.width(200).rasterize(Rasterizer.SALAMANDER).toFile(new File("example/ex5s.png"));
    viz.width(200).rasterize(Rasterizer.builtIn("pdf")).toFile(new File("example/ex5p"));
    
    String dot = viz.render(Format.DOT).toString();
    String json = viz.engine(Engine.NEATO).render(Format.JSON).toString();
    BufferedImage image = viz.render(Format.PNG).toImage();
  5. Configure alternative graphviz-java dependencies

    master

    Depending on your deployment needs, you can use alternative artifacts:

    • graphviz-java-all-j2v8: Includes dependencies for all J2V8 platforms (Linux, Mac OS X, Windows), allowing the same application to run cross-platform.
    • graphviz-java-min-deps: Contains only essential dependencies. All others are marked as optional and must be added manually.
    • GraalVM JavaScript: If using JDK 15+ (where Nashorn was removed), add the GraalJS dependency:
    <dependency>
        <groupId>org.graalvm.js</groupId>
        <artifactId>js</artifactId>
        <version>20.0.0</version>
    </dependency>
  6. Include images in graphs

    master

    You can include images in your graphs using two different methods depending on your engine requirements:

    1. HTML Labels: Use the <img> tag inside an HTML label. Note: This method only works when using the GraphvizCmdLineEngine because viz.js does not support <img> tags.
    2. Node Image Attribute: Use the Image.of(path) attribute on a node. This method is compatible with all engines.

    In both cases, use the basedir(File) method on the Graphviz instance to define the base directory for resolving relative image paths.

    // Method 1: HTML Label (Requires GraphvizCmdLineEngine)
    Graphviz.useEngine(new GraphvizCmdLineEngine());
    Graphviz g = Graphviz.fromGraph(graph()
            .with(node(Label.html("<table border='0'><tr><td><img src='graphviz.png' /></td></tr></table>"))));
    g.basedir(new File("example")).render(Format.PNG).toFile(new File("example/ex7.png"));
    
    // Method 2: Image Attribute (Works with all engines)
    Graphviz g = Graphviz.fromGraph(graph()
            .with(node(" ").with(Size.std().margin(.8, .7), Image.of("graphviz.png"))));
    g.basedir(new File("example")).render(Format.PNG).toFile(new File("example/ex8.png"));
  7. Configure Logging with SLF4J

    master

    graphviz-java uses the SLF4J facade. You must provide a logging implementation such as Logback or Log4j to see logs.

    <!-- Logback example -->
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.2.3</version>
    </dependency>
    
    <!-- Log4j example -->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.13.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-slf4j-impl</artifactId>
        <version>2.13.0</version>
    </dependency>
  8. Integrate Graphviz into Javadoc

    master

    You can render graphs directly inside Javadoc comments using the GraphvizTaglet.

    1. Add the maven-javadoc-plugin to your pom.xml.
    2. Configure the plugin with the guru.nidi.graphviz.taglet.GraphvizTaglet taglet and its corresponding artifact.
    3. For JDK 9+, use graphviz-taglet9 instead of graphviz-taglet.

    Usage in Javadoc:

    /**
     * <p>
     * {@graphviz
     * graph test { a -- b }
     * }
     * </p>
     */
    public class MyClass {}
    <build>
      <plugins>
        <plugin>
          <artifactId>maven-javadoc-plugin</artifactId>
          <version>3.1.0</version>
          <configuration>
            <taglet>guru.nidi.graphviz.taglet.GraphvizTaglet</taglet>
            <tagletArtifact>
              <groupId>guru.nidi</groupId>
              <artifactId>graphviz-taglet</artifactId>
              <version>0.18.1</version>
            </tagletArtifact>
          </configuration>
        </plugin>
      </plugins>
    </build>
  9. Install graphviz-java via Maven

    master

    Add the following dependency to your pom.xml. Note that graphviz-java includes the necessary J2V8 dependencies for Linux, Mac OS X, and Windows automatically.

    Note for Gradle users: Gradle does not support this automatic dependency resolution for J2V8. You must manually add the dependency for your specific platform (e.g., com.eclipsesource.j2v8:j2v8_linux_x86_64:4.6.0).

    <dependency>
        <groupId>guru.nidi</groupId>
        <artifactId>graphviz-java</artifactId>
        <version>0.18.1</version>
    </dependency>
  10. Use the Imperative API for dot-like syntax

    master

    The Imperative API is built on top of the Mutable API and uses a lambda-based approach (MutableGraph.use) that mimics the structure of a .dot file. Inside the lambda, nodes and links are automatically added to the parent graph when referenced.

    MutableGraph g = mutGraph("example1").setDirected(true).use((gr, ctx) -> {
        mutNode("b");
        nodeAttrs().add(Color.RED);
        mutNode("a").addLink(mutNode("b"));
    });
    Graphviz.fromGraph(g).width(200).render(Format.PNG).toFile(new File("example/ex1i.png"));
  11. Use the Mutable API to create graphs

    master

    The Mutable API allows you to modify objects in place. It uses different factory methods and setter-style syntax compared to the Immutable API.

    Key differences:

    • Use mutGraph(...) instead of graph(...).
    • Use mutNode(...) instead of node(...).
    • Use setters like .setDirected(true) instead of .directed().
    • Use .add(...) instead of .with(...).
    MutableGraph g = mutGraph("example1").setDirected(true).add(
            mutNode("a").add(Color.RED).addLink(mutNode("b")));
    Graphviz.fromGraph(g).width(200).render(Format.PNG).toFile(new File("example/ex1m.png"));
  12. Use the Immutable API to create graphs

    master

    The Immutable API uses a functional approach where every 'mutating' method returns a new object.

    Warning: Calling node.with(Color.RED) does not change the existing node; you must capture the returned object: node = node.with(Color.RED).

    Key patterns:

    • Use graph(...) to create a graph.
    • Use graphAttr(), nodeAttr(), and linkAttr() for global styling.
    • Use node(...) for nodes and to(...) for edges/links.
    • Use .with(...) to apply attributes (predefined like Style, Color, or custom strings).
    import static guru.nidi.graphviz.model.Factory.*;
    
    Graph g = graph("example1").directed()
            .graphAttr().with(Rank.dir(LEFT_TO_RIGHT))
            .nodeAttr().with(Font.name("arial"))
            .linkAttr().with("class", "link-class")
            .with(
                    node("a").with(Color.RED).link(node("b")),
                    node("b").link(
                            to(node("c")).with(attr("weight", 5), Style.DASHED)
                    )
            );
    Graphviz.fromGraph(g).height(100).render(Format.PNG).toFile(new File("example/ex1.png"));