protected Node createNode(Document document, String title,
			GraphLayoutCache cache, int inset)
			throws ParserConfigurationException {

		// Collects basic geometric details
		Rectangle2D bounds = GraphLayoutCache.getBounds(cache.getAllViews());
		double dx = bounds.getX() - inset;
		double dy = bounds.getY() - inset;
		int titleheight = 0;
		int titlewidth = 0;

		// Adds the title to the diagram geometry
		Node titleNode = null;
		if (title != null && title.length() > 0) {
			Font titleFont = TITLE_FONT;
			titleheight = titleFont.getSize() + TITLE_VSPACING;
			titlewidth = (int) (title.length() * titleFont.getSize() / FONT_PROPORTION_FACTOR);
			titleNode = createTextNode(document, title, null, titleFont,
					TITLE_HEXCOLOR, inset, inset + titleheight - TITLE_VSPACING);
		}
		double width = Math.max(titlewidth, bounds.getWidth() + 2 * inset);
		double height = bounds.getHeight() + 2 * inset + titleheight;
		Node root = createRoot(document, width, height, inset);

		// Adds defs for filters, gradients, markers etc
		Element defs = (Element) document.createElement("defs");
		// Define arrow markers
		Element g = (Element) document.createElement("g");
		g.setAttribute("id", "arrowMarker");
		defs.appendChild(g);
		Element stroke = (Element) document.createElement("g");
		stroke.setAttribute("stroke-width", "0");
		g.appendChild(stroke);
		Element path = (Element) document.createElement("path");
		path.setAttribute("d", "M 4 -2 L 0 0 L 4 2 L 3 1 L 3 -1 L 4 -2");
		stroke.appendChild(path);
		Element marker = (Element) document.createElement("marker");
		marker.setAttribute("id", "startMarker");
		marker.setAttribute("markerWidth", "48");
		marker.setAttribute("markerHeight", "24");
		marker.setAttribute("viewBox", "-4 -4 25 5");
		marker.setAttribute("orient", "auto");
		marker.setAttribute("refX", "0");
		marker.setAttribute("refY", "0");
		marker.setAttribute("markerUnits", "strokeWidth");
		defs.appendChild(marker);
		g = (Element) document.createElement("g");
		marker.appendChild(g);
		Element use = (Element) document.createElement("use");
		use.setAttribute("xlink:href", "#arrowMarker");
		use.setAttribute("transform", "rotate(180)");
		use.setAttribute("stroke-width", "1");
		g.appendChild(use);

		// Also definition for non-rotated arrow
		marker = (Element) document.createElement("marker");
		marker.setAttribute("id", "endMarker");
		marker.setAttribute("markerWidth", "48");
		marker.setAttribute("markerHeight", "24");
		marker.setAttribute("viewBox", "-4 -4 25 5");
		marker.setAttribute("orient", "auto");
		marker.setAttribute("refX", "0");
		marker.setAttribute("refY", "0");
		marker.setAttribute("markerUnits", "strokeWidth");
		defs.appendChild(marker);
		g = (Element) document.createElement("g");
		marker.appendChild(g);
		use = (Element) document.createElement("use");
		use.setAttribute("xlink:href", "#arrowMarker");
		use.setAttribute("stroke-width", "1");
		g.appendChild(use);

		// This does currently not work in the SVG plugin
		// defs.appendChild(createDropShadowFilter(document, 2, 2, 2));
		root.appendChild(defs);

		// Adds the graph title
		if (titleNode != null) {
			root.appendChild(titleNode);
			dy -= titleheight;
		}

		// "Draws" all views, topmost first
		GraphModel model = cache.getModel();
		CellView[] views = cache.getAllViews();
		for (int i = 0; i < views.length; i++) {
			Object cell = views[i].getCell();
			if (!model.isPort(cell)) {

				// Invokes edge- or vertex renderer based on the cell type
				Node node = (model.isEdge(cell)) ? edgeFactory.createNode(this,
						document, views[i], dx, dy) : vertexFactory.createNode(
						this, document, cache, views[i], dx, dy);
				if (node != null) {
					root.appendChild(node);
				}
			}
		}

		// Collect the dynamically created gradient definitions
		// and add them to the definitions in the defs section
		Iterator it = gradients.values().iterator();
		while (it.hasNext()) {
			Object gradient = it.next();
			if (gradient instanceof Node) {
				defs.appendChild((Node) gradient);
			}
		}

		return root;
	}