Search code examples
pythonpython-3.xsvgvector-graphicssvgwrite

Unexplained behavior when saving SVG file using svgwrite


Background: I have been working on a image creator wrapper that allows creating images using raster (OpenCV/numpy/PIL) & vector graphics (svgwrite). The problem is when creating vector images using svgwrite.

Problem: The image creation process requires having a qrcode with some image overlayed above it and below it. So it is something like Image#1 --> Qrcode --> Image#2. Each of these is in vector format, qrcode is created using pyqrcode which is being saved into BytesIO and then converted to base64 to embed it as image inside the bigger SVG.

The quirk happens is when saving the drawing handle into format like SVG or PNG, the QRCode portion is lost. The string dump of the drawing handle though has it and am able to see the image through SVGViewer. Thoughts on what could be going wrong?

String dump of svgwrite Drawing handle (drawing.tostring()):

<svg baseProfile="full" height="425" version="1.1" viewBox="0 0 425 425" width="425" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:
xlink="http://www.w3.org/1999/xlink"><defs /><g transform="translate(11, 11)"><image xlink:href="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+CjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSIzMzNweCIgaGVpZ2h0PSIzMzNweCIgdmlld0JveD0iMCAwIDMzMyAzMzMiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDMzMyAzMzMiIHhtbDpzcGFjZT0icHJlc2VydmUiPiAgPGltYWdlIGlkPSJpbWFnZTAiIHdpZHRoPSIzMzMiIGhlaWdodD0iMzMzIiB4PSIwIiB5PSIwIgogICAgaHJlZj0iZGF0YTppbWFnZS9wbmc7YmFzZTY0LGlWQk9SdzBLR2dvQUFBQU5TVWhFVWdBQUFVMEFBQUZOQVFBQUFBQ3Npd0JwQUFBQUJHZEJUVUVBQUxHUEMveGhCUUFBQUNCalNGSk4KQUFCNkpnQUFnSVFBQVBvQUFBQ0E2QUFBZFRBQUFPcGdBQUE2bUFBQUYzQ2N1bEU4QUFBQUFtSkxSMFFBQWQyS0U2UUFBQUFKY0VoWgpjd0FBQUdBQUFBQmdBUEJyUXM4QUFBQUhkRWxOUlFmbEJnRU9KQUJHMTM3NEFBQUNla2xFUVZSbzN1MmFzWEhETUF4RjRYT2gwaU40CkZJMW1qNlpSTklKS0Zia2dCUEJCU2s1S0lkVkhrYVBOeHpULzhFRUNGb200cWI3bjlhRmZ0cERuTm8yRmZBVFJxOUdYdG1nYldOZzMKU3h5MnYzRzR4VWEwRG0wYnJvMHV6MDN1M3hJYmQvVnY0cjlJazQxb1BXb0pzM3RpekVNL3p4eWkvNGgyYllMSU0wUnJVUjJmM2JQYQp4OVZGczdLaGY5b2IwVXZSQ0V1UEJTSjlMT1FqaUY2TWFvWTdsSmVOOTR5Rmw0Mk1uV2dodW1WNmJKTTYydXFIbi9ITFU2VFFnMmdWCjJrV0NaL25MUWJ5RUg2MHF5d2JSQ2pRZEtndDJpS1J4T1BWVG9uV296S3RNdm9GRnBBZk1LeXYzS2tUclVLU0hYV1RiSWhQR1huRisKbzQyS2txOExvdGVqdlU2YldyN2hJcmxuOVhiU3k5VWlXb01lUFN0RnN2cGhLVFN1VTZFbzBSTFUvZWhVRzdwREhlNnZiVUcwQ29WVgo1UWJhMlpybUpVaVliS29TdlI0Tmg3ckRxcnhJaEhsRnd1UVpGNVpvR1JwNVl0cWtRNG1FZVFsNmZQT28zRVFMMEZWeW9vQ08zbUpzCnRGbmR2Q0FiMFNJMGlOdFJtMnhoakw1UzNMT0lGcUZabmtjWGU3R0hOQW8yeGp4RUM5RXN6OTJxWXN4c0N6a2Uzb21Xb2IyRk9pdTAKK2VWWmtwV2JhQVdxeHhaR3BvZDdsczA0ODQzdDkxZWlKU2hleXpIZDhkdXF0L2JrZm5JeDlES0lWcUNvM0ZFL29vc2RDdy9NL3VPbApSN1FLeFliMWxYUWZWNlUwcnh6OEVLMUNZNklBdGZ4RkxUSis4dWh6dGU1WlJFdFF6Y0Q4QU0vbWJsVWhwQkt0UXhFd3BueElQL3gwCnErVXk1Uy93aUphaEx5OGJ2VFpnd0k4ZjNrRXRpWXNzMFNKMFNVbVdKeTVQcDE0R0lnWkJSQ3ZSWVY3UlF1MGp0Ky9qRklKb0pmbysKL3RKb2l0SENpdW5PY2xhTDZMVW9QR3M4SVVhbk8zTUpRYlFHamNpMkhkS2pkN3E5Ykp6TWkrakY2QS9uVGlaS3hhWHdKQUFBQUNWMApSVmgwWkdGMFpUcGpjbVZoZEdVQU1qQXlNUzB3Tmkwd01WUXhORG96Tmpvd01Dc3dNRG93TU9GS0JSWUFBQUFsZEVWWWRHUmhkR1U2CmJXOWthV1o1QURJd01qRXRNRFl0TURGVU1UUTZNelk2TURBck1EQTZNRENRRjcycUFBQUFBRWxGVGtTdVFtQ0MiIC8+Cjwvc3ZnPgo=" /></g><g transform="translate(132, 132)"><g><circle cx="80" cy="80" fill="none" r="12" stroke="rgb(0,0,0)" stroke-width="4" /><circle cx="80" cy="80" fill="none" r="68" stroke="rgb(0,0,0)" stroke-width="4" /><circle cx="93" cy="115" fill="rgb(0,0,0)" r="10" stroke="rgb(0,0,0)" stroke-width="1" /><circle cx="117" cy="86" fill="rgb(0,0,0)" r="10" stroke="rgb(0,0,0)" stroke-width="1" /><circle cx="57" cy="109" fill="rgb(0,0,0)" r="10" stroke="rgb(0,0,0)" stroke-width="1" /><circle cx="43" cy="74" fill="rgb(0,0,0)" r="8" stroke="rgb(0,0,0)" stroke-width="1" /><circle cx="103" cy="51" fill="rgb(0,0,0)" r="8" stroke="rgb(0,0,0)" stroke-width="1" /><circle cx="67" cy="45" fill="rgb(0,0,0)" r="8" stroke="rgb(0,0,0)" stroke-width="1" /></g></g></svg>

The saved SVG using svgwrite.save():

<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="51pt" height="51pt" viewBox="0 0 51 51" version="1.1">
<g id="surface6">
<path style="fill:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:4;" d="M 91.990885 80.011719 C 91.990885 95.994792 68 95.994792 68 80.011719 C 68 63.996094 91.990885 63.996094 91.990885 80.011719 " transform="matrix(0.12,0,0,0.12,15.84,15.84)"/>
<path style="fill:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:4;" d="M 148.013021 80.011719 C 148.013021 117.544271 117.544271 148.013021 80.011719 148.013021 C 42.446615 148.013021 12.010417 117.544271 12.010417 80.011719 C 12.010417 42.446615 42.446615 12.010417 80.011719 12.010417 C 117.544271 12.010417 148.013021 42.446615 148.013021 80.011719 " transform="matrix(0.12,0,0,0.12,15.84,15.84)"/>
<path style="fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:4;" d="M 62.986979 54.002604 C 62.986979 67.348958 43 67.348958 43 54.002604 C 43 40.65625 62.986979 40.65625 62.986979 54.002604 " transform="matrix(0.12,0,0,0.12,15.84,15.84)"/>
<path style="fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:4;" d="M 54.002604 90.005208 C 54.002604 103.31901 34.015625 103.31901 34.015625 90.005208 C 34.015625 76.658854 54.002604 76.658854 54.002604 90.005208 " transform="matrix(0.12,0,0,0.12,15.84,15.84)"/>
<path style="fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:4;" d="M 116.990885 105.988281 C 116.990885 119.334635 97.003906 119.334635 97.003906 105.988281 C 97.003906 92.674479 116.990885 92.674479 116.990885 105.988281 " transform="matrix(0.12,0,0,0.12,15.84,15.84)"/>
<path style="fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:4;" d="M 97.003906 44.009115 C 97.003906 54.653646 80.988281 54.653646 80.988281 44.009115 C 80.988281 33.332031 97.003906 33.332031 97.003906 44.009115 " transform="matrix(0.12,0,0,0.12,15.84,15.84)"/>
<path style="fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:4;" d="M 123.989583 69.985677 C 123.989583 80.66276 108.00651 80.66276 108.00651 69.985677 C 108.00651 59.341146 123.989583 59.341146 123.989583 69.985677 " transform="matrix(0.12,0,0,0.12,15.84,15.84)"/>
<path style="fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:4;" d="M 79.002604 116.014323 C 79.002604 126.658854 62.986979 126.658854 62.986979 116.014323 C 62.986979 105.33724 79.002604 105.33724 79.002604 116.014323 " transform="matrix(0.12,0,0,0.12,15.84,15.84)"/>
</g>
</svg>


Solution

  • Figured the issue was because the image width & height was not being provided. Added the width & height using size parameter.

    group.add(background.image.image(href=foreground.image, size=(foreground.width, foreground.height)))