graphdog¶
Package graphdog turns Graphviz DOT source into SVG (or PNG, or laid-out DOT) by calling the Graphviz C library in process. It depends only on Odin’s core packages and Graphviz.
Example:
import "core:fmt"
import "lib/graphdog"
source := "digraph { a -> b }"
ctx := graphdog.init()
defer graphdog.close(&ctx)
svg, err := graphdog.render(&ctx, source, {engine = .Dot}) // svg: the caller's
if err.kind != .None do fmt.eprintln(graphdog.format_error(err, source, "g.dot"))
defer delete(svg)
Threads. Graphviz keeps process-wide state (the parser, the error handler, plugin tables), so it is not thread-safe even across separate contexts. graphdog therefore holds one process-wide mutex around every call into Graphviz: render from as many threads as you like, but renders run one at a time.
Types
- graphdog.Attribute :: struct¶
-
One -G/-N/-E default. Like dot, it overrides the graph’s own defaults (node [shape=box]) but not attributes written on a single node or edge.
- kind: Attribute_Kind¶
- name: string¶
- value: string¶
- graphdog.Attribute_Kind :: enum u8¶
-
What a default attribute applies to: -G, -N and -E on the dot command line.
- Graph¶
- Node¶
- Edge¶
- graphdog.Context :: struct¶
-
A Graphviz context: loaded plugins and nothing else. Make one with init and keep it; loading plugins is the expensive part.
- gvc: ^GVC¶
- graphdog.Engine :: enum u8¶
-
Layout engines. Each one is a Graphviz layout plugin: .Dot comes from dot_layout, the rest from neato_layout. The zero value is .Dot, like the dot command.
- Dot¶
-
hierarchical, for directed graphs
- Neato¶
-
spring model (stress majorization)
- Fdp¶
-
spring model (force directed)
- Sfdp¶
-
multiscale force directed, for large graphs
- Circo¶
-
circular
- Twopi¶
-
radial
- Osage¶
-
clustered array packing
- Patchwork¶
-
squarified treemap
- graphdog.Error :: struct¶
-
Error says what went wrong and how to fix it. line and column are 1-based positions in the source (column counts characters), or 0 when Graphviz does not say. message is one sentence, in Graphviz’s own words when it gave any; hint is advice for the author. render allocates near, message and hint with its allocator; delete_error frees them.
- kind: Error_Kind¶
- line: int¶
- column: int¶
- near: string¶
-
the text Graphviz stopped at, for .Syntax and .Html_Label
- message: string¶
- hint: string¶
- graphdog.Error_Kind :: enum u8¶
-
- None¶
- Syntax¶
-
the DOT source does not parse
- Html_Label¶
-
an HTML-like label (label=<…>) is malformed
- Empty_Input¶
-
the source holds no graph
- Missing_Engine¶
-
this Graphviz has no plugin for the layout engine
- Missing_Format¶
-
this Graphviz has no plugin for the output format
- Layout¶
-
the layout engine reported an error
- Render¶
-
the renderer reported an error
- Out_Of_Memory¶
- File_Refused¶
-
the caller refused a file the graph names (Options.files)
- Not_Initialized¶
-
render was called with a context that init did not make
- graphdog.File_Access :: struct¶
-
File_Access decides each file name a graph gives through attribute (「src」 for an <IMG>): it returns the path Graphviz may open, 「」 to leave the attribute out, or ok false to refuse the graph, which render reports as a .File_Refused error.
- procedure: proc(user: rawptr, attribute, name: string) -> (path: string, ok: bool)¶
- user: rawptr¶
- graphdog.Format :: enum u8¶
-
Output formats. The zero value is .Svg, which the core plugin always provides.
- Svg¶
-
standalone SVG 1.1 document, UTF-8
- Png¶
-
needs the cairo (pango) or gd plugin; the static build has neither
- Dot¶
-
the input with layout positions attached, like
dot -Tdot
- graphdog.Options :: struct¶
-
- warnings: ^[dynamic]string¶
-
When set, each Graphviz warning (「using box for unknown shape boxx」) is appended, allocated with the allocator given to render.
- files: File_Access¶
-
When set, decides every file the graph names before Graphviz may open it (see files.odin); otherwise Graphviz opens what the graph names, as dot does.
- graphdog.Reserve :: struct¶
-
Reserve lets the caller count the output before render allocates it: false refuses the bytes, and render reports .Out_Of_Memory.
- procedure: proc(user: rawptr, bytes: int) -> bool¶
- user: rawptr¶
Procedures
- graphdog.close :: proc(ctx: ^Context)¶
-
close frees the context. Output and errors returned by render stay valid.
- graphdog.delete_error :: proc(err: Error, allocator := context.allocator)¶
-
delete_error frees the strings of an error that render returned, with the allocator given to render.
- graphdog.engine_name :: proc(engine: Engine) -> string¶
-
engine_name is the Graphviz name of an engine (「dot」, 「neato」, …).
- graphdog.error_title :: proc(kind: Error_Kind) -> string¶
-
error_title is a short upper-case title for the kind of error, such as 「DOT SYNTAX ERROR」.
- graphdog.format_error :: proc(err: Error, source, name: string, allocator := context.allocator) -> string¶
-
format_error writes err the way Elm writes compiler errors: a title rule naming the file, the source line with a caret under the problem, what went wrong, and a hint. name labels the source (「diagram.dot」, 「<stdin>」); source is the text given to render. The text is allocated with allocator, and the caller owns it.
-- DOT SYNTAX ERROR ---------------------------------------------- diagram.dot 3 | b -> -> c ^^ Graphviz stopped at `->`. Hint: An edge needs a node on each side, like `a -> b`. ...
- graphdog.format_message :: proc(title, name, message, hint: string, allocator := context.allocator) -> string¶
-
format_message writes an error that has no source to point at, such as a bad command line option, in the same shape as format_error. The text is allocated with allocator, and the caller owns it.
- graphdog.init :: proc() -> (ctx: Context, ok: bool) #optional_ok¶
-
init creates a Graphviz context and loads its plugins. ok is false only when Graphviz cannot allocate one.
- graphdog.parse_engine :: proc(name: string) -> (Engine, bool)¶
-
parse_engine maps a Graphviz engine name to an Engine.
- graphdog.parse_format :: proc(name: string) -> (Format, bool)¶
-
parse_format maps a -T format name to a Format.
- graphdog.render :: proc(ctx: ^Context, source: string, options := Options{}, allocator := context.allocator) -> (output: []u8, err: Error)¶
-
render lays out one graph from DOT source and renders it. On success output holds the rendered bytes, allocated with allocator (string(output) for SVG); Graphviz’s own buffers are already freed. On failure output is nil and err says what went wrong and how to fix it; its strings are allocated with allocator too (see delete_error). Only the first graph in source is rendered, as with
dotwithout -O.
- graphdog.version :: proc(ctx: ^Context) -> string¶
-
version is the linked Graphviz version, such as 「16.1.0」. It is owned by Graphviz and valid until close.
Constants
- graphdog.FILE_ATTRIBUTES :: [?]string{"image", "shapefile", "imagepath", "fontpath"}¶
-
FILE_ATTRIBUTES are the attributes through which Graphviz reads files.
- graphdog.STATIC :: #config(GRAPHDOG_STATIC, false)¶
-
The raw Graphviz C API that graphdog uses, and nothing more. Every signature was checked against the headers of Graphviz 16.1 (Homebrew) and 2.42 (Debian); where they differ, the note says how this binding stays correct for both.
Linking. By default graphdog links the shared libraries that a package manager installs:
macOS brew install graphviz Odin already passes -L/opt/homebrew/lib and -L/usr/local/lib, and Homebrew's dylibs carry absolute install names, so no rpath is needed. Linux apt install libgraphviz-dev the libraries are on the default search path.
GRAPHDOG_LINK_FLAGS adds linker flags for any other prefix, for example
-define:GRAPHDOG_LINK_FLAGS="-L/opt/gv/lib -Wl,-rpath,/opt/gv/lib"
-define:GRAPHDOG_STATIC=true links the static libraries that native/graphviz/build.sh builds instead, and preloads the dot, neato and core plugins (see static.odin), so the program needs no Graphviz installation at run time.
Windows has no static build: native/graphviz/windows.sh installs the official Graphviz release for Windows (the same version, checked against its SHA-256) into native/graphviz/out, and graphdog links its import libraries there. The program then needs the DLLs and the plugin list (config8) from native/graphviz/out/bin beside it, which is how the Windows release archive ships them.
Foreign imports
- @(extra_linker_flags = #config(GRAPHDOG_LINK_FLAGS, "")) foreign import graphdog.graphviz {"../../native/graphviz/out/lib/libgvplugin_dot_layout.a", "../../native/graphviz/out/lib/libgvplugin_neato_layout.a", "../../native/graphviz/out/lib/libgvplugin_core.a", "../../native/graphviz/out/lib/libgvc.a", "../../native/graphviz/out/lib/libpathplan.a", "../../native/graphviz/out/lib/libxdot.a", "../../native/graphviz/out/lib/libcgraph.a", "../../native/graphviz/out/lib/libcdt.a", "../../native/graphviz/out/lib/libutil.a", "../../native/graphviz/out/lib/libvpsc.a", "system:expat", "system:z", "system:c++"}¶
-
対応環境: Darwin.
The attribute takes only literals, so the vendored directory is spelled out.
- @(extra_linker_flags = #config(GRAPHDOG_LINK_FLAGS, "")) foreign import graphdog.graphviz {"../../native/graphviz/out/lib/libgvplugin_dot_layout.a", "../../native/graphviz/out/lib/libgvplugin_neato_layout.a", "../../native/graphviz/out/lib/libgvplugin_core.a", "../../native/graphviz/out/lib/libgvc.a", "../../native/graphviz/out/lib/libpathplan.a", "../../native/graphviz/out/lib/libxdot.a", "../../native/graphviz/out/lib/libcgraph.a", "../../native/graphviz/out/lib/libcdt.a", "../../native/graphviz/out/lib/libutil.a", "../../native/graphviz/out/lib/libvpsc.a", "../../native/graphviz/out/lib/libexpat.a", "../../native/graphviz/out/lib/libz.a", "system:stdc++", "system:m"}
-
対応環境: Windows, Linux, FreeBSD, OpenBSD, NetBSD, WASI, JS, Orca, Freestanding.
- @(extra_linker_flags = #config(GRAPHDOG_LINK_FLAGS, "")) foreign import graphdog.graphviz {"../../native/graphviz/out/lib/gvc.lib", "../../native/graphviz/out/lib/cgraph.lib"}
-
対応環境: Windows.
- @(extra_linker_flags = #config(GRAPHDOG_LINK_FLAGS, "")) foreign import graphdog.graphviz {"system:gvc", "system:cgraph"}
-
対応環境: Darwin, Linux, FreeBSD, OpenBSD, NetBSD, WASI, JS, Orca, Freestanding.