Adding SVGs to PDFs with Python and ReportLab

ReportLab has native support for generating SVGs, but not for embedding SVGs in their PDFs. Fortunately, Dinu Gherman created the svglib package, a pure-Python package that can read SVG files and convert them to other formats that ReportLab can use. The official website for svglib is on Github.

The svglib package will work on Linux, Mac OS and Windows. The website states that it works with Python 2.7 - 3.5, but it should work in newer versions of Python as well.

You can use svglib to read your existing SVG giles and convert them into ReportLab Drawing objects. The svglib package also has a command-line tool, svg2pdf, that can convert SVG files to PDFs.


Dependencies

The svglib package depends on ReportLab and lxml. You can install both of these packages using pip:

pip install reportlab lxml

Installation

The svglib package can be installed using one of three methods.

Install the latest release

If you'd like to install the latest release from the Python Packaging Index, then you can just use pip the normal way:

pip install svglib

Install from latest version from source control

On the off chance that you want to use the latest version of the code (i.e. the bleeding edge / alpha builds), then you can install directly from Github using pip like this:

pip install git+https://github.com/deeplook/svglib

Manual installation

Most of the time, using pip is the way to go. But you can also download the tarball from the Python Packaging Index and do all the steps that pip does for you automatically if you want to. Just run the following three commands in your terminal in order:

tar xfz svglib-0.8.1.tar.gz
cd svglib-0.8.1
python setup.py install

Now that we have svglib installed, let's learn how to use it!


Usage

Using svglib with ReportLab is actually quite easy. All you need to do is import svg2rlg from svglib.svglib and give it the path to your SVG file. Let's take a look:

# svg_demo.py

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


def svg_demo(image_path, output_path):
    drawing = svg2rlg(image_path)
    renderPDF.drawToFile(drawing, output_path)
    renderPM.drawToFile(drawing, 'svg_demo.png', 'PNG')


if __name__ == '__main__':
    svg_demo('snakehead.svg', 'svg_demo.pdf')

After giving svg2rlg your path to the SVG file, it will return a drawing object. Then you can use this object to write it out as a PDF or a PNG. You could go on to use this script to create your own personal SVG to PNG converting utility!


Drawing on the Canvas

Personally, I don't like to create one-off PDFs with just an image in them like in the previous example. Instead, I want to be able to insert the image and write out text and other things. Fortunately, you can do this very easily by painting your canvas with the drawing object. Here's an example:

# svg_on_canvas.py

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

def add_image(image_path):
    my_canvas = canvas.Canvas('svg_on_canvas.pdf')
    drawing = svg2rlg(image_path)
    renderPDF.draw(drawing, my_canvas, 0, 40)
    my_canvas.drawString(50, 30, 'My SVG Image')
    my_canvas.save()

if __name__ == '__main__':
    image_path = 'snakehead.svg'
    add_image(image_path)

Here we create a canvas.Canvas object and then create our SVG drawing object. Now you can use renderPDF.draw to draw your drawing on your canvas at a specific x/y coordinate. We go ahead and draw out some small text underneath our image and then save it off. The result should look something like this:


Adding an SVG to a Flowable

Drawings in ReportLab can usually be added as a list of Flowables and built with a document template. The svglib's website says that its drawing objects are compatible with ReportLab's Flowable system. Let's use a different SVG for this example. We will be using the Flag of Cuba from Wikipedia. The svglib tests download a bunch of flag SVGs in their tests, so we will try one of the images that they use. You can get it here:

https://upload.wikimedia.org/wikipedia/commons/b/bd/Flag_of_Cuba.svg

Once you have the image saved off, we can take a look at the code:

# svg_demo2.py

import os

from reportlab.graphics import renderPDF, renderPM
from reportlab.platypus import SimpleDocTemplate
from svglib.svglib import svg2rlg


def svg_demo(image_path, output_path):
    drawing = svg2rlg(image_path)
    
    doc = SimpleDocTemplate(output_path)
    
    story = []
    story.append(drawing)
    
    doc.build(story)

if __name__ == '__main__':
    svg_demo('Flag_of_Cuba.svg', 'svg_demo2.pdf')

This worked pretty well, although the flag is cut off on the right side. Here's the output:

I actually had some trouble with this example. ReportLab or svglib seems to be really picky about the way the SVG is formatted or its size. Depending on the SVG I used, I would end up with an AttributeError or a blank document or I would be successful. So your mileage will probably vary. I will say that I spoke with some of the core developers and they mentioned that **SimpleDocTemplate** doesn't give you enough control over the frame that the drawing goes into, so you may need to create your own Frame or PageTemplate to make the SVG show up correctly. A workaround to get the snakehead.svg to work was to set the left and right margins to zero:

# svg_demo3.py

from reportlab.platypus import SimpleDocTemplate
from svglib.svglib import svg2rlg


def svg_demo(image_path, output_path):
    drawing = svg2rlg(image_path)
    
    doc = SimpleDocTemplate(output_path,
                            rightMargin=0,
                            leftMargin=0)
    
    story = []
    story.append(drawing)
    
    doc.build(story)

if __name__ == '__main__':
    svg_demo('snakehead.svg', 'svg_demo3.pdf')

Scaling SVGs in ReportLab

The SVG drawings you create with svglib are not scaled by default. So you will need to write a function to do that for you. Let's take a look:

# svg_scaled_on_canvas.py

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


def scale(drawing, scaling_factor):
    """
    Scale a reportlab.graphics.shapes.Drawing()
    object while maintaining the aspect ratio
    """
    scaling_x = scaling_factor
    scaling_y = scaling_factor
    
    drawing.width = drawing.minWidth() * scaling_x
    drawing.height = drawing.height * scaling_y
    drawing.scale(scaling_x, scaling_y)
    return drawing


def add_image(image_path, scaling_factor):
    my_canvas = canvas.Canvas('svg_scaled_on_canvas.pdf')
    drawing = svg2rlg(image_path)
    scaled_drawing = scale(drawing, scaling_factor=scaling_factor)
    renderPDF.draw(scaled_drawing, my_canvas, 0, 40)
    my_canvas.drawString(50, 30, 'My SVG Image')
    my_canvas.save()

if __name__ == '__main__':
    image_path = 'snakehead.svg'
    add_image(image_path, scaling_factor=0.5)

Here we have two functions. The first function will scale our image using a scaling factor. In this case, we use 0.5 as our scaling factor. Then we do some math against our drawing object and tell it to scale itself. Finally we draw it back out in much the same way as we did in the previous example.

Here is the result:


Using SVG Plots from matplotlib in ReportLab

In a previous article, we learned how to create graphs using just the ReportLab toolkit. One of the most popular 2D graphing packages for Python is matplotlib though. You can read all about matplotlib here: https://matplotlib.org/. The reason I am mentioning matplotlib in this article is that it supports SVG as one of its output formats. So we will look at how to take a plot created with matplotlib and insert it into ReportLab.

To install matplotlib, the most popular method is to use pip:

pip install matplotlib

Now that we have matplotlib installed, we can create a simple plot and export it as SVG. Let's see how this works:

import matplotlib.pyplot as pyplot

def create_matplotlib_svg(plot_path):
    pyplot.plot(list(range(5)))
    pyplot.title = 'matplotlib SVG + ReportLab'
    pyplot.ylabel = 'Increasing numbers'
    pyplot.savefig(plot_path, format='svg')

if __name__ == '__main__':
    from svg_demo import svg_demo
    svg_path = 'matplot.svg'
    create_matplotlib_svg(svg_path)
    svg_demo(svg_path, 'matplot.pdf')

In this code, we import the pyplot sub-library from matplotlib. Next we create a simple function that takes the path to where we want to save our plot. For this simple plot, we create a simple range of five numbers for one of the axes. hen we add a title and a y-label. Finally we save the plot to disk as an SVG.

The last step is in the if statement at the bottom of the code. Here we import our svg_demo code from earlier in this article. We create oru SVG image and then we run it through our demo code to turn it into a PDF.

The result looks like this:


Using svg2pdf

When you install svglib, you also get a command-line tool called svg2pdf. As the name implies, you can use this tool to convert SVG files to PDF files. Let's look at a couple of examples:

svg2pdf /path/to/plot.svg

This command just takes the path to the SVG file that you want to turn into a PDF. It will automatically rename the output to the same name as the input file, but with the PDF extension. You can specify the output name though:

svg2pdf -o /path/to/output.pdf /path/to/plot.svg

The -o flag tells svg2pdf requires that you pass in the output PDF path followed by the input SVG path.

The documentation also mentions that you can convert all the SVG files to PDFs using a command like the following:

svg2pdf -o "%(base)s.pdf" path/to/file*.svg

This will rename the output PDF to the same name as the input SVG file for each SVG file in the specified folder.


Wrapping Up

The svglib is the primary method to add SVGs to ReportLab at the time of writing this book. While it isn't full featured, it works pretty well and the API is quite nice. We also learned how to insert a plot SVG created via the popular matplotlib package. Finally we looked at how to turn SVGs to PDFs using the svg2pdf command line tool.


Related Reading

Copyright © 2024 Mouse Vs Python | Powered by Pythonlibrary