-
-
Notifications
You must be signed in to change notification settings - Fork 224
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
resolves #507 add png support for Mermaid #951
Conversation
mermaid/src/index.js
Outdated
} catch (e) { | ||
console.log('e', e) | ||
return micro.send(res, 400, 'Unable to convert the diagram') | ||
const outputType = req.url.match(/\/((?:png)|(?:svg))/)?.[1]; |
There was a problem hiding this comment.
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:
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.
There was a problem hiding this comment.
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 ?
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left a few nitpicks but overall great work 👌🏻
I would like to add a test on this feature. You can take a look at https://github.com/yuzutech/kroki/blob/main/mermaid/tests/test.js for inspiration.
Comparing PNG images can be tricky but we can use https://github.com/mapbox/pixelmatch to do pixel-level image comparison.
Thanks!
- fix lint errors, - apply PR comment propositions, - add return statements on `micro.send` (headers sent error), - add PNG tests using pixelmatch.
@Mogztter thank you for the review ! I applied the proposed changes, fixed the linter issues and added the missing I also included two tests about PNG generation using pixelmatch as you proposed. I just had a doubt on where to put the reference images. They are currently under |
👍🏻
That's a good idea. |
Tests are currently failing because the width and height of the PNG images are slightly different. My guess is that rasterization is slightly different (most likely on fonts) and you can get a few pixels difference.
So we should also use a threshold on the width and height. Something like, more or less 5% of the expected width/height: // threshold, more or less 5% of the expected widht/height
expect(width).to.be.within(expectedImage.width / 1.05, expectedImage.width * 1.05);
expect(height).to.be.within(expectedImage.height / 1.05, expectedImage.height * 1.05); |
Wow, it passed locally and I didn't think of this. Just to ensure I understood, you mean that depending on the os, puppeteer will generate slightly different images, due mostly to the font rendering? If this is the case, and if the sizes differ, I fear pixelmatch will fail as well... And I am not sure how to proceed with testing then (appart checking that the png is valid and at least of a given size, as mermaid.ink does) |
Yes, that's correct.
const numOfDifferentPixels = pixelmatch(resultImage.data, expectedImage.data, diffImage.data, width, height, {threshold: 0.1}) My only concern is that |
Yes I am. I thought also of simply making the tests run on docker. However, to me having a test that fails locally but succeeds on docker build is more problematic than not. I imagine the next developer cloning the repo, and spending 3h understanding why the tests suddenly fail, because he didn't think of trying to run it inside docker. But anyways, as you said, first things first: let's see if pixelmatch can handle different sizes (will work on it after work). Will keep you posted! |
@Mogztter Ok, so I investigated a bit. The SVGs in docker have a more padding below the text, making all shapes grow a bit in height. This explains the extra pixels and is not due to any change in CSS: comparing the two SVGs on the same browser, I can see the computed size of the shapes, hard-coded, are different, so it is during mermaid rendering. I thought it could come from the Chromium version, which is different between docker and my Mac. I tried using the latest alpine image,
Also, what I propose: since the png feature is only about taking a screenshot from the svg, we could argue that if the svg is rendered correctly, the png should as well. In PNG tests, we should thus only ensure that 1) the worker doesn't throw an error, and 2) the returned PNG is valid: pngTests.forEach((testCase) => {
it(`should return a valid PNG from content: ${testCase.content}`, async function () {
const browser = await puppeteer.launch({args: ['--no-sandbox', '--disable-setuid-sandbox']})
try {
const worker = new Worker(browser)
const result = await worker.convert(new Task(testCase.content, true));
const image = PNG.sync.read(result); // this will fail on invalid image
const {width, height} = image;
// ensure we have some content in the PNG
expect(image.data.length).to.be.greaterThan(100);
} finally {
await browser.close()
}
})
}) Also, I was looking at the SVG generation, and since we are taking the content of const svg = await page.$('#container > svg')
if(svg == null) {
throw Error("Invalid syntax in graph (no svg found)")
} Finally, I am wondering if I should update the alpine image since we are at it, or do another PR for that... What do you think of all this ? |
Wow, thanks for taking the time to look at it closely, great work!
I think that's perfectly fine 👍🏻
As far as I understand, Mermaid will still generate an SVG image, for instance: https://kroki.io/mermaid/svg/eNpLL0osyFAIceGK5QIAFwMDPA== Do you have an example where Mermaid does not generate an SVG?
In any case, I think you're right, it would be better to check if it has an SVG child 👍🏻
We are using Dependabot to update Alpine images (and more broadly dependencies), see: #882 I had a few issues with Alpine 3.14.0, that's why the pull request is still opened. Alpine 3.14.2 is now out but I want to test it thoroughly to make sure that everything is now running fine. I hope I answered your questions, if not, feel free to ping me 😄 |
Ok, so I can actually make the code return the error SVG as image, but since this error as SVG doesn't provide any information (except that a problem has occurred), to me it would make sense to instead return This is actually what most other endpoints are doing: PlantUML, BlockDiag, PacketDiag, etc. |
- make endpoints return a 400 error code on invalid syntax - make endpoints return a 500 error code on other errors - change PNG test: only check loosely the size of the PNG - add test about syntax error
Yes, that would be more consistent 👍🏻
So, we won't lose information. Sorry for the late reply, I thought I had replied right when I received the notification (but apparently not). Thanks again for your excellent suggestions 🥇 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perfect, thanks again 👍🏻
Add the
/png
endpoint support for mermaid diagrams.The resulting png has transparent background, and has no margin (will really only contain the actual rendered diagram).
See #507