From b195d569cadbc88afdb0cdfba955efac84c3ac05 Mon Sep 17 00:00:00 2001 From: Guillaume Grossetie Date: Sat, 3 Jun 2023 19:11:39 +0200 Subject: [PATCH] Export standalone SVG for diagrams.net (including width, height and viewBox) --- diagrams.net/src/index.js | 14 ++++++---- diagrams.net/src/worker.js | 55 ++++++++++++++++++++++++-------------- 2 files changed, 44 insertions(+), 25 deletions(-) diff --git a/diagrams.net/src/index.js b/diagrams.net/src/index.js index e9d747165..466073584 100644 --- a/diagrams.net/src/index.js +++ b/diagrams.net/src/index.js @@ -1,5 +1,5 @@ const http = require('node:http') -const { Worker } = require('./worker') +const { Worker, SyntaxError } = require('./worker') const Task = require('./task') const instance = require('./browser-instance') const micro = require('micro') @@ -23,11 +23,15 @@ const puppeteer = require('puppeteer') const output = await worker.convert(new Task(diagramSource, isPng)) res.setHeader('Content-Type', isPng ? 'image/png' : 'image/svg+xml') return micro.send(res, 200, output) - } catch (e) { - if (!(e instanceof puppeteer.errors.TimeoutError)) { - console.log('Exception during convert', e) + } catch (err) { + if (err instanceof puppeteer.errors.TimeoutError) { + return micro.send(res, 408, 'Request timeout') + } else if (err instanceof SyntaxError) { + return micro.send(res, 400, err.message) + } else { + console.log('Exception during convert', err) + return micro.send(res, 500, 'An error occurred while converting the diagram') } - return micro.send(res, 500, 'An error occurred while converting the diagram') } } return micro.send(res, 400, 'Body must not be empty.') diff --git a/diagrams.net/src/worker.js b/diagrams.net/src/worker.js index cc10f3d97..4b29bcfb5 100644 --- a/diagrams.net/src/worker.js +++ b/diagrams.net/src/worker.js @@ -2,6 +2,13 @@ const path = require('node:path') const puppeteer = require('puppeteer') +class SyntaxError extends Error { + constructor (err) { + console.log({err}) + super(`Syntax error in graph: ${JSON.stringify(err)}`) + } +} + class Worker { constructor (browserInstance) { this.browserWSEndpoint = browserInstance.wsEndpoint() @@ -19,37 +26,44 @@ class Worker { await page.setViewport({ height: 800, width: 600 }) await page.goto(this.pageUrl) // QUESTION: should we reuse the page for performance reason ? - await page.evaluate((source) => { - /* global render */ - return render({ - xml: source, - format: 'svg' - }) - }, task.source) - await page.waitForSelector('#LoadingComplete', { timeout: this.convertTimeout }) + const evalResult = await Promise.race([ + page.evaluate((source) => { + /* global render */ + try { + const svgRoot = render({ + xml: source, + format: 'svg' + }).getSvg() + const s = new XMLSerializer() + console.log({s}) + return { svg: s.serializeToString(svgRoot), error: null } + } catch (err) { + console.log({err}) + return { svg: null, error: err } + } + }, task.source), + page.waitForTimeout(this.convertTimeout) + ]); + + if (evalResult && evalResult.error) { + throw new SyntaxError(evalResult.error) + } // const bounds = await page.mainFrame().$eval('#LoadingComplete', div => div.getAttribute('bounds')) // const pageId = await page.mainFrame().$eval('#LoadingComplete', div => div.getAttribute('page-id')) // const scale = await page.mainFrame().$eval('#LoadingComplete', div => div.getAttribute('scale')) // const pageCount = parseInt(await page.mainFrame().$eval('#LoadingComplete', div => div.getAttribute('pageCount'))) - // diagrams are directly under #graph, while the SVG generated upon syntax error is wrapped in a div - const svg = await page.$('#graph > svg') if (task.isPng) { - return await svg.screenshot({ + await page.setContent(evalResult.svg) + const container = await page.$('svg') + return await container.screenshot({ type: 'png', omitBackground: true }) } else { - return await page.$eval('#graph', container => { - const xmlSerializer = new XMLSerializer() - const nodes = [] - for (let i = 0; i < container.childNodes.length; i++) { - nodes.push(xmlSerializer.serializeToString(container.childNodes[i])) - } - return nodes.join('') - }) + return evalResult.svg } } finally { try { @@ -67,5 +81,6 @@ class Worker { } module.exports = { - Worker + Worker, + SyntaxError }