Giter Club home page Giter Club logo

graphviz.netwrapper's Introduction

Graphviz.NetWrapper

codecov

Supported platforms

At the moment, Rubjerg.Graphviz ships with a bunch of precompiled Graphviz dlls built for 64 bit Windows. This library is compatible with .NET Standard 2.0. The unit tests run against .NET Framework 4.8 and .NET 6.0. In the future support may be extended to other platforms.

Contributing

This project aims to provide a thin .NET shell around the Graphviz C libraries, together with some convenience functionality that helps abstracting away some of the peculiarities of the Graphviz library and make it easier to integrate in an application. Pull request that fall within the scope of this project are welcome.

Installation

You can either add this library as a nuget package to project, or include the source and add a project reference.

Adding as a Nuget package

Add the Rubjerg.Graphviz nuget package to your project.

Adding the Rubjerg.Graphviz code to your project or solution

  1. Make this code available to your own code, e.g. by adding this repository as a git submodule to your own repository.
  2. Add the projects Rubjerg.Graphviz and GraphvizWrapper to your solution.
  3. To use Rubjerg.Graphviz within a project of yours, simply add a project reference to it.

When building your project, you should now see all the Graphviz binaries show up in your output folder. If you don't, try setting the CopyLocalLockFileAssemblies property in your referencing project file to true. If that still fails, try reordering the projects in your solution, such that GraphvizWrapper and Rubjerg.Graphviz are at the top. There is an outstanding issue for this.

Documentation

For a reference of attributes to instruct Graphviz have a look at Node, Edge and Graph Attributes. For more information on the inner workings of the graphviz libraries, consult the various documents presented at the Graphviz documentation page.

Tutorial

using NUnit.Framework;
using System.Linq;

namespace Rubjerg.Graphviz.Test;

#nullable enable

[TestFixture()]
public class Tutorial
{
    public const string PointPattern = @"{X=[\d.]+, Y=[\d.]+}";
    public const string RectPattern = @"{X=[\d.]+, Y=[\d.]+, Width=[\d.]+, Height=[\d.]+}";
    public const string SplinePattern =
        @"{X=[\d.]+, Y=[\d.]+}, {X=[\d.]+, Y=[\d.]+}, {X=[\d.]+, Y=[\d.]+}, {X=[\d.]+, Y=[\d.]+}";

    [Test, Order(1)]
    public void GraphConstruction()
    {
        // You can programmatically construct graphs as follows
        RootGraph root = RootGraph.CreateNew(GraphType.Directed, "Some Unique Identifier");
        // The graph name is optional, and can be omitted. The name is not interpreted by Graphviz,
        // except it is recorded and preserved when the graph is written as a file.

        // The node names are unique identifiers within a graph in Graphviz
        Node nodeA = root.GetOrAddNode("A");
        Node nodeB = root.GetOrAddNode("B");
        Node nodeC = root.GetOrAddNode("C");

        // The edge name is only unique between two nodes
        Edge edgeAB = root.GetOrAddEdge(nodeA, nodeB, "Some edge name");
        Edge edgeBC = root.GetOrAddEdge(nodeB, nodeC, "Some edge name");
        Edge anotherEdgeBC = root.GetOrAddEdge(nodeB, nodeC, "Another edge name");

        // An edge name is optional and omitting it will result in a new nameless edge.
        // There can be multiple nameless edges between any two nodes.
        Edge edgeAB1 = root.GetOrAddEdge(nodeA, nodeB);
        Edge edgeAB2 = root.GetOrAddEdge(nodeA, nodeB);
        Assert.AreNotEqual(edgeAB1, edgeAB2);

        // We can attach attributes to nodes, edges and graphs to store information and instruct
        // Graphviz by specifying layout parameters. At the moment we only support string
        // attributes. Cgraph assumes that all objects of a given kind (graphs/subgraphs, nodes,
        // or edges) have the same attributes. An attribute has to be introduced with a default value
        // first for a certain kind, before we can use it.
        Node.IntroduceAttribute(root, "my attribute", "defaultvalue");
        nodeA.SetAttribute("my attribute", "othervalue");

        // Attributes are introduced per kind (Node, Edge, Graph) per root graph.
        // So to be able to use "my attribute" on edges, we first have to introduce it as well.
        Edge.IntroduceAttribute(root, "my attribute", "defaultvalue");
        edgeAB.SetAttribute("my attribute", "othervalue");

        // To introduce and set an attribute at the same time, there are convenience wrappers
        edgeBC.SafeSetAttribute("arrowsize", "2.0", "1.0");
        // If we set an unintroduced attribute, the attribute will be introduced with an empty default value.
        edgeBC.SetAttribute("new attr", "value");

        // Some attributes - like "label" - accept HTML strings as value
        // To tell Graphviz that a string should be interpreted as HTML use the designated methods
        Node.IntroduceAttribute(root, "label", "defaultlabel");
        nodeB.SetAttributeHtml("label", "<b>Some HTML string</b>");

        // We can simply export this graph to a text file in dot format
        root.ToDotFile(TestContext.CurrentContext.TestDirectory + "/out.dot");

        // A word of advice, Graphviz doesn't play very well with empty strings.
        // Try to avoid them when possible. (https://gitlab.com/graphviz/graphviz/-/issues/1887)
    }

    [Test, Order(2)]
    public void Layouting()
    {
        // If we have a given dot file (in this case the one we generated above), we can also read it back in
        RootGraph root = RootGraph.FromDotFile(TestContext.CurrentContext.TestDirectory + "/out.dot");

        // We can ask Graphviz to compute a layout and render it to svg
        root.ToSvgFile(TestContext.CurrentContext.TestDirectory + "/dot_out.svg");

        // We can use layout engines other than dot by explicitly passing the engine we want
        root.ToSvgFile(TestContext.CurrentContext.TestDirectory + "/neato_out.svg", LayoutEngines.Neato);

        // Or we can ask Graphviz to compute the layout and programatically read out the layout attributes
        // This will create a copy of our original graph with layout information attached to it in the form
        // of attributes. Graphviz outputs coordinates in a bottom-left originated coordinate system.
        // But since many applications require rendering in a top-left originated coordinate system,
        // we provide a way to translate the coordinates.
        RootGraph layout = root.CreateLayout(coordinateSystem: CoordinateSystem.TopLeft);

        // There are convenience methods available that parse these attributes for us and give
        // back the layout information in an accessible form.
        Node nodeA = layout.GetNode("A")!;
        PointD position = nodeA.GetPosition();
        Utils.AssertPattern(PointPattern, position.ToString());

        RectangleD nodeboundingbox = nodeA.GetBoundingBox();
        Utils.AssertPattern(RectPattern, nodeboundingbox.ToString());

        // Or splines between nodes
        Node nodeB = layout.GetNode("B")!;
        Edge edge = layout.GetEdge(nodeA, nodeB, "Some edge name")!;
        PointD[] spline = edge.GetFirstSpline();
        string splineString = string.Join(", ", spline.Select(p => p.ToString()));
        Utils.AssertPattern(SplinePattern, splineString);

        // If we require detailed drawing information for any object, we can retrieve the so called "xdot"
        // operations. See https://graphviz.org/docs/outputs/canon/#xdot for a specification.
        var activeFillColor = System.Drawing.Color.Black;
        foreach (var op in nodeA.GetDrawing())
        {
            if (op is XDotOp.FillColor { Value: Color.Uniform { HtmlColor: var htmlColor } })
            {
                activeFillColor = System.Drawing.ColorTranslator.FromHtml(htmlColor);
            }
            else if (op is XDotOp.FilledEllipse { Value: var boundingBox })
            {
                Utils.AssertPattern(RectPattern, boundingBox.ToString());
            }
            // Handle any xdot operation you require
        }

        foreach (var op in nodeA.GetLabelDrawing())
        {
            if (op is XDotOp.Text { Value: var text })
            {
                Utils.AssertPattern(PointPattern, text.Anchor.ToString());
                var boundingBox = text.TextBoundingBoxEstimate();
                Utils.AssertPattern(RectPattern, boundingBox.ToString());
                Assert.AreEqual(text.Text, "A");
                Assert.AreEqual(text.Font.Name, "Times-Roman");
            }
            // Handle any xdot operation you require
        }

        // These are just simple examples to showcase the structure of xdot operations.
        // In reality the information can be much richer and more complex.
    }

    [Test, Order(3)]
    public void Clusters()
    {
        RootGraph root = RootGraph.CreateNew(GraphType.Directed, "Graph with clusters");
        Node nodeA = root.GetOrAddNode("A");
        Node nodeB = root.GetOrAddNode("B");
        Node nodeC = root.GetOrAddNode("C");
        Node nodeD = root.GetOrAddNode("D");

        // When a subgraph name is prefixed with cluster,
        // the dot layout engine will render it as a box around the containing nodes.
        SubGraph cluster1 = root.GetOrAddSubgraph("cluster_1");
        cluster1.AddExisting(nodeB);
        cluster1.AddExisting(nodeC);
        SubGraph cluster2 = root.GetOrAddSubgraph("cluster_2");
        cluster2.AddExisting(nodeD);

        // COMPOUND EDGES
        // Graphviz does not really support edges from and to clusters. However, by adding an
        // invisible dummynode and setting the ltail or lhead attributes of an edge this
        // behavior can be faked. Graphviz will then draw an edge to the dummy node but clip it
        // at the border of the cluster. We provide convenience methods for this.
        // To enable this feature, Graphviz requires us to set the "compound" attribute to "true".
        Graph.IntroduceAttribute(root, "compound", "true"); // Allow lhead/ltail
        // The boolean indicates whether the dummy node should take up any space. When you pass
        // false and you have a lot of edges, the edges may start to overlap a lot.
        _ = root.GetOrAddEdge(nodeA, cluster1, false, "edge to a cluster");
        _ = root.GetOrAddEdge(cluster1, nodeD, false, "edge from a cluster");
        _ = root.GetOrAddEdge(cluster1, cluster1, false, "edge between clusters");

        var layout = root.CreateLayout();

        SubGraph cluster = layout.GetSubgraph("cluster_1")!;
        RectangleD clusterbox = cluster.GetBoundingBox();
        RectangleD rootgraphbox = layout.GetBoundingBox();
        Utils.AssertPattern(RectPattern, clusterbox.ToString());
        Utils.AssertPattern(RectPattern, rootgraphbox.ToString());
    }

    [Test, Order(4)]
    public void Records()
    {
        RootGraph root = RootGraph.CreateNew(GraphType.Directed, "Graph with records");
        Node nodeA = root.GetOrAddNode("A");
        nodeA.SetAttribute("shape", "record");
        // New line characters are not supported by record labels, and will be ignored by Graphviz
        nodeA.SetAttribute("label", "1|2|3|{4|5}|6|{7|8|9}");

        var layout = root.CreateLayout();

        // The order of the list matches the order in which the labels occur in the label string above.
        var rects = layout.GetNode("A")!.GetRecordRectangles().ToList();
        var rectLabels = layout.GetNode("A")!.GetRecordRectangleLabels().Select(l => l.Text).ToList();
        Assert.AreEqual(9, rects.Count);
        Assert.AreEqual(new[] { "1", "2", "3", "4", "5", "6", "7", "8", "9" }, rectLabels);
    }

    [Test, Order(5)]
    public void StringEscaping()
    {
        RootGraph root = RootGraph.CreateNew(GraphType.Directed, "Graph with escaped strings");
        Node.IntroduceAttribute(root, "label", "\\N");
        Node nodeA = root.GetOrAddNode("A");

        // Several characters and character sequences can have special meanings in labels, like \N.
        // When you want to have a literal string in a label, we provide a convenience function for you to do just that.
        nodeA.SetAttribute("label", CGraphThing.EscapeLabel("Some string literal \\N \\n |}>"));

        // When defining portnames, some characters, like ':' and '|', are not allowed and they can't be escaped either.
        // This can be troubling if you have an externally defined ID for such a port.
        // We provide a function that maps strings to valid portnames.
        var somePortId = "port id with :| special characters";
        var validPortName = Edge.ConvertUidToPortName(somePortId);
        Node nodeB = root.GetOrAddNode("B");
        nodeB.SetAttribute("shape", "record");
        nodeB.SetAttribute("label", $"<{validPortName}>1|2");

        // The conversion function makes sure different strings don't accidentally map onto the same portname
        Assert.AreNotEqual(Edge.ConvertUidToPortName(":"), Edge.ConvertUidToPortName("|"));
    }
}

graphviz.netwrapper's People

Contributors

chivandikwa avatar chtenb avatar manuelesvi avatar modery avatar peitschie avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

graphviz.netwrapper's Issues

Fix flaky test TestRecordShapeAlignment

The test TestRecordShapeAlignment fails on the github test server, but passes locally. Figure out why.
For some reason calling dot.exe directly on dot files does not give a wrong alignment on the github server.

Can't create SVG/PNG with space in folder path

I just tried to upgrade my project to .net8 and Graphviz.NetWrapper 2.0.0. After making a few smaller changes to make my code compatible with the new version, I still ran into some issues when trying to save my Graph as a SVG or PNG. I then tried a small proof of concept app, and noticed that I only get an error if my folder path contains a space.

Here's my sample code:

using Rubjerg.Graphviz;

class Program
{
    static void Main(string[] args)
    {
        RootGraph root = RootGraph.CreateNew(GraphType.Directed, "Some Unique Identifier");
        Node nodeA = root.GetOrAddNode("A");
        Node nodeB = root.GetOrAddNode("B");
        Node nodeC = root.GetOrAddNode("C");
        Edge edgeAB = root.GetOrAddEdge(nodeA, nodeB, "Some edge name");
        Edge edgeBC = root.GetOrAddEdge(nodeB, nodeC, "Some edge name");
        Edge anotherEdgeBC = root.GetOrAddEdge(nodeB, nodeC, "Another edge name");
        Edge edgeAB1 = root.GetOrAddEdge(nodeA, nodeB);
        Edge edgeAB2 = root.GetOrAddEdge(nodeA, nodeB);
        Node.IntroduceAttribute(root, "my attribute", "defaultvalue");
        nodeA.SetAttribute("my attribute", "othervalue");
        Edge.IntroduceAttribute(root, "my attribute", "defaultvalue");
        edgeAB.SetAttribute("my attribute", "othervalue");
        edgeBC.SafeSetAttribute("arrowsize", "2.0", "1.0");
        edgeBC.SetAttribute("new attr", "value");
        Node.IntroduceAttribute(root, "label", "defaultlabel");
        nodeB.SetAttributeHtml("label", "<b>Some HTML string</b>");

        // We can simply export this graph to a text file in dot format
        root.ToDotFile(@"c:/tmp/Test Folder/out.dot");

        RootGraph root2 = RootGraph.FromDotFile("c:/tmp/Test Folder/out.dot");

        //the following 2 lines fail
        root2.ToSvgFile("c:/tmp/Test Folder/dot_out.svg");
        root2.ToPngFile("c:/tmp/Test Folder/dot_out.png");

    }
}

This is the error I get:

An unhandled exception of type 'System.ApplicationException' occurred in Rubjerg.Graphviz.dll: 'Process exited with code 2. Error details: Error: dot: can't open Folder/dot_out.svg: No such file or directory'

If I change it to "c:/tmp/TestFolder/dot_out.svg" it works.

Support different coordinate systems

Graphviz has a bottomleft origin, but many applications require a topleft origin. It would be nice if you could configure this and the coordinates were transformed correspondingly.

Update main.yml

Actions issues two warnings about things being outdated:

Unable to use html in node label

On setting node label to html via SetAttribute call with something like "<sample>" the result is rendered as raw text.

How can I achieve this?

Switch off using System.Drawing

  • It's based on float, which means we loose accuracy
  • It's tied to topleft coordinate system, which means we're currently misusing it.
  • To decide whether we should keep using the Color type

.NET 6 tests give warnings on the stderr

.NET Framework 4.8 tests run without any warnings showing.
Example:

Warning: Could not load "D:\a\Graphviz.NetWrapper\Graphviz.NetWrapper\Rubjerg.Graphviz.Test\bin\x64\Release\net6.0\gvplugin_pango.dll" - It was found, so perhaps one of its dependents was not.  Try ldd.
Warning: dummynode-2 -> dummynode-3: tail is inside head cluster cluster_1
Warning: dummynode-2 -> dummynode-3: head is inside tail cluster cluster_1
Warning: no hard-coded metrics for 'Tahoma'.  Falling back to 'Times' metrics
Warning: node N, port >|< unrecognized

Is this cause by the .NET Framework tests not redirecting stderr properly, or are these warnings actually only occuring on .NET 6.0?

Unable to add non HTML label starting surrounded with `<` and `>`

De AddLabel method performs a check on the first and last character of the value and decides based on that whether it is a HTML string or not. However, this makes it unable to express that you want a literal string starting and ending with <,>. Therefore we need to add separate methods for adding string literals and HTML literals.

@chivandikwa I'm mentioning you for completeness sake, since you provided the current code.

File generation in 2.0.0 in a Windows Forms app much slower

I noticed that version 2.0.0 is significantly slower in file generation (ToDotFile, ToSvgFile, for example) than 1.1.0 when used in a Windows Forms application. Every single ToXYZFile call seems to open a separate console window for a second, I assume to generate the file. This did not happen in 1.1.0

The code example (which generates 20 files) below takes less than a second overall with version 1.1.0. For 2.0.0, it takes a bit more than 20 seconds.
For some of my actual use cases in my application, I noticed that instead of 18 seconds it takes 2 minutes to complete.

Here's how to replicate it:

  1. Create a new VSCode project. Use dotnet new winforms to initialize a new simple Windows Forms app
  2. update the csproj file to reference the Nuget package (either 1.1.0 or 2.0.0):
  <ItemGroup>
    <PackageReference Include="Rubjerg.Graphviz" Version="1.1.0" />
  </ItemGroup>
  1. Update the Form1.cs file to include the following code right after InitializeComponent();

1.1.0:

        Console.WriteLine("Starting: " + DateTime.Now);
        for (int i = 0; i < 10; i++)
        {

            RootGraph root = RootGraph.CreateNew("",GraphType.Directed);
            Node nodeA = root.GetOrAddNode("A");
            root.ComputeLayout();
            root.ToDotFile("c:/tmp/out" + i + ".dot");
            RootGraph root2 = RootGraph.FromDotFile("c:/tmp/out" + i + ".dot");
            root2.ComputeLayout();
            root2.ToSvgFile("c:/tmp/dot_out" + i + ".svg");
        }
        Console.WriteLine("Finished: " + DateTime.Now);

2.0.0:

        Console.WriteLine("Starting: " + DateTime.Now);
        for (int i = 0; i < 10; i++)
        {

            RootGraph root = RootGraph.CreateNew(GraphType.Directed, "");
            Node nodeA = root.GetOrAddNode("A");
            root.CreateLayout();
            root.ToDotFile("c:/tmp/out" + i + ".dot");
            RootGraph root2 = RootGraph.FromDotFile("c:/tmp/out" + i + ".dot");
            root2.CreateLayout();
            root2.ToSvgFile("c:/tmp/dot_out" + i + ".svg");
        }
        Console.WriteLine("Finished: " + DateTime.Now);
    }

Can't add an edge to a port - API only seems to support Nodes

Graphviz supports drawing an edge from a Node to a Port, e.g. this Fiddle

(DOT file contents below in case the Fiddle doesn't work)

digraph Graph1 {
   node [fontname=Helvetica,
   	shape=box,
   	style=filled,
   	fillcolor=lemonchiffon  
   ];
   edge [color=red,
   	fontname=Helvetica
   ];
   Node1	[label=<<table border="0"><tr><td>Node1</td></tr><tr><td port="port1" border="1">Port1</td></tr></table>>];
   Node2 -> Node1:port1;
}

So it's valid to target the edge at the port using the reference "Node1:port1".

I'm trying to achieve this using Graphviz.NetWrapper. What I'm finding is that RootGraph.GetNode("Node1:port1") doesn't work, and if I use RootGraph.GetNode("Node1") then I can't find anything in the API that will let me specify the portname when I try to use RootGraph.GetOrAddEdge()

 public class DemonstratePortIssue
  {
      private const string OutputFolder = "Diagrams";

      public void GenerateDiagram()
      {
          var graphName = "Graph1";
          RootGraph root = CommonDiagramUtils.GenerateNewRootGraph(graphName);

          var node1 = root.GetOrAddNode("Node1");
          node1.SetAttributeHtml("label", "<table border=\"0\"><tr><td>Node1</td></tr><tr><td port=\"port1\" border=\"1\">Port1</td></tr></table>");
          var node2 = root.GetOrAddNode("Node2");

          //var port = root.GetNode("port1");
          var port = root.GetOrAddNode("Node1:port1");
          root.GetOrAddEdge(node2, port, "edge1");

          GenerateOutputFiles("DemonstratePortIssue", root);
      }

      private void GenerateOutputFiles(string graphName, RootGraph root)
      {
          // Make sure output folder exists
          var baseFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, OutputFolder);
          Directory.CreateDirectory(OutputFolder);

          root.ToDotFile(Path.Combine(baseFolder, $"{graphName}.dot"));
          root.ToSvgFile(Path.Combine(baseFolder, $"{graphName}.svg"), "fdp");

      }
  }

I'm thinking it might need an optional "portName" parameter on GetOrAddEdge, if it's non-null then append it to the target node ref, separated with a colon?

Support unicode

Currently we marshal strings as ANSI strings, but I would like to support unicode in e.g. label strings.
Relevant links:

Graphviz doesn't seem to support utf16, while PtrToStringUni is utf16.
PtrToStringUTF8 is not available on .NET 4.8.

Linux Support

I am curious if there has been any attempt at getting a Linux compatible version? I have a fork of this repo where I am able to compile against netcoreapp3.1-windows just fine. However, since the Graphviz native libraries are windows specific I can't just use it on Linux. I am using the library from an Azure app service and it would be a lot cheaper if it could run on a Linux app service plan.

I spent a day diving into the C++ project to see what it might take to get a Linux version compiling. After that initial effort I thought it might be worth asking if you have already given this subject any thought and just haven't had time to do the work. If you do already have some ideas on the path forward here I would be willing to help progress Linux support if I can. As a disclaimer I am predominately a Windows C# developer myself, so I don't bring any significant C++ or Linux expertise, but this is an area of interest for me.

Changing project order in solution makes the build fail

#33 reproduces the issue.

This problems only occurs when executing MSBuild Rubjerg.Graphviz.sln from the commandline (Developer Command Prompt for VS 2019).
From within Visual Studio the build always succeeds.
MSBuild version is the newest: 16.8.3.61104

Generation bug

Given this DOT graph:

digraph {
compound=true;
node [shape=Mrecord]
rankdir="LR"

subgraph "clusterOpen"
	{
	label = "Open"
"Assigned" [label="Assigned|exit / OnDeassigned"];
}
"Deferred" [label="Deferred|entry / Function"];
"Closed" [label="Closed"];

"Open" -> "Assigned" [style="solid", label="Assign / OnAssigned"];
"Assigned" -> "Assigned" [style="solid", label="Assign"];
"Assigned" -> "Closed" [style="solid", label="Close"];
"Assigned" -> "Deferred" [style="solid", label="Defer"];
"Deferred" -> "Assigned" [style="solid", label="Assign / OnAssigned"];
 init [label="", shape=point];
 init -> "Open"[style = "solid"]
}

Graphiviz generates this diagram:
image

Using this code:

	var root = RootGraph.FromDotString(bug.ToDotGraph());
	root.ComputeLayout();
	root.ToSvgFile(@"c:\temp\diagram.svg");

It generates this diagram:
image

The Open text is missing.

Change dot rendering to be done out-of-process

Right now this is done in ComputeLayout though interop calls, but experience has shown that this is prone to uprooting lingering issues in GraphViz. A more robust approach would probably be to invoke dot.exe in a separate process, and communicate dot/xdot via stdin/stdout.

Assembly.GetExecutingAssembly().Location is empty for self-contained (single file) apps

I'm using this library in a self-contained .NET application. During development, everything was working well, but once I published a package I got the following error:
image

The following code is the "culprit" here:
string exeDirectory = AppDomain.CurrentDomain.RelativeSearchPath ?? Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
https://github.com/Rubjerg/Graphviz.NetWrapper/blob/master/Rubjerg.Graphviz/GraphvizCommand.cs#L51

The problem is that Assembly.GetExecutingAssembly().Location is empty for single file apps, thus the Path.Combine call right after the above code fails.

Using System.AppContext.BaseDirectory as an additional option should work from what I understand, so string exeDirectory = AppDomain.CurrentDomain.RelativeSearchPath ?? Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? Path.GetDirectoryName(System.AppContext.BaseDirectory);
might be the right way to fix this issue

Update graphviz version

Update to the next official release, whenevere that comes out. That would also be a good moment to release a new version of this wrapper.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.