UNPKG

gojs

Version:

Interactive diagrams, charts, and graphs, such as trees, flowcharts, orgcharts, UML, BPMN, or business diagrams

239 lines (212 loc) 9.63 kB
<!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Generating PDF with multiple Diagrams</title> <meta name="description" content="A demonstration of generating a PDF file showing multiple diagrams that are not visible on the page." /> <!-- Copyright 1998-2023 by Northwoods Software Corporation. --> <meta charset="UTF-8"> <script src="https://unpkg.com/gojs"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js"></script> <script src="https://unpkg.com/svg-to-pdfkit"></script> <script id="code"> // Remember three separate models for the diagram const allModels = new go.Map(); // This just creates and initializes a Diagram. // The details do not really matter for this demo. var myDiagram = null; function init() { const $ = go.GraphObject.make; myDiagram = new go.Diagram("myDiagramDiv", { "grid.visible": true, "undoManager.isEnabled": true }); myDiagram.nodeTemplate = $(go.Node, "Auto", new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify), $(go.Shape, "RoundedRectangle", { strokeWidth: 0, fill: "white" }, new go.Binding("fill", "color")), $(go.TextBlock, { margin: 8 }, new go.Binding("text")) ); // create and remember three models allModels.add("one", new go.GraphLinksModel( { nodeDataArray: [ { key: 1, text: "one", color: "#FFAAAA" }, { key: 2, text: "one" + " 1", color: "#FFAAAA" }, { key: 3, text: "one" + " 2", color: "#FFAAAA" } ], linkDataArray: [ { from: 1, to: 2 }, { from: 1, to: 3 } ] })); allModels.add("two", new go.GraphLinksModel( { nodeDataArray: [ { key: 1, text: "two", color: "#AAFFAA", loc: "0 0" }, { key: 2, text: "two" + " 1", color: "#AAFFAA", loc: "150 -50" }, { key: 3, text: "two" + " 2", color: "#AAFFAA", loc: "-50 150" }, { key: 4, text: "two" + " 3", color: "#AAFFAA", loc: "1150 150" } ], linkDataArray: [ { from: 1, to: 2 }, { from: 1, to: 3 }, { from: 1, to: 4 } ] })); allModels.add("three", new go.GraphLinksModel( { nodeDataArray: [ { key: 1, text: "three", color: "#AAAAFF", loc: "0 0" }, { key: 2, text: "three" + " 1", color: "#AAAAFF", loc: "-150 -150" }, { key: 3, text: "three" + " 2", color: "#AAAAFF", loc: "150 150" }, { key: 4, text: "three" + " 3", color: "#AAAAFF", loc: "-150 150" }, { key: 5, text: "three" + " 4", color: "#AAAAFF", loc: "150 -150" } ], linkDataArray: [ { from: 1, to: 2 }, { from: 1, to: 3 }, { from: 1, to: 4 }, { from: 1, to: 5 } ] })); fetchModel("one"); } // Load a model for a given NAME; can be used without CALLBACK, // which would initialize myDiagram rather than using the hidden diagram/div function fetchModel(name, callback) { if (!callback) callback = m => myDiagram.model = m; const m = allModels.get(name) || allModels.get("one"); callback(m); } // Use a hidden diagram/div to render a diagram function useModel(model, callback) { // this initializes the model for an existing visible Diagram on the page, // or this will create and initialize a new Diagram const div = document.getElementById("myHiddenDiagramDiv"); const olddiag = go.Diagram.fromDiv(div); if (olddiag) olddiag.div = null; const diagram = new go.Diagram(div); diagram.nodeTemplate = myDiagram.nodeTemplate; diagram.addDiagramListener("InitialLayoutCompleted", callback); diagram.model = model; } // This common function is called both when showing the PDF in an iframe and when downloading a PDF file. // The options include: // "pageSize", either "A4" or "LETTER" (the default) // "layout", either "portrait" (the default) or "landscape" // "margin" for the uniform page margin on each page (default is 36 pt) // "padding" instead of the Diagram.padding when adjusting the Diagram.documentBounds for the area to render // "imgWidth", size of diagram image for one page; defaults to the page width minus margins // "imgHeight", size of diagram image for one page; defaults to the page height minus margins // "parts", "background", "showTemporary", "showGrid", all are passed to Diagram.makeSvg function generatePdf(action, namelist, options) { if (!options) options = {}; const pageSize = (options.pageSize || "LETTER").toUpperCase(); if (pageSize !== "LETTER" && pageSize !== "A4") throw new Error("unknown page size: " + pageSize); // LETTER: 612x792 pt == 816x1056 CSS units // A4: 595.28x841.89 pt == 793.71x1122.52 CSS units const pageWidth = (pageSize === "LETTER" ? 612 : 595.28) * 96 / 72; // convert from pt to CSS units const pageHeight = (pageSize === "LETTER" ? 792 : 841.89) * 96 / 72; const layout = (options.layout || "portrait").toLowerCase(); if (layout !== "portrait" && layout !== "landscape") throw new Error("unknown layout: " + layout); if (layout === "landscape") { const temp = pageWidth; pageWidth = pageHeight; pageHeight = temp; } const margin = options.margin !== undefined ? options.margin : 36; // pt: 0.5 inch margin on each side const padding = options.padding !== undefined ? options.padding : new go.Margin(0); // CSS units const imgWidth = options.imgWidth !== undefined ? options.imgWidth : (pageWidth-margin/72*96*2); // CSS units const imgHeight = options.imgHeight !== undefined ? options.imgHeight : (pageHeight-margin/72*96*2); // CSS units const pageOptions = { size: pageSize, margin: margin, // unit is pt layout: layout }; const namelistlength = namelist.length; function generateDiagram(namelist, doc, stream, callback) { if (namelist.length > 0) { if (namelist.length !== namelistlength) doc.addPage(pageOptions); const name = namelist.shift(); fetchModel(name, m => useModel(m, e => { const diagram = e.diagram; const bnds = diagram.documentBounds; // add some descriptive text doc.text("Diagram: " + name); const db = diagram.documentBounds.copy().subtractMargin(diagram.padding).addMargin(padding); const p = db.position; const makeOptions = {}; if (options.parts !== undefined) makeOptions.parts = options.parts; if (options.background !== undefined) makeOptions.background = options.background; if (options.showTemporary !== undefined) makeOptions.showTemporary = options.showTemporary; if (options.showGrid !== undefined) makeOptions.showGrid = options.showGrid; makeOptions.scale = Math.min(1, Math.min(imgWidth/db.width, imgHeight/db.height)); makeOptions.position = new go.Point(p.x, p.y); makeOptions.size = new go.Size(db.width, db.height); const svg = diagram.makeSvg(makeOptions); SVGtoPDF(doc, svg, doc.x, doc.y, null); // then do rest of list generateDiagram(namelist, doc, stream, callback); }) ); } else { // no more diagrams to generate, so just finish up the PDF callback(); } } // end generateDiagram require(["blob-stream", "pdfkit"], (blobStream, PDFDocument) => { // start the PDF const doc = new PDFDocument(pageOptions); const stream = doc.pipe(blobStream()); generateDiagram(namelist, doc, stream, () => { // finish up the PDF doc.end(); stream.on('finish', () => action(stream.toBlob('application/pdf'))); }); }); } // end generatePdf const namelist = ["one", "two", "three"]; // this could be used to generate the <ul> myList const pdfOptions = { showTemporary: true, // default is false // layout: "landscape", // instead of "portrait" // pageSize: "A4" // instead of "LETTER" }; function downloadPdf() { // called by HTML button generatePdf(blob => { const datauri = window.URL.createObjectURL(blob); const a = document.createElement("a"); a.style = "display: none"; a.href = datauri; a.download = "myDiagram.pdf"; document.body.appendChild(a); requestAnimationFrame(() => { a.click(); window.URL.revokeObjectURL(datauri); document.body.removeChild(a); }); }, namelist, pdfOptions); } </script> </head> <body onload="init()"> <div id="sample"> <div id="myDiagramDiv" style="border: solid 1px black; width:400px; height:400px"></div> Models: <ul id="myList"> <li><button onclick="fetchModel(event.target.textContent)">one</button></li> <li><button onclick="fetchModel(event.target.textContent)">two</button></li> <li><button onclick="fetchModel(event.target.textContent)">three</button></li> </ul> <button onclick="downloadPdf()">Download PDF of all three diagrams</button> <div id="myHiddenDiagramDiv" style="display:none"></div> </div> </body> </html>