Adding SVGs to PDFs With Python and ReportLab
Adding SVGs to PDFs With Python and ReportLab
Adding graphics into PDFs programmatically has never been easier! Read on to check out how to do it using Python in this post!
Join the DZone community and get the full member experience.Join For Free
Learn how error monitoring with Sentry closes the gap between the product team and your customers. With Sentry, you can focus on what you do best: building and scaling software that makes your users’ lives better.
ReportLab has native support for generating SVG, 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 files and convert them into ReportLab Drawing objects. The svglib package also has a command-line tool, svg2pdf, that can convert SVG files to PDFs.
The svglib package depends on ReportLab and lxml. You can install both of these packages using pip:
pip install reportlab lxml
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
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!
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:
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. Then 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 our SVG image and then we run it through our demo code to turn it into a PDF.
The result looks like this:
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:
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
-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.
The svglib is the primary method to add SVGs to ReportLab at the time of writing this post. 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.
Published at DZone with permission of Mike Driscoll , DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.