yWorks / svg2pdf.js

A javascript-only SVG to PDF conversion utility that runs in the browser. Brought to you by yWorks - the diagramming experts
MIT License
654 stars 100 forks source link

Advanced Paths Do Not Show Up Correctly #135

Closed Hiovita closed 3 years ago

Hiovita commented 4 years ago

When using simple paths (specifically with only one closing Z instruction), the full path is rendered in the final pdf. However, when more complicated paths with multiple Z closing instructions are present in the svg (i.e. subpaths), only the last closed piece of the path is rendered. The SVG specification allows for multiple subpaths within a single path.

Steps to reproduce the behavior:

  1. Open inkscape and type some text. Then select the text and choose path-> object to path. You will notice that a group of several paths is created. Ungroup them and export as plain svg. Paste the result into your HTML document. Use the example svg2pdf code and notice how the resulting pdf shows the text correctly.
  2. Now go back to inkscape and select all the text charters and choose path->combine. Export again. Now you will see paths in the form d="M x1 y1 L x2 y2 ... Z M x3 y3 L x4 y4 ... Z". Notice the multiple closing Z instructions. The vector renders correctly in inkscape and on the web, but now converting it to pdf ONLY RENDERS THE LAST LETTER (or character like a period). The last letter's position seems to be correct.

Expected behavior According to SVG 1.1: 'The "closepath" (Z or z) ends the current subpath and causes an automatic straight line to be drawn from the current point to the initial point of the current subpath. If a "closepath" is followed immediately by a "moveto", then the "moveto" identifies the start point of the next subpath.'

More info: Svg Specifications

For now I will have to crunch my brain around getting more custom Fonts to work with jsPDF and use text objects until this is fixed. Because of the way I generate my SVGs, multiple paths would be a mess, especially as I would have several thousand of them if it is for every letter. I specifically used text-to-path to save myself from font madness, as there are some pages where a few lines of text are in a crazy font that I either don't have the licence to embed (but have the license to use), or the font is so big that it doubles the pdf size for one line of text.

yGuy commented 4 years ago

Careful: If we first normalize the paths (so that there are no more relative path instructions, anymore), splitting path elements looks like a trivial (just regex match the 'Z's and split the elements into many) fix, but it isn't: Shapes often consist of multiple paths and even-odd filling logic cannot produce holes in shapes, anymore.

yGuy commented 4 years ago

Possible test-cases on MDN:

<svg viewBox="-10 -10 320 120" xmlns="http://www.w3.org/2000/svg">
  <!-- Effect of nonzero fill rule on crossing path segments -->
  <polygon fill-rule="nonzero" stroke="red"
           points="50,0 21,90 98,35 2,35 79,90"/>

  <!--
  Effect of nonzero fill rule on a shape inside a shape
  with the path segment moving in the same direction
  (both squares drawn clockwise, to the "right")
  -->
  <path fill-rule="nonzero" stroke="red"
        d="M110,0  h90 v90 h-90 z
           M130,20 h50 v50 h-50 z"/>

    <!--
  Effect of nonzero fill rule on a shape inside a shape
  with the path segment moving in the opposite direction
  (one square drawn clockwise, the other anti-clockwise)
  -->
  <path fill-rule="nonzero" stroke="red"
        d="M210,0  h90 v90 h-90 z
           M230,20 v50 h50 v-50 z"/>
</svg>
<svg viewBox="-10 -10 320 120" xmlns="http://www.w3.org/2000/svg">
  <!-- Effect of evenodd fill rule on crossing path segments -->
  <polygon fill-rule="evenodd" stroke="red"
           points="50,0 21,90 98,35 2,35 79,90"/>

  <!--
  Effect of evenodd fill rule on on a shape inside a shape
  with the path segment moving in the same direction
  (both squares drawn clockwise, to the "right")
  -->
  <path fill-rule="evenodd" stroke="red"
        d="M110,0  h90 v90 h-90 z
           M130,20 h50 v50 h-50 z"/>

    <!--
  Effect of evenodd fill rule on a shape inside a shape
  with the path segment moving in opposite direction
  (one square drawn clockwise, the other anti-clockwise)
  -->
  <path fill-rule="evenodd" stroke="red"
        d="M210,0  h90 v90 h-90 z
           M230,20 v50 h50 v-50 z"/>
</svg>

From: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/fill-rule

HackbrettXXX commented 4 years ago

This was fixed with #119. At least @yGuy's test cases work.

HackbrettXXX commented 3 years ago

Closing. @Hiovita if the issue still persists in svg2pdf@2 please reopen.