function renderPage(currentPageId)
	{
		// Configures math typesetting
		math = xmlDoc.documentElement.getAttribute('math') == '1';
		graph.mathEnabled = math;

		// Sets grid size
		var gs = xmlDoc.documentElement.getAttribute('gridSize');

		if (gs != null)
		{
			graph.gridSize = parseInt(gs);
		}
		else
		{
			graph.gridSize = mxGraph.prototype.gridSize;
		}

		// Configure graph
		graph.foldingEnabled = false;
		graph.setEnabled(false);
		
		// Sets background image
		var bgImg = xmlDoc.documentElement.getAttribute('backgroundImage');

		if (bgImg != null)
		{
			bgImg = JSON.parse(bgImg);
			graph.setBackgroundImage(new mxImage(bgImg.src, bgImg.width,
				bgImg.height, bgImg.x, bgImg.y));
		}
		else
		{
			graph.setBackgroundImage(null);
		}
		
		// Parses XML into graph
		var codec = new mxCodec(xmlDoc);
		var model = graph.getModel();
		codec.decode(xmlDoc.documentElement, model);

		var bg;
		
		if (data.format == 'pdf')
		{
			if (data.bg == 'none' || bg == '')
			{
				bg = null;
			}
			else
			{
				bg = xmlDoc.documentElement.getAttribute('background');
				
				if (bg == 'none' || !bg)
				{
					bg = '#ffffff';
				}
			}
		}
		else
		{
			// Loads background color
			bg = xmlDoc.documentElement.getAttribute('background');

			// Normalizes values for transparent backgrounds
			if (bg == 'none' || bg == '')
			{
				bg = null;
			}
			
			// Checks if export format supports transparent backgrounds
			if (bg == null && data.format != 'gif' && data.format != 'png' && data.format != 'svg')
			{
				bg = '#ffffff';
			}
			
			// Sets background color on page
			if (bg != null)
			{
				document.body.style.backgroundColor = bg;
			}
		}
		
		//handle layers
		if (extras != null && ((extras.layers != null && extras.layers.length > 0) || 
			(extras.layerIds != null && extras.layerIds.length > 0)))
		{
			var childCount = model.getChildCount(model.root);
			
			// Hides all layers
			for (var i = 0; i < childCount; i++)
			{
				model.setVisible(model.getChildAt(model.root, i), false);
			}

			if (extras.layerIds != null)
			{
				for (var i = 0; i < extras.layerIds.length; i++)
				{
					model.setVisible(model.getCell(extras.layerIds[i]), true);
				}
			}
			else
			{
				for (var i = 0; i < extras.layers.length; i++)
				{
					var layer = model.getChildAt(model.root, extras.layers[i]);
					
					if (layer != null)
					{
						model.setVisible(layer, true);
					}
				}
			}
		}
		
		// Sets initial value for PDF page background
		var gb = graph.getGraphBounds();
		graph.pdfPageVisible = false;
		
		// Handles PDF output where the output should match the page format if the page is visible
		if (data.print || data.format == 'pdf')
		{
			var printScale = 1;
			
			var pw = data.pageWidth || xmlDoc.documentElement.getAttribute('pageWidth');
			var ph = data.pageHeight || xmlDoc.documentElement.getAttribute('pageHeight');
			graph.pdfPageVisible = true;
			
			if (pw != null && ph != null)
			{
				graph.pageFormat = new mxRectangle(0, 0, parseFloat(pw), parseFloat(ph));
			}
			
			var ps = data.pageScale || xmlDoc.documentElement.getAttribute('pageScale');
			
			if (ps != null)
			{
				graph.pageScale = ps;
			}

			if (data.fileTitle != null)
			{
				document.title = data.fileTitle;
			}
			
			var pf = graph.pageFormat;
			var temp = data.scale;
			pf.width = Math.ceil(pf.width * graph.pageScale);
			pf.height = Math.ceil(pf.height * graph.pageScale);
			var scale = 1;

			if (data.fit == '1' && data.sheetsAcross != null && data.sheetsDown != null)
			{
				var h = data.sheetsAcross;
				var v = data.sheetsDown;

				if (!isNaN(temp))
				{
					pf.width = Math.ceil(pf.width * temp);
					pf.height = Math.ceil(pf.height * temp);
				}
				
				scale = Math.min((pf.height * v) / (gb.height / graph.view.scale),
					(pf.width * h) / (gb.width / graph.view.scale));
			}
			else
			{
				scale = !isNaN(temp) ? temp : 1;
			}

			// Applies print scale
			data.scale = scale * printScale;

			graph.getPageSize = function()
			{
				return new mxRectangle(0, 0, this.pageFormat.width * this.pageScale,
					this.pageFormat.height * this.pageScale);
			};
			
			graph.getPageLayout = function()
			{
				var size = this.getPageSize();
				var bounds = this.getGraphBounds();

				if (bounds.width == 0 || bounds.height == 0)
				{
					return new mxRectangle(0, 0, 1, 1);
				}
				else
				{
					// Computes untransformed graph bounds
					var x = Math.ceil(bounds.x / this.view.scale - this.view.translate.x);
					var y = Math.ceil(bounds.y / this.view.scale - this.view.translate.y);
					var w = Math.floor(bounds.width / this.view.scale);
					var h = Math.floor(bounds.height / this.view.scale);
					
					var x0 = Math.floor(x / size.width);
					var y0 = Math.floor(y / size.height);
					var w0 = Math.ceil((x + w) / size.width) - x0;
					var h0 = Math.ceil((y + h) / size.height) - y0;
					
					return new mxRectangle(x0, y0, w0, h0);
				}
			};

			// Fits the number of background pages to the graph
			graph.view.getBackgroundPageBounds = function()
			{
				var layout = this.graph.getPageLayout();
				var page = this.graph.getPageSize();
				
				return new mxRectangle(
					this.scale * (this.translate.x + layout.x * page.width),
					this.scale * (this.translate.y + layout.y * page.height),
					this.scale * layout.width * page.width,
					this.scale * layout.height * page.height);
			};
		}

		if (!graph.pdfPageVisible)
		{
			var b = graph.getGraphBounds();
			
			// Floor is needed to keep rendering crisp
			if (data.w > 0 || data.h > 0)
			{
				var s = 1;
				
				if (data.w > 0 && data.h > 0)
				{
					s = Math.min(data.w / b.width, data.h / b.height);
				}
				else if (data.w > 0)
				{
					s = data.w / b.width;
				}
				else
				{
					s = data.h / b.height;
				}
				
				graph.view.scaleAndTranslate(s,
					Math.floor(data.border / s - Math.floor(b.x)),
					Math.floor(data.border / s - Math.floor(b.y)));
			}
			else
			{
				var s = data.scale;
				
				if (autoScale)
				{
					var pageWidth = (extras != null && extras.pageWidth != null) ? extras.pageWidth : 800;
					
					if (b.width < pageWidth & b.height < 1.5 * pageWidth)
					{
						s = 4;
					}
					else if (b.width < 2 * pageWidth & b.height < 3 * pageWidth)
					{
						s = 3;
					}
					else if (b.width < 4 * pageWidth && b.height < 6 * pageWidth)
					{
						s = 2;
					}
					
					if (extras != null && extras.isGoogleSheet != null)
					{
						GOOGLE_APPS_MAX_AREA = GOOGLE_SHEET_MAX_AREA;
					}
					
					//The image cannot exceed 25 MP to be included in Google Apps
					if (b.width * s * b.height * s > GOOGLE_APPS_MAX_AREA)
					{
						//Subtracting 0.01 to prevent any other rounding that can make slightly over 25 MP 
						s = Math.sqrt(GOOGLE_APPS_MAX_AREA / (b.width * b.height)) - 0.01;
					}
				}
				
				graph.view.scaleAndTranslate(s,
					Math.floor(data.border - Math.floor(b.x)),
					Math.floor(data.border - Math.floor(b.y)));
			}
		}
		else
		{
			// Disables border for PDF page export
			data.border = 0;
			
			// Moves to first page in page layout
			var layout = graph.getPageLayout();
			var page = graph.getPageSize();
			var dx = layout.x * page.width;
			var dy = layout.y * page.height;
			
			if (dx != 0 || dy != 0)
			{
				graph.view.setTranslate(Math.floor(-dx), Math.floor(-dy));
			}
		}
		
		// Gets the diagram bounds and sets the document size
		bounds = (graph.pdfPageVisible) ? graph.view.getBackgroundPageBounds() : graph.getGraphBounds();
		bounds.width = Math.ceil(bounds.width + data.border) + 1; //The 1 extra pixels to prevent cutting the cells on the edges when crop is enabled
		bounds.height = Math.ceil(bounds.height + data.border) + 1; //The 1 extra pixels to prevent starting a new page. TODO Not working in every case
		
		// Print to pdf fails for 1x1 pages
		if (bounds.width <= 1 && bounds.height <= 1)
		{
			bounds.width = 2;
			bounds.height = 2;
		}

		expScale = graph.view.scale || 1;
		
		// Converts the graph to a vertical sequence of pages for PDF export
		if (graph.pdfPageVisible)
		{
			var pf = graph.pageFormat || mxConstants.PAGE_FORMAT_A4_PORTRAIT;
			var scale = (data.print || data.format == 'pdf') ? data.scale : 1 / graph.pageScale;
			var autoOrigin = ((data.print || data.format == 'pdf') && data.fit == '1') ||
				data.crop == '1' || xmlDoc.documentElement.getAttribute('page') != '1';
			var border = 0;

			// Negative coordinates are cropped or shifted if page visible
			var x0 = 0;
			var y0 = 0;
	
			if (data.crop == '1')
			{
				pf.width = (gb.width + 1.5) * scale;
				pf.height = (gb.height + 1.5) * scale;
			}

			// Starts at first visible page
			if (!autoOrigin)
			{
				var layout = graph.getPageLayout();
				x0 -= layout.x * pf.width;
				y0 -= layout.y * pf.height;
			}

			var anchorId = (currentPageId != null) ? 'page/id,' + currentPageId : null;
			
			if (preview == null)
			{
				preview = new mxPrintPreview(graph, scale, pf, border, x0, y0);
				preview.printBackgroundImage = true;
				preview.autoOrigin = autoOrigin;
				preview.backgroundColor = bg;
				preview.pageMargin = (data.pageMargin != null) ?
					parseInt(data.pageMargin) : ((data.crop == '1') ?
						0 : preview.pageMargin);
				
				// Replaces background images with SVG subtrees
				var previewDrawBackgroundImage = preview.drawBackgroundImage;
				
				preview.drawBackgroundImage = function(img)
				{
					previewDrawBackgroundImage.apply(this, arguments);

					if (img.node != null)
					{
						EditorUi.embedSvgImages(img.node);

						graph.disableSvgLinks(img.node, function(link)
						{
							link.setAttribute('href', 'javascript:void(0)');		
						});
					}
				};

				// Renders the grid and configures math
				var previewAddGraphFragment = preview.addGraphFragment;

				preview.addGraphFragment = function(dx, dy, scale, pageNumber, div, clip)
				{
					previewAddGraphFragment.apply(this, arguments);

					// Disables math rendering in graph
					if (!graph.mathEnabled)
					{
						div.classList.add('geDisableMathJax')
					}

					waitForGoogleFontImports(div);
				};

				// Renders print output into this document and removes the graph container
				preview.gridColor = gridColor;
				preview.gridSize = graph.gridSize;
				preview.gridSteps = graph.view.gridSteps;
				preview.open(null, window, null, null, anchorId, pf);
			}
			else
			{
				preview.gridColor = gridColor;
				preview.gridSize = graph.gridSize;
				preview.gridSteps = graph.view.gridSteps;
				preview.backgroundColor = bg;
				preview.autoOrigin = autoOrigin; 
				preview.appendGraph(graph, scale, x0, y0, null, null, anchorId, pf);
			}

			// Adds shadow
			// NOTE: Shadow rasterizes output
			/*if (mxClient.IS_SVG && xmlDoc.documentElement.getAttribute('shadow') == '1')
			{
				var svgs = document.getElementsByTagName('svg');
				
				for (var i = 0; i < svgs.length; i++)
				{
					var svg = svgs[i];

					var filter = graph.addSvgShadow(svg, null, true);
					filter.setAttribute('id', 'shadow-' + i);
					svg.appendChild(filter);
					svg.setAttribute('filter', 'url(#' + 'shadow-' + i + ')');
				}
				
				border = 7;
			}*/
			
			bounds = new mxRectangle(0, 0, pf.width, pf.height);
		}
		else
		{
			var bgImg = graph.backgroundImage;

			if (bgImg != null)
			{
				var t = graph.view.translate;
				var s = graph.view.scale;

				bounds.add(new mxRectangle(
					(t.x + bgImg.x) * s, (t.y + bgImg.y) * s,
					bgImg.width * s, bgImg.height * s));

				if (t.x < 0 || t.y < 0)
				{
					graph.view.setTranslate(
						t.x < 0 ? Math.max(-bgImg.x * s, t.x) : t.x,
						t.y < 0 ? Math.max(-bgImg.y * s, t.y) : t.y);
					bounds.x = 0.5;
					bounds.y = 0.5;
				}
			}

			// Adds shadow
			// NOTE: PDF shadow rasterizes output so it's disabled
			if (data.format != 'pdf' && mxClient.IS_SVG && xmlDoc.documentElement.getAttribute('shadow') == '1')
			{
				graph.addSvgShadow(graph.view.canvas.ownerSVGElement, null, true);
				graph.setShadowVisible(true);
				bounds.width += 7;
				bounds.height += 7;
			}
			
			document.body.style.width = Math.ceil(bounds.x + bounds.width) + 'px';
			document.body.style.height = Math.ceil(bounds.y + bounds.height) + 'px';
		}
	}
Renders the given page or all pages.