Search code examples
svgwkhtmltopdf

SVG content with wkhtmltopdf not rendered properly


While rendering a PDF file using wkhtmltopdf using a file that has an embedded SVG chart I came up with a strange rendering problem.

For the purpose of this question I tried to simplify the SVG and HTML content, while keeping the problem reproductible. However, in production, I cannot (or rather I'd very much like not to) alter the SVG content, as it is generated from canvas2svg.

The SVG is a simple line chart and has at least 2 data sets (the example has 4).

On screen, the SVG is rendered like this, not being pixelated.

But in the resulting PDF file, it gets like this, with some lines getting pixelated, with rendering that makes you think of raster images rather than vector ones.

Screen image is zoomed in using the windows magnifier, while the pdf is zoomed in in viewer.

By varying the number of datasets in the chart, my conclusion is that only the last one gets rendered properly.

Conversion is done without any additional parameters, like this :

wkhtmltopdf.exe test.html test.pdf

Test files are here :

test.svg : https://drive.google.com/file/d/1y3onOnBhMPrlUgrRYQXlIckTOgvzuW2L/view?usp=sharing

test.html : https://drive.google.com/file/d/1SkspS3IfggSz9RCf17LL4w9qo8dN0WWH/view?usp=sharing

test.pdf : https://drive.google.com/file/d/1q7HXTqJASRDIzaFLn9w4XCVfHF5z2n5M/view?usp=sharing

Any explanation that would point me to a fix or workaround or any suggestion or idea about how to get the PDF right is appreciated.


Solution

  • This is not really a solution, but it's more of a workaround. Honestly I cannot remember how I found it (it's been too long, probably by tinkering with the SVG content), but here it is.

    Inside the SVG there is some clip-path property; the trick was to just remove it. I'm no SVG expert, so I cannot explain why this works.

    The original SVG looked like this :

    ...
    <defs>
        <clipPath id="vOWrkDAZwIkA">
            <path fill="none" stroke="none" d="..."/>
        </clipPath>
    </defs>
    ...
    <g clip-path="url(#vOWrkDAZwIkA)">
    ...
    </g>
    

    The flow of my app is this :

    • web app displays a report with embedded charts
    • chart content gets sent back to the server to reassemble a report
    • the report is converted from html to PDF by the wkhtmltopdf app

    So just before the SVG content gets reassembled, I used this :

    String svgData = /* just get the chart data here or possibly the whole HTML content */;
    String regex = "(\\sclip-path=\\\"url\\(#)[a-zA-Z]*(\\)\\\")";
    Matcher m = Pattern.compile(regex).matcher(svgData);
    
    while (m.find()) {
        svgData = svgData.replace(m.group(0), "");
    }   
    // use the modified SVG data
    

    Hope this helps.