48

I am trying to generate a PDF from a SVG input file with Python in a Django application.

I have already found 2 working solutions: cairo+rsvg and imagemagick but they both have one problem: They have some strange dependencies that I do not want to install on a server, for example DBUS and GTK.

So I am asking for another method for generating a PDF from SVG without having to install all these dependencies on a server.

pppery
  • 3,731
  • 22
  • 33
  • 46
Julian
  • 2,051
  • 2
  • 22
  • 30
  • This is EXACTLY my problem, but I have another limitation: being Unicode-compatible (for non-english texts inside svg). – saeedgnu May 09 '11 at 09:02

6 Answers6

55

Have you considered svglib?

It looks quite promising, especially as reportlab is the featured pdf tool in Django's docs.

from svglib.svglib import svg2rlg
from reportlab.graphics import renderPDF

drawing = svg2rlg("file.svg")
renderPDF.drawToFile(drawing, "file.pdf")
Sebastian Nielsen
  • 3,835
  • 5
  • 27
  • 43
arie
  • 18,737
  • 5
  • 70
  • 76
  • 1
    hmmm, this library is good, but seems that has problems with non-ascii (unicode) text! I'm still searching for a unicode-compatible library... – saeedgnu May 09 '11 at 08:56
  • 2
    how to add multiple drawings to one PDF? – andilabs Nov 13 '14 at 10:37
  • 1
    Just a side note to people... Make sure your .svg is formatted correctly... for some reason mine was formatted as a string... which mean that any occurrence of " or ' in the file was written with a \" or \'... That will cause issues... – Shmack Dec 29 '19 at 07:10
18

Yes, I would also suggest using svglib and the reportlab library for this task although there is very little documentation of the svglib library. I would actually suggest doing the following in your Django view:

from svglib.svglib import SvgRenderer
from reportlab.graphics import renderPDF
import xml.dom.minidom
@csrf_exempt
def export_svg(request):
    # Get data from client side via POST variables
    svg = request.POST.get("svg")
    doc = xml.dom.minidom.parseString(svg.encode( "utf-8" ))
    svg = doc.documentElement
    # Create new instance of SvgRenderer class
    svgRenderer = SvgRenderer()
    svgRenderer.render(svg)
    drawing = svgRenderer.finish()

    # Instead of outputting to a file, we simple return
    # the data and let the user download to their machine
    pdf = renderPDF.drawToString(drawing)
    response = HttpResponse(mimetype='application/pdf')
    response.write(pdf)     

    # If one were to remove the 'attachment; ' from this line
    # it would simple invoke the browsers default PDF plugin
    response["Content-Disposition"]= "attachment; filename=converted.pdf"
    return response

This way you never need to save a temporary file on the server for the user to just download locally anyway. The svglib example that is given requires providing a path to a file... but why not just provide the file itself?

I have documented the steps I have taken using Django and the Raphael SVG library here.

simoes
  • 4,897
  • 4
  • 23
  • 28
5

My answer may help someone on macOS:

I user CairoSVG

Firstly, install it with:

pip install cairosvg

Then you can use it in Python:

>>> import cairosvg
>>> cairosvg.svg2pdf(url='image.svg', write_to='image.pdf')

from its documentation:

on macOS, you’ll have to install cairo and libffi (with Homebrew for example)

fujianjin6471
  • 5,168
  • 1
  • 36
  • 32
  • 1
    Note for M1/M2 chip users: this might not work! I tried for hours and I couldn't get `cairosvg` to be imported correctly. The error message was `"cairosvg" is not accessed Pylance`. – M. Al Jumaily Aug 17 '22 at 22:50
2

As these answers are all quite old, I would like to post a modern solution using CairoSVG

This works with Django views:

import cairosvg

def export_svg(request):
    # Get data from POST
    svg = request.POST.get("svg")
    pdf = cairosvg.svg2pdf(bytestring=svg.encode("utf-8"))

    # The pdf is now a bytestring that can be returned instead of saving to disk. 
    response = HttpResponse(mimetype='application/pdf')
    response.write(pdf)     

Zexelon
  • 495
  • 5
  • 18
1

You will need to add "import string" for version 0.6.3 to work with python 2.7.

you can use my frok until the pypy is updated.

pip install git+git://github.com/ddehghan/libsvg.git
David Dehghan
  • 22,159
  • 10
  • 107
  • 95
1

I personally use pdfkit wrapper for Wkhtmltopdf. I tested with your example of SVG and it does have opacity.

Though, to test - I've enclosed SVG file into HTML and then converted HTML to PDF.

You can give it a try on my server (which uses Wkhtmltopdf):

response = requests.post('http://194.67.110.124:8000/html_to_pdf/',
                         files={
                             'template': ('template.html', open('template.html', 'rb')),
                             'picture': ('template.svg', open('template.svg', 'rb'))
                         },
                         data={})

where template.html is an HTML file contained SVG with prefix {{image_path}}. For example:

<!DOCTYPE html>
<html lang="en">
<img src="{{image_path}}/template.svg">
</html>

and template.svg if the SVG file. The result I get is:

enter image description here

The code for pdfkit is quite simple:

import pdfkit

pdfkit.from_file('template.html', 'output.pdf')

Where template.html contains the embedded SVG.

Note that pdfkit is only a wrapper and Wkhtmltopdf app has to be installed on the machine. (BTW, on Windows it's quite slow)

Egor Wexler
  • 1,754
  • 6
  • 22
  • @KJ on Windows it's ridiculously slow anyway, much better even to run it inside Docker container under Windows. Regarding the command - I see that it initiates Wkhtmltopdf process as `Wkhtmltopdf - out.pdf` and then passes the HTML file as UTF-8 text into stdin of the process. – Egor Wexler Jun 21 '21 at 06:55
  • @KJ When running on windows I also don't see the SVG. You can check the source of my server here: https://github.com/Luckykarter/html_to_pdf/blob/main/html_to_pdf/views.py and play around locally – Egor Wexler Jun 22 '21 at 06:28