Guidedog 手册 0.2.0
语言
本页内容
Guidedog / 文档 0.2.0

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
engine: Engine
format: Format
attributes: []Attribute
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.

reserve: Reserve

When set, counts the output before render allocates it.

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 dot without -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.