# graphviz-java **Repository Path**: fate83/graphviz-java ## Basic Information - **Project Name**: graphviz-java - **Description**: No description available - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-02-01 - **Last Updated**: 2024-02-01 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # graphviz-java [![Build Status](https://travis-ci.org/nidi3/graphviz-java.svg)](https://travis-ci.org/nidi3/graphviz-java) [![codecov](https://codecov.io/gh/nidi3/graphviz-java/branch/master/graph/badge.svg)](https://codecov.io/gh/nidi3/graphviz-java) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/guru.nidi/graphviz-java/badge.svg)](https://maven-badges.herokuapp.com/maven-central/guru.nidi/graphviz-java) Use graphviz with pure java. Create graphviz models using java code and convert them into nice graphics. #### [How it works ](#user-content-how-it-works) #### [Prerequisites ](#user-content-prerequisites) #### [API ](#user-content-api) #### [Examples ](#user-content-examples) #### [Images ](#user-content-images) #### [Configuration ](#user-content-configuration) #### [Javadoc ](#javadoc) ## How it works To execute the graphviz layout engine, one of these options is used: - If the machine has graphviz installed and a `dot` command is available, spawn a new process running `dot`. - Use this [javascript version](https://github.com/mdaines/viz.js) of graphviz and execute it on the V8 javascript engine. This is done with the bundled [J2V8](https://github.com/eclipsesource/J2V8) library. - Alternatively, the javascript can be executed on Java's own Nashorn or GraalVM engine. The method(s) to be used can be configured with the `Graphviz.useEngine()` method. ## Prerequisites ### Maven This project is available via Maven: ```xml guru.nidi graphviz-java 0.8.8 ``` ### Logging Graphviz-java uses the [SLF4J](https://www.slf4j.org/) facade to log. Users must therefore provide a logging implementation like [LOGBack](https://logback.qos.ch/) ```xml ch.qos.logback logback-classic 1.2.3 ``` or [Log4j](https://logging.apache.org/log4j/2.x/) ```xml org.apache.logging.log4j log4j-core 2.11.1 org.apache.logging.log4j log4j-slf4j-impl 2.11.1 ``` ## API The API is separated into a mutable and immutable part. The basic usage is as follows (assuming `import static guru.nidi.graphviz.model.Factory.*`). ### Immutable [//]: # (basic) ```java Graph g = graph("example1").directed() .graphAttr().with(RankDir.LEFT_TO_RIGHT) .with( node("a").with(Color.RED).link(node("b")), node("b").link(to(node("c")).with(Style.DASHED)) ); Graphviz.fromGraph(g).height(100).render(Format.PNG).toFile(new File("example/ex1.png")); ``` [//]: # (end) Global attributes are set using the `graphAttr`, `linkAttr` and `nodeAttr` methods. Nodes are styled using the `with` method. To style edges, use the static method `to` which returns a `Link` that also has a `with` method. The `with` method accepts predefined attributes like `Style`, `Arrow` or `Shape` as well as everything defined in the [Graphviz reference](https://graphviz.gitlab.io/_pages/doc/info/attrs.html) e.g. `with("weight", 5)` **Attention:** `Node a = node("a"); a.with(Color.RED);` Is not working as it might be expected. All "mutating" methods like `with` on nodes, links and graphs create new objects and leave the original object unchanged. So in the example above, variable `a` contains a node that is NOT red. If you want a red node, do `a = a.with(Color.RED)` or use the mutable API. ### Mutable [//]: # (mutable) ```java 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")); ``` [//]: # (end) ### Imperative There is a third possibility to use the API, based on the mutable version. Its form is closer to the way dot files are written. In the lambda of the `MutableGraph.use` method, all referenced nodes, links and graphs are automatically added to the parent graph, without explicitly calling the `add` method. [//]: # (imperative) ```java 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")); ``` [//]: # (end) This corresponds to the following `dot` file: ```dot digraph example1 { b node[color=red] a -> b } ``` ### Kotlin DSL **Kotlin DSL is still experimental.** Things can change and any feedback is very welcome. ```xml guru.nidi graphviz-kotlin 0.8.8 ``` The kotlin DSL based on the imperative API. It defines that following elements: - `edge`, `node`, `graph` variables to define global attributes. - `-`, `/`, `[]` operators on MutableNode which link, define ports, set attributes. - `-`, `/`, `[]` operators on String so that strings can be used directly to define nodes. - `-`, `[]` operators on Link which allow to chain links and set attributes. To enable the functions, use `import guru.nidi.graphviz.*` [//]: # (kotlin) ```kotlin graph(directed = true) { edge["color" eq "red", Arrow.TEE] node[Color.GREEN] graph[RankDir.LEFT_TO_RIGHT] "a" - "b" - "c" ("c"[Color.RED] - "d"[Color.BLUE])[Arrow.VEE] "d" / NORTH - "e" / SOUTH }.toGraphviz().render(PNG).toFile(File("example/ex1.png")) ``` [//]: # (end) ## Examples ### Complex example [//]: # (complex) ```java Node main = node("main").with(Label.html("main
start"), Color.rgb("1020d0").font()), init = node(Label.markdown("**_init_**")), execute = node("execute"), compare = node("compare").with(Shape.RECTANGLE, Style.FILLED, Color.hsv(.7, .3, 1.0)), mkString = node("mkString").with(Label.lines(LEFT, "make", "a", "multi-line")), printf = node("printf"); Graph g = graph("example2").directed().with( main.link( to(node("parse").link(execute)).with(LinkAttr.weight(8)), to(init).with(Style.DOTTED), node("cleanup"), to(printf).with(Style.BOLD, Label.of("100 times"), Color.RED)), execute.link( graph().with(mkString, printf), to(compare).with(Color.RED)), init.link(mkString)); Graphviz.fromGraph(g).width(900).render(Format.PNG).toFile(new File("example/ex2.png")); ``` [//]: # (end) ### Example with records ```java import static guru.nidi.graphviz.attribute.Records.*; import static guru.nidi.graphviz.model.Compass.*; ``` [//]: # (records) ```java Node node0 = node("node0").with(Records.of(rec("f0", ""), rec("f1", ""), rec("f2", ""), rec("f3", ""), rec("f4", ""))), node1 = node("node1").with(Records.of(turn(rec("n4"), rec("v", "719"), rec("")))), node2 = node("node2").with(Records.of(turn(rec("a1"), rec("805"), rec("p", "")))), node3 = node("node3").with(Records.of(turn(rec("i9"), rec("718"), rec("")))), node4 = node("node4").with(Records.of(turn(rec("e5"), rec("989"), rec("p", "")))), node5 = node("node5").with(Records.of(turn(rec("t2"), rec("v", "959"), rec("")))), node6 = node("node6").with(Records.of(turn(rec("o1"), rec("794"), rec("")))), node7 = node("node7").with(Records.of(turn(rec("s7"), rec("659"), rec("")))); Graph g = graph("example3").directed() .graphAttr().with(RankDir.LEFT_TO_RIGHT) .with( node0.link( between(port("f0"), node1.port("v", SOUTH)), between(port("f1"), node2.port(WEST)), between(port("f2"), node3.port(WEST)), between(port("f3"), node4.port(WEST)), between(port("f4"), node5.port("v", NORTH))), node2.link(between(port("p"), node6.port(NORTH_WEST))), node4.link(between(port("p"), node7.port(SOUTH_WEST)))); Graphviz.fromGraph(g).width(900).render(Format.PNG).toFile(new File("example/ex3.png")); ``` [//]: # (end) ### Read and manipulate graphs Dot files can be parsed and thus manipulated. Given this file `color.dot`: ``` graph { { rank=same; white} { rank=same; cyan; yellow; pink} { rank=same; red; green; blue} { rank=same; black} white -- cyan -- blue white -- yellow -- green white -- pink -- red cyan -- green -- black yellow -- red -- black pink -- blue -- black } ``` Then running this program: [//]: # (manipulate) ```java MutableGraph g = Parser.read(getClass().getResourceAsStream("/color.dot")); Graphviz.fromGraph(g).width(700).render(Format.PNG).toFile(new File("example/ex4-1.png")); g.graphAttrs() .add(Color.WHITE.gradient(Color.rgb("888888")).background().angle(90)) .nodeAttrs().add(Color.WHITE.fill()) .nodes().forEach(node -> node.add( Color.named(node.name().toString()), Style.lineWidth(4).and(Style.FILLED))); Graphviz.fromGraph(g).width(700).render(Format.PNG).toFile(new File("example/ex4-2.png")); ``` [//]: # (end) results in this graphics: ## Images Images can be included in graphviz in two ways. One possibility is using the \ tag inside a HTML label: [//]: # (img) ```java Graphviz.useEngine(new GraphvizCmdLineEngine()); Graphviz g = Graphviz.fromGraph(graph() .with(node(Label.html("
")))); g.basedir(new File("example")).render(Format.PNG).toFile(new File("example/ex7.png")); ``` [//]: # (end) Because viz.js [does not support \ tags](https://github.com/mdaines/viz.js/issues/125), **this works only when using the command line engine**. The other possibility is the `image` attribute of a node: [//]: # (image) ```java 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")); ``` [//]: # (end) This works with all engines. In both cases, the `basedir()` method can be used to define where relative paths are looked up. ## Configuration The size of the resulting image, the rendering engine and the output format can be configured: [//]: # (config) ```java 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")); String json = viz.engine(Engine.NEATO).render(Format.JSON).toString(); BufferedImage image = viz.render(Format.PNG).toImage(); ``` [//]: # (end) To rasterize with batik, provide this library on the classpath: ```xml org.apache.xmlgraphics batik-rasterizer 1.10 ``` ### Font size The layout of a graph is done with Javascript / natively and the rendering with Java. The two environments are not guaranteed to calculate the width of text the same way. This can lead to node labels that are too broad for their box or not correctly centered. To help with this, there is the `fontAdjust` option to adjust the font size _after_ the layout has been done. To help finding the correct `fontAdjust`, [FontTools.java](graphviz-java/src/main/java/guru/nidi/graphviz/use/FontTools.java) can be used. [//]: # (fontAdjust) ```java Node width = node("Very long node labels can go over the border"); Node center = node(Label.html("HTML labels on the other side, can get uncentered")); Graphviz g = Graphviz.fromGraph(graph() .nodeAttr().with(Font.name("casual"), Shape.RECTANGLE) .with(width.link(center))); g.render(Format.PNG).toFile(new File("example/ex6d.png")); g.fontAdjust(.87).render(Format.PNG).toFile(new File("example/ex6a.png")); ``` [//]: # (end) ## Javadoc To use graphviz inside javadoc comments, add this to `pom.xml`: ```xml maven-javadoc-plugin 3.1.0 guru.nidi.graphviz.GraphvizTaglet guru.nidi graphviz-taglet 0.8.8 ``` To use this with JDK 9 or later, replace `graphviz-taglet` with `graphviz-taglet9`. The usage inside javadoc is then as follows: ```java /** * Support graphviz inside javadoc. *

* {@graphviz * graph test { a -- b } * } *

* So easy. */ public class GraphvizTaglet implements Taglet {} ```