Bug in SVG parsing


#1

Hi Jules,

I have a bunch of SVG files that do not render correctly with Drawable::createFromSVG. What they have in common, is that a <use> XML tag (which references a path) includes x and y attributes. Those attributes seem to be ignored, as each path is drawn at the origin rather than (x, y).

This comes up in rendering SVG that represents multiple characters, each represented by its own path. Each character is described in local coordinates, and is meant to be translated to (x, y). I am trying to display math text rendered from LaTeX into SVG format. An example file is given below:

<?xml version='1.0' encoding='ISO-8859-1'?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- This file was generated by dvisvgm 1.2.2 (x86_64-unknown-linux-gnu) -->
<!-- Mon Dec 26 11:49:18 2016 -->
<svg height='11.3819pt' version='1.1' viewBox='0 0 11.7284 11.3819' width='11.7284pt' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'>
<defs>
<path d='M3.612 -1.428C3.552 -1.224 3.552 -1.2 3.384 -0.972C3.12 -0.636 2.592 -0.12 2.028 -0.12C1.536 -0.12 1.26 -0.564 1.26 -1.272C1.26 -1.932 1.632 -3.276 1.86 -3.78C2.268 -4.62 2.832 -5.052 3.3 -5.052C4.092 -5.052 4.248 -4.068 4.248 -3.972C4.248 -3.96 4.212 -3.804 4.2 -3.78L3.612 -1.428ZM4.38 -4.5C4.248 -4.812 3.924 -5.292 3.3 -5.292C1.944 -5.292 0.48 -3.54 0.48 -1.764C0.48 -0.576 1.176 0.12 1.992 0.12C2.652 0.12 3.216 -0.396 3.552 -0.792C3.672 -0.084 4.236 0.12 4.596 0.12S5.244 -0.096 5.46 -0.528C5.652 -0.936 5.82 -1.668 5.82 -1.716C5.82 -1.776 5.772 -1.824 5.7 -1.824C5.592 -1.824 5.58 -1.764 5.532 -1.584C5.352 -0.876 5.124 -0.12 4.632 -0.12C4.284 -0.12 4.26 -0.432 4.26 -0.672C4.26 -0.948 4.296 -1.08 4.404 -1.548C4.488 -1.848 4.548 -2.112 4.644 -2.46C5.088 -4.26 5.196 -4.692 5.196 -4.764C5.196 -4.932 5.064 -5.064 4.884 -5.064C4.5 -5.064 4.404 -4.644 4.38 -4.5Z' id='g0-97'/>
<path d='M2.256 -1.632C2.384 -1.752 2.72 -2.016 2.848 -2.128C3.344 -2.584 3.816 -3.024 3.816 -3.752C3.816 -4.704 3.016 -5.32 2.016 -5.32C1.056 -5.32 0.424 -4.592 0.424 -3.88C0.424 -3.488 0.736 -3.432 0.848 -3.432C1.016 -3.432 1.264 -3.552 1.264 -3.856C1.264 -4.272 0.864 -4.272 0.768 -4.272C1 -4.856 1.536 -5.056 1.928 -5.056C2.672 -5.056 3.056 -4.424 3.056 -3.752C3.056 -2.92 2.472 -2.312 1.528 -1.344L0.52 -0.304C0.424 -0.216 0.424 -0.2 0.424 0H3.584L3.816 -1.432H3.568C3.544 -1.272 3.48 -0.872 3.384 -0.72C3.336 -0.656 2.728 -0.656 2.6 -0.656H1.176L2.256 -1.632Z' id='g1-50'/>
</defs>
<g id='page1' transform='matrix(1.12578 0 0 1.12578 -63.986 -63.1554)'>
<use x='56.6248' xlink:href='#g0-97' y='66'/>
<use x='62.7928' xlink:href='#g1-50' y='61.0453'/>
</g>
</svg>

I took a cursory glance through the SVG parser, and it looks like this behavior is meant to be supported, so it might be a bug.


#2

After some more fiddling, I found that the use tag works as expected when placement is done by supplying an attribute such as transform="translate(32 64)", whereas x="32" y="64" appears to be ignored by the SVG parser in <use> tags.

This could be worked around by pre-processing the XML, inserting transform attributes where needed.

Should the “x-y” attribute method for placement be supported in JUCE? It must be part of the official standard, and all LaTeX-to-SVG compilers I’ve found so far use this.


#3

Yes, sounds like it’s just something we need to add to the ‘use’ element parser. Thanks for the heads-up, we’ll sort it out soon!


#4

Hi Jules,

Here some more issues with the SVG parser! Just what you wanted to work on, I’m sure. I would love to contribute a solution if I was smarter and could comprehend the parser code. Feel free to point me in the right direction if you have a clue what’s causing the issue. Of course I’ll contribute the solution if I somehow find one.

The SVG parser generates artifacts for certain paths, two examples of which I have pasted below. These SVG’s are created by MathJax and render correctly in Chrome and Safari. This issue is unrelated to JUCE’s lack of support for x and y attributes in use tags, which started this thread.

Below is SVG code for a minus sign ‘-’:

<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="1.808ex" height="2.176ex" style="vertical-align: -0.505ex;" viewBox="0 -719.6 778.5 936.9" role="img" focusable="false" xmlns="http://www.w3.org/2000/svg">
<defs>
<path stroke-width="1" id="E1-MJMAIN-2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"></path>
</defs>
<g stroke="currentColor" fill="currentColor" stroke-width="0" transform="matrix(1 0 0 -1 0 0)">
 <use xlink:href="#E1-MJMAIN-2212" x="0" y="0"></use>
</g>
</svg>

Which yields the following:

Another example is an ‘8’:

<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="1.162ex" height="2.176ex" style="vertical-align: -0.338ex;" viewBox="0 -791.3 500.5 936.9" role="img" focusable="false" xmlns="http://www.w3.org/2000/svg">
      <defs>
        <path stroke-width="1" id="E1-MJMAIN-38" d="M70 417T70 494T124 618T248 666Q319 666 374 624T429 515Q429 485 418 459T392 417T361 389T335 371T324 363L338 354Q352 344 366 334T382 323Q457 264 457 174Q457 95 399 37T249 -22Q159 -22 101 29T43 155Q43 263 172 335L154 348Q133 361 127 368Q70 417 70 494ZM286 386L292 390Q298 394 301 396T311 403T323 413T334 425T345 438T355 454T364 471T369 491T371 513Q371 556 342 586T275 624Q268 625 242 625Q201 625 165 599T128 534Q128 511 141 492T167 463T217 431Q224 426 228 424L286 386ZM250 21Q308 21 350 55T392 137Q392 154 387 169T375 194T353 216T330 234T301 253T274 270Q260 279 244 289T218 306L210 311Q204 311 181 294T133 239T107 157Q107 98 150 60T250 21Z"></path>
      </defs>
      <g stroke="currentColor" fill="currentColor" stroke-width="0" transform="matrix(1 0 0 -1 0 0)">
        <use xlink:href="#E1-MJMAIN-38" x="0" y="0"></use>
      </g>
    </svg>


#5

Thanks. I started fixing the other one but it’s actually a total nightmare to implement correctly and I didn’t have time to finish. Will look at this one too when I get back to it!


#6

Hi Jules, quick update on this issue:

The failure occurs when a T command follows an M command. The SVG specs read:

If there is no previous command or if the previous command was not a Q, q, T or t, assume the control point is coincident with the current point. https://www.w3.org/TR/SVG/paths.html#PathDataQuadraticBezierCommands

For example, the minus sign I posted earlier reads like this:

	<path id="theMinusSignProducedByMathJax"
		d="
		M 84 237
		T 84 250
		T 98 270
		H 679
		Q 694 262 694 250
		T 679 230
		H 98
		Q 84 237 84	250
		Z">
	</path>

This path contains a T immediately following an M. From the specs, this should be equivalent to the following:

	<path id="theMinusSignWhichRendersCorrectlyInJUCE"
		d="
		M 84 237
		Q 84 237 84 250
		T 98 270
		H 679
		Q 694 262 694 250
		T 679 230
		H 98
		Q 84 237 84	250
		Z">
	</path>

That is, since the T does not follow any of Q, q, T, or t, the control point for the T should be the current point, which is the argument to the M. JUCE incorrectly assumes the origin for the control point there.

I hope this is enough info for you to fix the bug in a flash. If not, I’ll dig into the parser and try it myself.

Best,
Jon


#7

Thanks - I’ve fixed the ‘T’ thing now, but haven’t had chance to look at the ‘use’ one yet, that’s a bit more complicated.


#8

Thanks, Jules! I took a look and it seems the two SVG’s from the earlier posts are still not rendered equivalently, although it looks better. It behaves as if maybe drawing a line from the arg of M to the arg of T, rather than inserting the equivalent of a Q, whose control point is the arg of M. I’ll see what I can do.

Correct result:

Current:


#9

Hmm, I followed the SVG spec, and the example SVG I was looking at produced the same result in my renderer as the OSX preview did… If you want to suggest a tweak to the parser, please do!


#10

Yes, I think the issue is rather specific to the way MathJax issues its SVG, so probably not relevant to many users. I’ll send you the diff when I find a solution.


#11

For the use with x and y fields, I believe you can achieve what you need easily by first applying this patch https://github.com/soundradix/JUCE/commit/027c5ae0cd7e22dbcc2adeed8eacb198371a7292 which I made in SR’s branch to add transform field support to use clauses (which we happened to have in the SVGs made by our designer), and in parsePathElementWithTransform add code to check for the x and y fields. Though in the presence of both x/y + transform one should find out which of them should be applied first (i.e. scaling after x/y offset is different than scaling and then applying x/y offset)…