Attached to this report are two matched sets of files. One set used versions 2.38, the other used version 2.44. For each version, the files consist of 1) the input dot file, 2) the output SVG file, 3) a screenshot of SVG file displayed in my Polymeter software (via the IWebBrowser interface which wraps Internet Explorer) and 4) a screenshot of the same SVG file displayed in Firefox. The 2.38 graph fits neatly within the specified window size, but the 2.44 graph is slightly too wide and is consequently partially clipped. This misbehavior is observed in both IWebBrowser and Firefox.
Expected Behaviour
I expect the graph to respect the specified size. This was so in version 2.38, and is not so in 2.44. Comparing the two SVG files shows that they are significantly different. Based on this comparison, I surmise that the SVG layout engine has changed and that the change is causing the incorrect sizing.
Actual Behaviour
The input file, correct and incorrect SVG files, and screenshots are attached to this report.
OS Version
Windows 7 64-bit.
Graphviz Version
I'm reporting this issue for version 2.44.1. I don't know exactly which version introduced the issue. All I can say for sure is that version 2.38 does NOT exhibit the issue. I can also confirm that the issue occurs with both the 32-bit and 64-bit versions of 2.44.1.
The command line was:
dot -Ksvg -o"xxx" "yyy"
where xxx is the output SVG file path and yyy is the input dot file path. All other options are specified in the dot file.
This issue is still present using GraphViz 2.47.2 (64-bit version) on Windows 10 Pro x64. I attach the Graphviz input file and the resulting SVG file. The graphic is visibly clipped on the right edge. The issue doesn't always occur, but it seems to occur more probably when the graph contains many nodes. It occurs regardless of how the SVG file is displayed.
This a serious defect and frankly I'm dismayed that there has been no progress on this issue since I first reported it seven months ago. It appears that relatively esoteric problems are getting priority and I find this hard to understand since a sizing error will affect all users of SVG format, which is the only widely supported and non-proprietary vector output format Graphviz supports.plm9DC1.gv
Hi. It’s not a common problem; it has something to do with your settings of graph size and aspect ratio, some characteristic of the native SVG that we generate, and probably graphviz fontconfig is loading a different font than the final renderer. Strange! For example if I change the fontnames to Palatino, the bug goes away (there’s probably only one distinct Palatino font on this computer.) If I try Courier, the bug is maybe even a little worse, which is unexpected. I’m a little surprised the mismatch is so much (I thought the native SVG renderer emits code for every node independently of the sizes of other nodes, so you can’t get the kind of cumulative font size errors that seem to be cropping up, maybe just a few percent difference). It could be possible to improve the accuracy of our native SVG generator a little though ultimately if we do not embed our fonts then we are depending on the kindness of strangers to ensure the final renderer does exactly what the graphviz text layout algorithm thinks it does. We have known this all along.
To try to fix this someone would have to do the hard work of staring at the coordinates in the generated SVG and compare with the unscaled internal layout to start inferring what pathological behavior is responsible and how to engineer around it.
Meanwhile the solution is to use -Tsvg:cairo or -Tsvg:quartz instead of -Tsvg i.e. -Tsvg:svg:core as they embed the fonts.
If this were a commercial product, we might abandon -Tsvg:svg:core since it will always have at least minor issues like this given its oversimplification.
Bear in mind “esoteric” may be a little in the eye of the beholder.
Hi! Thank you very much for replying, and I apologize if my language was too strident. I would think that it might be worth investigating what if anything changed in the SVG rendering component between versions 2.38 and 2.44. My examination showed significant differences in the SVG output files between those two versions, for the exact same input. Meanwhile I will try specifying cairo and/or quartz and see if the issue goes away. Is there any downside to doing so? For example would it make the resulting SVG file much bigger, or less likely to be compatible with various browsers? CK
I can confirm that the suggested workaround of specifying -Tsvg:cairo does indeed eliminate the sizing error, however it also makes the SVG file approximately five times larger, because all graph elements--including fonts--are broken down into low-level primitives, i.e. lines and splines. This somewhat defeats the intent of a high-level graphical object specification. I would also expect render time to increase, because any optimizations for higher-level primitives--e.g. ellipses and text--are lost. Is that a fair assumption? I don't see an obvious render slowdown, but it's non-trivial to measure since rendering occurs outside of my code.
I concur that changing the font does vary the magnitude of the error, however unlike Stephen North, I wasn't able to make the error go away completely by using Palantino. Arial is worse than Helvetica, and this seems remarkable to me since Arial is a native Windows font. If Windows couldn't compute the size of text in Arial correctly, many systems besides Graphviz would be failing.
I found an explanation of the underlying problem: Graphviz and fonts. It seems fair to say that cairo and Windows are computing the size of text slightly differently. This difference is relatively recent--it appeared only seven months ago--and it is insensitive to environmental factors such as choice of Windows version or browser. Switching from Windows 7 to Windows 10, or from Firefox to IE or Edge has no effect on the issue, but rolling back to Graphviz version 2.38 completely solves it. No doubt it's a complex situation, but the above factors suggest that some traceable change within Graphviz (or possibly within cairo) is the cause.
I think this is mostly true but hardly anyone cares about file size or rendering time. I think SVG in the DOM runs out of steam at 50-100K objects but possibly the text in Cairo SVG is not rendered into shapes but into pixels? Anyway they have to do it that way, there’s no other way to guarantee what fonts are available downstream.
I vaguely remember writing that doc about fonts in Graphviz or most of it. Who else on the planet even cares about details like an API to report the quality of fontconfig lookup? No one.
The graphviz driver code that deals with: scaling, rotating for landscape mode, pagination, margins and viewports, is just awful. Did I mention layers? There isn’t much abstraction, just a lot of features with weird interactions. Ugh.
So specifically what broke in the generated SVG between 2.38 and today? The problem could be an underlying library, maybe not our code.
When I compare the two SVG files in graphviz_SVG_size_bug.zip (included in the original bug report above), one thing that immediately jumps out at me is that all the differences are x-coordinates; the y-coordinates are unchanged. This is at least consistent with the clipping only occuring on the right edge of the graph. It suggests that issue could possibly be narrowed down to a change in how text width is calculated. Perhaps kerning could be a factor?
I'm reluctant to risk bloating the SVG files by so much. Each text character generates a mini-forest of lines and splines. It's a lot more objects, plausibly enough to choke the downstream renderer. I'm really not sure what to do. I could play it safe and expose a Cairo vs. native renderer selection in my application. I could scale down the graph width by some fudge factor. Ugh is right.
This a serious defect and frankly I'm dismayed that there has been no progress on this issue since I first reported it seven months ago. It appears that relatively esoteric problems are getting priority and I find this hard to understand since a sizing error will affect all users of SVG format
If you want a partial explanation, most of the maintainers have no access to Windows, so Windows-only issues are difficult to troubleshoot. Compounding this, at the time this issue was created Windows 7 was already EOLed.
If this problem is reproducible on Linux, I can bisect and debug this. Though obviously berating a group of volunteers providing you with free software doesn't exactly help your case.
Matthew, thank you for your reply. Not sure being "dismayed" rises to level of "berating" but I did apologize for my overly strident language above. Again I'm sorry if it rubs the wrong way. My intention was only to draw attention to this issue, which I felt was underrated. FWIW I am a huge fan of Graphviz and have relied on it for many years. I maintain various gnarly open source projects myself. Providing people with good free stuff, well, it's a calling. I totally get that Windows is a PITA but I'm too old to learn Mac/Linux. I was hoping the issue would quietly go away with Windows 10, but alas not.
Attached is a version of the graph that has no labels, but still manifests the bug. I changed my code that generates the .gv file to set all labels to the empty string. Could the issue be less related to fonts than previously supposed? I have also found that the bug's appearance is sensitive to the aspect ratio of the specified output size. I would think this points more towards some change in how fillratio is handled.Graphviz_sizing_bug_empty_labels.gv
Here's a simpler test that demonstrates the issue. Notice that all the labels are null. I can make the bug go away by playing with the graph size in the .gv file. Again, doesn't this point to fillratio handling rather than font discrepancies?
Here's a simpler test that demonstrates the issue reliably. Notice that all the labels are null. I can make the bug go away by playing with the graph size in the .gv file. Again, doesn't this point to fillratio handling rather than font discrepancies?
Another work-around is to embed the entire contents of the graph in a cluster with peripheries=0. Based on a different graph, this seems to keep the entire contents visible.
Thank you for your reply. I tried your proposed cluster work-around on my original test case, but the resulting graph is still clipped. I enclose my input and output files below.
While I was hopeful that a cluster would solve all problems, I'm not amazed that it did not. Sorry.
But adding this attribute graph [margin=20] // points (10 fails)
to the cluster seems to succeed as a work-around for your graph.
Note: you still need the cluster.
No clue why a margin of 20 points works but a margin of 10 does not.
[the plot thickens, and I was already confused]
ratio=fill is not relevant. it appears there is some problem with the emitted “width” in the
or its relationship to the context of the entire graph.
dot -Tsvg:cairo does not have this problem. So the problem is definitely in plugin/core/gvrender_core_svg.c
It’s not clear what’s wrong, since the code is just
gvprintf(job, "<svg width="%dpt" height="%dpt"\n", job->width, job->height);
so there is no other arithmetic going on. Maybe the subsequent for the entire graph is wrong, but why?
It’s just copying the job->scale, rotation, translation into the output SVG. Clipping happens even if X translation is 0.
While I was hopeful that a cluster would solve all problems, I'm not amazed that it did not. Sorry.
But adding this attribute graph [margin=20] // points (10 fails) to the cluster seems to succeed as a work-around for your graph.
Note: you still need the cluster.
No clue why a margin of 20 points works but a margin of 10 does not.
[the plot thickens, and I was already confused]
Also, a useful exercise would be to compare the output of dot -Tps with dot -Tsvg since they are should be very close.
They are readable -- this is output that we generate by printing a bunch of strings, that’s all, and the graphics models are very close.
ratio=fill is not relevant. it appears there is some problem with the emitted “width” in the
or its relationship to the context of the entire graph.
dot -Tsvg:cairo does not have this problem. So the problem is definitely in plugin/core/gvrender_core_svg.c
It’s not clear what’s wrong, since the code is just
gvprintf(job, "<svg width="%dpt" height="%dpt"\n", job->width, job->height);
so there is no other arithmetic going on. Maybe the subsequent for the entire graph is wrong, but why?
It’s just copying the job->scale, rotation, translation into the output SVG. Clipping happens even if X translation is 0.
While I was hopeful that a cluster would solve all problems, I'm not amazed that it did not. Sorry.
But adding this attribute graph [margin=20] // points (10 fails) to the cluster seems to succeed as a work-around for your graph.
Note: you still need the cluster.
No clue why a margin of 20 points works but a margin of 10 does not.
[the plot thickens, and I was already confused]
Following your advice, I generated the postscript file and enclose it below. Assuming x increases from left to right as usual, the rightmost ellipse (the one that gets clipped in this case) has the following coordinates in SVG:
center x, y = 2187, -1124
radius x, y = 27, 18
The ellipse has the exact same coordinates in PS except that the center y is positive, presumably due to an axis reversal. The ellipse is defined at line 350 in the SVG and line 980 in the PS.
Applying the scaling of 0.33, the ellipse x center becomes 721.71, barely within the view x limit of 722. Assuming scaling also applies to the radii, the x radius is 8.91, making the ellipse's rightmost point 730.61 which exceeds the viewbox x limit of 722. So there you have it: the ellipse is in fact clipped in the SVG file, and by roughly the right amount (half the ellipse is missing).
Since the ellipse has the same coordinates in PS and SVG, it seems reasonable to suppose that the problem is with the page setup. It's possible that the Postscript file also clips. I tried it several online Postscript viewers, and in all of them, the file was indeed clipped on the right, but by a considerably larger amount than the SVG is. Perhaps we have two bugs rather than one?
The page setup does differ, for example the translation might be an issue. It appears that SVG translates X by 4, whereas PS translates it by 114.741, and I'm not sure why that would be. Also SVG defines the viewbox from 0, whereas PS defines it from a margin of 36 in both axes.
I note with great interest that adjusting the SVG file's scaling from 0.33 to 0.325 makes the problem go away. The scaling is given as 0.325082 in the PS file. Hypothesis: the SVG export is rounding to two significant digits, and the resulting loss of precision is the root cause of the bug.
And sure enough, the scale is output via the function gvprintdouble in gvdevice.c, which rounds the output to two decimal places, apparently insufficient in this case. Line 567:
snprintf(buf, 50, "%.02f", num);
It might be helpful to make a version of dot.exe that uses more (six?) decimal places for the scale and see how it does on my other test cases.
[The function call is gvprintdouble(job, job->scale.x); in svg_begin_page at line 256 of gvdevice.c]
The issue I'm pointing to is the use of the format string %.02f for all double values. I assume double values are rounded to two decimal places in order to keep the size of SVG files reasonable. Two decimal places might be sufficient precision for object coordinates, which are relatively large values, but for scaling, which ranges from zero to one, it's apparently not precise enough.
The least invasive solution would be to add a second print function for doubles, something like vgprintdoubleprecise, that uses %f instead of %.02f, and use that function for outputting the SVG's scale, but leave everything else alone.
A more drastic solution would be to use %g instead of %.02f. The %g format would allow up to six digits of precision by default, but for cases where the value requires less digits, it would use less. One downside is that %g can use exponential format. If I'm reading the SVG specification right, exponential format is supported, but maybe it could break some parsers. https://www.w3.org/TR/css3-values/#numbers
The issue I'm pointing to is the use of the format string %.02f for all double values. I assume double values are rounded to two decimal places in order to keep the size of SVG files reasonable. Two decimal places might be sufficient precision for object coordinates, which are relatively large values, but for scaling, which ranges from zero to one, it's apparently not precise enough.
If I'm reading the SVG specification right, the format supports exponential format. So why not use %g instead of %.02f? This format would allow six digits of precision by default, but for cases where the number requires less digits, it would use less.
Nice work debugging, @victimofleisure! Chopping values to 2 decimal places indeed seems a little unmotivated. The logic in gvprintdouble seems targeted at producing human readable output; otherwise, who cares about trimming trailing zeros? A backend like SVG that's producing a machine-readable format should probably just emit the full precision it has available; i.e. not call `gvprintdouble at all.
As you say, prettiness is irrelevant in a back-end, so the only plausible argument I can think of for using two decimal places is file size; but since SVG is quite verbose, and numeric values are only a small portion of its syntax, I doubt using %g in all cases would visibly affect parsing performance.
Rounding to two decimals has a disproportionately corrupting effect on values with small ranges, such as normalized values or angles in radians. We've already bumped into one instance of this, but there could be others lurking.
Using literally the same input file (network drive), my Linux version of dot ( 2.49.4~dev.20211023.0130) produces correct SVG while my Windows version (2.49.2) produces clipped/truncated SVG.
However, when I created a modified version of the input file removing all the node labels, the Windows version of dot created correct SVG (effectively identical to the Linux version - only the "Generated by" comments differed)