Is it possible to have bpmn-to-image create images for the root diagram and for all diagrams of collapsed subprocesses?
What would be needed to iterate through all <bpmndi:BPMNDiagram>
in a BPMN model and create an image for each of them?
Is it possible to have bpmn-to-image create images for the root diagram and for all diagrams of collapsed subprocesses?
What would be needed to iterate through all <bpmndi:BPMNDiagram>
in a BPMN model and create an image for each of them?
Assuming the modeller application has a global variable modeler
(i.e var modeler = new BpmnModeler(/*...*/)
), the following script bpmn2svg.js
creates an SVG for the root and each (collapsed) subprocess:
const puppeteer = require('puppeteer');
const fs = require('fs');
const path = require('path');
const { exec } = require('child_process');
const args = process.argv.slice(2);
let fileName;
let outputDir = '.';
let serverURL;
// Parse command-line arguments
for (let i = 0; i < args.length; i++) {
if (args[i] === '-o' || args[i] === '--output') {
if (i + 1 >= args.length || args[i + 1].startsWith('-')) {
console.error('Error: Missing output directory after -o option');
process.exit(1);
}
outputDir = args[i + 1];
i++; // Skip the next argument (output directory)
}
else if (args[i] === '-s' || args[i] === '--server') {
if (i + 1 >= args.length || args[i + 1].startsWith('-')) {
console.error('Error: Missing server URL after -s option');
process.exit(1);
}
serverURL = args[i + 1];
i++; // Skip the next argument (server URL)
}
else {
fileName = args[i];
}
}
// Check if a filename is provided
if (!fileName) {
console.error('Usage: node bpmn2svg.js [-s <serverURL>] [-o <outputDir>] <BPMN filename>');
process.exit(1);
}
// Check if the file exists
if (!fs.existsSync(fileName)) {
console.error('File does not exist:', fileName);
process.exit(1);
}
// Check if the output directory exists; if not, create it
if (!fs.existsSync(outputDir)) {
console.log('Output directory does not exist. Creating it...');
try {
fs.mkdirSync(outputDir, { recursive: true });
} catch (err) {
console.error('Error: Unable to create output directory:', err);
process.exit(1);
}
}
const baseName = path.basename(fileName, path.extname(fileName));
if ( serverURL ) {
// Convert BPMN to SVG
bpmn2svg(serverURL);
}
else {
// Start the local server
const serverProcess = exec('npm run start');
serverProcess.on('error', (err) => {
console.error('Error starting local server:', err);
process.exit(1);
});
serverProcess.on('exit', (code, signal) => {
process.exit(1);
});
let serverReady = false;
serverProcess.stdout.on('data', async (data) => {
// Check if the stdout contains the message indicating that the server is ready
if (data.includes('Your application is ready')) {
serverReady = true;
}
if (serverReady && data.includes('http')) {
// Extract the URL from the data
const urlMatch = data.match(/(http\S+)/);
if (urlMatch && urlMatch[1]) {
serverURL = urlMatch[1];
console.log('Server URL:', serverURL);
// Convert BPMN to SVG
await bpmn2svg(serverURL);
// Close the local server process
serverProcess.kill();
} else {
console.error('Error: Failed to extract server URL.');
process.exit(1);
}
}
});
serverProcess.stderr.on('data', (data) => {
console.error(`Server process error: ${data}`);
});
}
async function bpmn2svg(serverURL) {
// Launch a headless browser
// const browser = await puppeteer.launch({ headless: false }); // use to debug
const browser = await puppeteer.launch({ headless: true });
// Open a new page
const page = await browser.newPage();
// Navigate to the local webpage
await page.goto(serverURL);
// Read the content of the local file
const diagram = fs.readFileSync(fileName, 'utf-8');
// Pass the file content to the page context and call the "show" function
await page.evaluate((diagram) => {
modeler.importXML(diagram);
}, diagram);
// Get SVG of root diagram
let svg = await page.evaluate(() => {
return modeler.saveSVG({ format: true }).then((model) => {
return model.svg;
});
});
// Create the SVG file
let outputFile = path.join(outputDir, baseName + '.svg');
fs.writeFileSync(outputFile, svg, 'utf-8');
// Find collapsed elements
const collapsed = await page.evaluate(() => {
let elementRegistry = modeler.get('elementRegistry');
return elementRegistry.getAll().filter( (element) => { return element.collapsed; } );
});
for ( const element of collapsed ) {
const id = element.id;
// Expand collapsed element
await page.evaluate((element) => {
let elementRegistry = modeler.get('elementRegistry');
let canvas = modeler.get('canvas');
canvas.setRootElement(elementRegistry.get(element.id + '_plane'));
}, element);
// Get SVG of expanded element
svg = await page.evaluate(() => {
return modeler.saveSVG({ format: true }).then((model) => {
return model.svg;
});
});
// Create the SVG file
outputFile = path.join(outputDir, baseName + '-' + id + '.svg');
fs.writeFileSync(outputFile, svg, 'utf-8');
}
console.log("Saved " + (collapsed.length+1) + " diagrams to: " + outputDir);
// Close the browser
await browser.close();
}
If no server URL is provided the script tries to start a local server using npm run start
(this needs to be available for the modeller used).
P.S. puppeteer
can be installed via npm install puppeteer
This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.