-
-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
desktop: Fixes #7521 Svg images are correctly sized when exported as png #7546
Conversation
CLA Assistant Lite bot All contributors have signed the CLA ✍️ ✅ |
I have read the CLA Document and I hereby sign the CLA |
edit: was some hoisting err perhaps, solved by removing and reinstalling node_modules |
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.
Thank you for working on this! I've left a few comments.
|
||
describe('contextMenuUtils',()=>{ | ||
test('should return the correct dimensions for svg with space seperated viewBox values',()=>{ | ||
const svg = '<svg viewBox="0 0 45.9375 60" "></svg> '; |
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.
const svg = '<svg viewBox="0 0 45.9375 60" "></svg> '; | |
const svg = '<svg viewBox="0 0 45.9375 60"></svg> '; |
Consider removing the extra "
.
expect(width).toBe(undefined); | ||
expect(height).toBe(undefined); | ||
}); | ||
|
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.
Please also consider the following cases:
viewBox
has the formviewBox='1,2,3,4'
orviewBox = "1,2,3,4"
instead ofviewBox="1,2,3,4"
viewBox
has the form
viewBox='
1,
2,
3 ,
4 ,
'
@@ -106,8 +106,12 @@ export function menuItems(dispatch: Function): ContextMenuItems { | |||
if (!options.filename) { | |||
throw new Error('Filename is needed to save as png'); | |||
} | |||
const [width,height] = svgDimensions(options.textToCopy); | |||
if (!Number.isInteger(width) || !Number.isInteger(height)) { | |||
bridge().showErrorMessageBox(_('failed to get width and height of image')); |
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 think, with this change, <svg xmlns="http:https://www.w3.org/2000/svg"><rect x='5' y='5' width='50' height='50' fill='red' stroke='black' style='stroke-width: 5;'/></svg>
would show an error message when it wouldn't before, because of the missing viewBox
attribute.
The viewport is the dimension in user space - doesn't it mean that if the window is small, the exported PNG will be small too? And we wouldn't want that, instead it should be dimensions that make sense (although I don't know what that is), independent of the viewport. Why is the PNG small to begin with? Isn't it one of those things where we can just double the exported dimensions and it will work? |
cause we are making an
both have same height of 150px (default height of replaced elements) |
So what would be the solution then? |
It might make sense to have a minimum dimension when exporting SVGs. We could then increase the image's width and height if necessary. |
it's viewBox actually 🥲. |
my bad 😅 ,
yeah that would be nice |
so what is the final implementation ? |
I don't think a hard coded minimum dimension would work, because 400 is way too big for certain tiny diagrams, or way too small for massive ones. Basically there can be all sizes of diagrams so the dimensions we choose need to be based on the diagram. @adarsh-sgh, did you check the Mermaid API - do they provide some info about the diagram such as recommended width or similar? |
could not find anything like that there, but found about their live editor which also has |
Sounds reasonable. How about using this technique but double the dimensions (just to be sure it's high res enough for printing)? |
Dimensions of rendered Svg will depend on window size, That's something we want to avoid I guess ?
|
Yes, allowing user's to override auto detected dimensions would be a very helpful feature IMO not sure about other point. |
But what's the alternative, do you have any suggestion? We don't want to ask the user for dimensions, just double it and that should work in most cases. |
using viewBox attribute's height and width values is a good plan IMO.
|
But since those are user space dimensions, isn't it the same as using the window dimensions? Also what is setting the viewbox? Is that the mermaid lib? And how do they determine the dimensions then? |
Yes.
They
sorry I could not get which window we are talking about. |
But the part I don't get here is that it's vector graphics, so the vector dimensions are irrelevant and can't be of any help to export to png. And the coordinates are floating values, so for example you could fit an infinity of vector graphics in a 1.0 x 1.0 square. However if you export that to a 1x1 pixels image, that's not going to make any sense. So how is it done normally, are there ways to get reasonable dimensions from some API or calculations? Does the DOM SVG API provides this maybe? |
Could not find anything useful in DOM svg APIs. When we render a mermaid diagram using mermaid js in browser, then the size in which browser renders it, can we say that that size is a reasonable dimension ?
yes that is a problem, by using viewBox as exported dimensions we are making this assumption that viewBox size is also the recommended size of svg, which seems true in case of mermaid exported svgs since they render with same size as defined in viewBox not scaled up or down. |
Yes that's what I meant by window dimensions. So it seems we always go back to this, so let's do this. You don't need the XML regex parsing as that's too unreliable, I think you can get the width and height directly from the DOM. Then double those dimensions to make sure it's always big enough even on hdpi screens. In fact, doesn't it mean that we keep the current code as it is but add |
Yes already removed that and was using dom parser to get those values, didn't pushed since I was not sure if we will choose this way or not.
Okay, on it.
No, That will be different, all images will have dimension 600 * 300. 2nd part of comment linked below explains why #7546 (comment) |
I don't know about the technical details, but the dimensions of all the exported diagrams should not be 600x300 - at a minimum the aspect ratio should match the diagram aspect ratio, which I assume we can get accurately. And the size should be based on the note viewer size. |
Sure, That's how it will be. 🙌🏼 we will
|
But why do you need to parse the SVG? In svgUriToPng there's full access to the DOM so you shouldn't need to do any XML regex parsing. |
we have access to the document but we need to select the svg element to get it's rendered size, I was thinking of getting the ID of svg using the svg string we are given, then select svg element from dom using querySelector
instead of regex, DOMParser.parseFromString() can be used, it wouldn't have those drawbacks I think. edit: I have pushed the proposed changes for discussion. will add tests if this approach is fine. |
const id = parser.parseFromString(svg, 'text/html').querySelector('svg').id; | ||
({ width , height } = document.querySelector<HTMLIFrameElement>('.noteTextViewer').contentWindow.document.querySelector(`#${id}`).getBoundingClientRect()); | ||
} catch { | ||
// do nothing |
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.
We don't hide errors, so at least logger.warn(...)
. Please search how loggers are created
@@ -106,8 +106,10 @@ export function menuItems(dispatch: Function): ContextMenuItems { | |||
if (!options.filename) { | |||
throw new Error('Filename is needed to save as png'); | |||
} | |||
// double dimensions to make sure it's always big enough even on hdpi screens | |||
const [width,height] = svgDimensions(document,options.textToCopy).map((x: number) => x * 2); |
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.
The dimensions can be undefined, and you'll end up with NaN as width and height.
Also when it's undefined, what dimensions do we end up using?
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.
when it's undefined, what dimensions do we end up using?
The dimensions that used to be before this fix.
anything better we can choose as default ? 🤔
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.
How about the NaN dimensions, what do you plan to do about it?
The dimensions that used to be before this fix.
Which are?
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.
How about the NaN dimensions, what do you plan to do about it?
assigned dimensions as undefined if it's NaN
Which are?
almost of zero use, but since we have no better fallback we have to use them if we can't find any sensible default value.
Dimensions were being calculated as follows,:
we are making an element for the svg and using it's resolution to set exported image's resolution
img is a replaced element and it's default size is 300 * 150 a/c to google search.
different sized mermaids i exported to check size were having height 150px each. and different heights all less than 300.
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.
Ok thanks for clarifying. The default dimensions indeed aren't useful so let's make them 1024. Is it possible at least to respect the diagram aspect ratio? Otherwise make it 1024x1024
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.
let's make them 1024
when we have the aspect ratio, should the bigger/smaller dimension be 1024 or make width = 1024 ?
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.
The max dimension would be 1024. For example 600x1024, or 1024x800
Please run |
I am facing a problem while trying to write jest tests , jsDom does not render elements so getBoundingClientRect() always returns all dimensions as 0. |
Ok let's drop the testing. Thanks for implementing this |
Mermaid images when exported as png were having very low resolution.
Before
using rendered image's dimensions as exported image dimension
![image](https://user-images.githubusercontent.com/63918341/209781831-21f9e334-92f3-4cb0-8bfc-57378a465989.png)
After
using SVG's viewBox attribute as exported image's dimensions
![image](https://user-images.githubusercontent.com/63918341/209781978-08e7f944-ee43-4e17-9aae-fd80763db5c1.png)
Fixes #7521