Search code examples
pythonpdfsvgcairo

Convert SVG with embedded images to PDF


I am making a script that auto-generates PDF documents from a template. The document is generated as an SVG, which should then be converted to a PDF using cairosvg. This step, however, fails when an embedded image is encountered.

<!-- test.svg -->
<svg viewBox="0 0 1 1" width="100" height="100" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <rect x="0" y="0" width="1" height="1" fill="green"/>
    <image x="0" y="0" width="1" height="1" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsIAAA7CARUoSoAAAAATSURBVBhXYwCC/yACicZgMPwHAFnNBPyimoN0AAAAAElFTkSuQmCC"/>
</svg>

Cairosvg appears to be silently disregarding <image> tags when converting to other formats, not just pdf.

import cairosvg

cairosvg.svg2pdf(url='test.svg', write_to='out.pdf')
cairosvg.svg2png(url='test.svg', write_to='out.png')
cairosvg.svg2svg(url='test.svg', write_to='out.svg')

In this example, all outputs are missing the embedded image. Even svg output, which yields

<!-- out.svg -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="75pt" height="75pt" viewBox="0 0 75 75" version="1.1">
    <g id="surface7">
        <rect x="0" y="0" width="75" height="75" style="fill:rgb(0%,50.196078%,0%);fill-opacity:1;stroke:none;"/>
    </g>
</svg>

I have tried both filesystem paths and data URLs for href, and running cairosvg from a command line. In all cases, the embeds were silently discarded.

I have seen examples of cairosvg converting documents with embedded images properly, but without any code I could compare mine against. How can the images be included in conversion?


Solution

  • <image> tags are considered unsafe and they are not resolved unless unsafe mode is enabled explicitly.

    From the documentation (cairosvg --help):

    -u, --unsafe          resolve XML entities and allow very large files
                          (WARNING: vulnerable to XXE attacks and various DoS)
    

    In the Python API, set the unsafe parameter to true.

    cairosvg.svg2pdf(url='test.svg', write_to='out.pdf', unsafe=True)