Creating Images on the Server

It may be useful for many applications to create images of Diagrams with GoJS, and this page details some of the options for such a task.

PhantomJS

One of the best options for creating images server-side (or anywhere) is PhantomJS, a platform-agnostic "headless" implementation of WebKit. PhantomJS allows us to run anything we would normally run inside of a browser in a command shell (terminal, command prompt), creating screen captures or image output as we go.

The following code is a small example using PhantomJS. If you saved the JavaScript as createImage.js and ran it with phantom (phantomjs createImage.js) it would create an image of a Diagram called myDiagramImage.png. It has the requirement that go.js is in the same folder, though this path can be modified as appropriate. The Diagram code in the sample is the same as that in the Minimal sample.

// Example of (server-side or headless) image creation using PhantomJS
// PhantomJS can be found at: https://github.com/ariya/phantomjs

// Our page will contain nothing but a div tag and an img tag.
// We will create our Diagram on the div and use Diagram.makeImageData to give the img a source.
// Then we will render only the image created.
var page = require('webpage').create();
page.content = '<html><body><div id="myDiagramDiv"></div> <img id="myImg" /></body></html>';

// We include go.js before acting on our page, assuming it is in the same directory
page.injectJs('go.js');

page.evaluate(function() {
  // GoJS is loaded, now we set up a diagram and make the image we want

  // This example GoJS code is taken from the Minimal.html sample
  var $ = go.GraphObject.make;  // for conciseness in defining templates

  myDiagram = new go.Diagram("myDiagramDiv");  // create a Diagram for the DIV HTML element
  myDiagram.initialContentAlignment = go.Spot.Center;

  // define a simple Node template (but use the default Link template)
  myDiagram.nodeTemplate =
    $(go.Node, "Auto",
      $(go.Shape, "RoundedRectangle",
        // Shape.fill is bound to Node.data.color
        new go.Binding("fill", "color")),
      $(go.TextBlock,
        { margin: 3 },  // some room around the text
        // TextBlock.text is bound to Node.data.key
        new go.Binding("text", "key"))
    );

  // create the model data that will be represented by Nodes and Links
  var nodeDataArray = [
    { key: "Alpha", color: "lightblue" },
    { key: "Beta", color: "orange" },
    { key: "Gamma", color: "lightgreen" },
    { key: "Delta", color: "pink" }
  ];
  var linkDataArray = [
    { from: "Alpha", to: "Beta" },
    { from: "Alpha", to: "Gamma" },
    { from: "Beta", to: "Beta" },
    { from: "Gamma", to: "Delta" },
    { from: "Delta", to: "Alpha" }
  ];
  myDiagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
  // end of code from Minimal.html sample

  var img = document.getElementById('myImg');
  img.src = myDiagram.makeImageData({
    scale: 1,
    // PhantomJS tends to handle transparency poorly in the images it renders,
    // so we prefer to use a white background:
    background: "white"
  })

  document.body.style.margin = '0px';
});

// We want to ensure that the image is done loading before we render
setInterval(function() {
  var imgComplete = page.evaluate(function() {
    return document.getElementById('myImg').complete
  });

  if (imgComplete) {
    // PhantomJS renders the entire page, and we just want to output the <img>,
    // so we must clip to its bounding rect:
    var clipRect = page.evaluate(function() {
      return document.getElementById('myImg').getBoundingClientRect();
    });
    page.clipRect = {
      top:    clipRect.top,
      left:   clipRect.left,
      width:  clipRect.width,
      height: clipRect.height
    }
    page.render('myDiagramImage.png');
    phantom.exit();
  }
}, 100);

Since PhantomJS contains a full, native DOM implementation, any work you wish to do with GoJS server-side (such as complex layouts or positioning) is also entirely possible.

Using GoJS with Node.js

GoJS can also be used with Node.js for server-side image creation, though Node.js does not have native support for DOM, CSS, or the canvas element. Therefore using Node.js with GoJS requires the installation of Node packages for DOM and canvas simulation that are not guaranteed to work. To use the GoJS library with Node.js, you must install the following:

We recommend that you follow the installation instructions for each of these carefully, as there are a few dependencies, such as Python for Node's package manager.

The following code gives a complete example of create a Diagram inside of Node.js and outputting it to a site. The Diagram code in the sample is the same as that in the Minimal sample.

// Example of server-side image creation using Node.js
// Node.js can be found at http://nodejs.org

// Module dependencies
var http = require("http");
var fs = require("fs");
var Canvas = require('canvas'); // https://github.com/LearnBoost/node-canvas
var jsdom = require('jsdom');  // https://github.com/tmpvar/jsdom


// Navigating to lochalhost:8888 will show an image of a Diagram made server-side with Node.js
http.createServer(function(request, response) {
  writeResponse(response);
}).listen(8888);

function writeResponse(response) {
  // In writing our response, we use jsdom to create a fake window and DOM,
  // create a Diagram in that environment, and
  jsdom.env(
    '<html><body></body></html>', // we create an empty HTML page
    ["go.js"], // assuming go.js is in the same folder as this file
    function(errors, window) {
      // Necessary in this environment, as typing simply "go" will not find "window.go"
      // Alternatively, we could have written this statement in makeDiagramAndData,
      // or made it global here
      var go = window.go;

      var data = makeDiagramAndData(window, go);

      if (response) {
        response.writeHeader(200, {"Content-Type": "text/html"});
        response.write('<img src="' + data + '" />');
        response.end();
      }
    }
  );
}

// All GoJS specific code is in this function
function makeDiagramAndData(window, go) {
  // This example GoJS code is taken from the Minimal.html sample
  var $ = go.GraphObject.make;  // for conciseness in defining templates
  myDiagram = new go.Diagram(); // Note that no DIV is supplied

  // define a simple Node template (but use the default Link template)
  myDiagram.nodeTemplate =
    $(go.Node, "Auto",
      $(go.Shape, "RoundedRectangle",
        // Shape.fill is bound to Node.data.color
        new go.Binding("fill", "color")),
      $(go.TextBlock,
        { margin: 3 },  // some room around the text
        // TextBlock.text is bound to Node.data.key
        new go.Binding("text", "key"))
    );

  // create the model data that will be represented by Nodes and Links
  var nodeDataArray = [
    { key: "Alpha", color: "lightblue" },
    { key: "Beta", color: "orange" },
    { key: "Gamma", color: "lightgreen" },
    { key: "Delta", color: "pink" }
  ];
  var linkDataArray = [
    { from: "Alpha", to: "Beta" },
    { from: "Alpha", to: "Gamma" },
    { from: "Beta", to: "Beta" },
    { from: "Gamma", to: "Delta" },
    { from: "Delta", to: "Alpha" }
  ];
  myDiagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
  // end of code from Minimal.html sample

  // Create image data
  return myDiagram.makeImageData({
    scale: 1
  });
}

Known issue with Node.js and node-canvas

As of December 2012, node-canvas does not parse named CSS colors unless the names are all lowercase. This means that "Blue" may not parse correctly, but "blue" will.