Skip to content

resolves #507 add png support for Mermaid #951

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Oct 27, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 15 additions & 10 deletions mermaid/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,23 @@ const micro = require('micro')
const server = micro(async (req, res) => {
// TODO: add a /_status route (return mermaid version)
// TODO: read the diagram source as plain text
const diagramSource = await micro.text(req, { limit: '1mb', encoding: 'utf8' })
if (diagramSource) {
try {
const svg = await worker.convert(new Task(diagramSource))
res.setHeader('Content-Type', 'image/svg+xml')
return micro.send(res, 200, svg)
} catch (e) {
console.log('e', e)
return micro.send(res, 400, 'Unable to convert the diagram')
const outputType = req.url.match(/\/((?:png)|(?:svg))/)?.[1];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it can be simplified to:

Suggested change
const outputType = req.url.match(/\/((?:png)|(?:svg))/)?.[1];
const outputType = req.url.match(/\/(png|svg)/)?.[1];

If needed we could check that /svg or /png is the last segment using $ at the end of the regular expression.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well, just adding $ at the end would still leave /svg/png to pass (and generate a png), so to me either we do complete validation (total match, ^ and $) or none. The alternative would the be:

const outputType = req.url.match(/^\/(png|svg)$/)?.[1];

Should I make the change ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well, just adding $ at the end would still leave /svg/png to pass (and generate a png), so to me either we do complete validation (total match, ^ and $) or none.

That's a good point.
Yes, I think we should use a strict/complete validation, /svg/png is unclear and we should definitely return an error (not try to proceed).

if (outputType) {
const diagramSource = await micro.text(req, { limit: '1mb', encoding: 'utf8' })
if (diagramSource) {
try {
const isPng = outputType == 'png'
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) {
console.log('exception during convert', e)
return micro.send(res, 400, 'Unable to convert the diagram')
}
}
micro.send(res, 400, 'Body must not be empty.')
}
micro.send(res, 400, 'Body must not be empty.')
micro.send(res, 400, 'Available endpoints are /svg and /png.')
})
server.listen(8002)
})().catch(error => {
Expand Down
3 changes: 2 additions & 1 deletion mermaid/src/task.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
class Task {
constructor (source) {
constructor (source, isPng = false) {
this.source = source
this.isPng = isPng
this.mermaidConfig = {
theme: 'default',
class: {
Expand Down
27 changes: 18 additions & 9 deletions mermaid/src/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class Worker {
this.pageUrl = process.env.KROKI_MERMAID_PAGE_URL || `file://${path.join(__dirname, '..', 'assets', 'index.html')}`
}

async convert (task) {
async convert(task) {
const browser = await puppeteer.connect({
browserWSEndpoint: this.browserWSEndpoint,
ignoreHTTPSErrors: true
Expand All @@ -23,14 +23,23 @@ class Worker {
window.mermaid.initialize(mermaidConfig)
window.mermaid.init(undefined, container)
}, task.source, task.mermaidConfig)
return await page.$eval('#container', 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('')
})

if (task.isPng) {
const svg = await page.$('#container > svg')
return await svg.screenshot({
type: 'png',
omitBackground: true,
})
} else {
return await page.$eval('#container', 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('')
})
}
} catch (e) {
console.error('Unable to convert the diagram', e)
throw e
Expand Down
5 changes: 4 additions & 1 deletion server/src/main/java/io/kroki/server/service/Mermaid.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@
import io.vertx.ext.web.client.HttpResponse;
import io.vertx.ext.web.client.WebClient;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class Mermaid implements DiagramService {

private static final List<FileFormat> SUPPORTED_FORMATS = Arrays.asList(FileFormat.PNG, FileFormat.SVG);

private final WebClient client;
private final String host;
private final int port;
Expand All @@ -37,7 +40,7 @@ public String decode(String encoded) throws DecodeException {

@Override
public List<FileFormat> getSupportedFormats() {
return Collections.singletonList(FileFormat.SVG);
return SUPPORTED_FORMATS;
}

@Override
Expand Down