create-gojs-kit
Version:
A CLI for downloading GoJS samples, extensions, and docs
265 lines (236 loc) • 11.5 kB
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-2025 by Northwoods Software Corporation. -->
<meta charset="UTF-8" />
<script src="https://cdn.jsdelivr.net/npm/gojs"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/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() {
myDiagram = new go.Diagram('myDiagramDiv', {
'grid.visible': true,
'undoManager.isEnabled': true,
});
myDiagram.nodeTemplate = new go.Node('Auto')
.bindTwoWay('location', 'loc', go.Point.parse, go.Point.stringify)
.add(
new go.Shape('RoundedRectangle', { strokeWidth: 0, fill: 'white' })
.bind('fill', 'color'),
new go.TextBlock({ margin: 8 })
.bind('text', 'key')
);
// 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);
// render image contents "inline" using a Data URL
makeOptions.elementFinished = (graphobject, svgelement) => {
const pic = graphobject;
if (!(pic instanceof go.Picture) || !(svgelement instanceof SVGImageElement)) return;
const img = pic.element;
if (img) {
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
try {
svgelement.setAttribute('href', canvas.toDataURL());
} catch (ex) {
console.log('Error drawing ' + pic.toString() + '\n ' + ex.toString());
}
}
};
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>