How I make bulk graphics using Inkscape and Python
In the organization on my campus, I occasionally work with various graphic works such as banners, document covers, etc. Inkscape is my software of choice to work with them.
Sometimes, I have to make bulk graphics. What do I mean by bulk graphics, though? It’s kind of graphic works that has variable pieces, commonly texts, in it. Certificates and member cards that would be delivered to many people are the examples.
Inkscape doesn’t come with the capability to create bulk graphics, sadly. There are actually several plugins to add this functionality into Inkscape. Yet the task is fairly simple. Why wouldn’t I use Python, my favorite programming language, to get the job done, then?
Here’s how I do it.
The SVG format
Firstly, let’s take a glance at Scalable Vector Graphics or SVG, Inkscape’s main format. It’s based on XML, therefore one can easily manipulate it even using a plain text editor.
Here’s how simple object such as a rectangle exists as an SVG code:
<rect x="40" y="30" width="20" height="10" fill="red" />
Neat, huh? Text and paragraph are written inside defined tags:
<text>The quick brown fox jumps over the lazy dog.</text>
The next step is to find a method to put “placeholders” for the variable contents inside the SVG code.
Python’s str.format()
Luckily, it seems there are several ways to format text in Python. However, as The Zen of Python says, there should be only one way to do it right for this case.
“There should be one–and preferably only one–obvious way to do it.”
It’s the str.format()
, whose ability is to format string by using replacement fields that delimited by braces in which the variable values are passed as keyword arguments:
>>> text = '{name} is a VTuber'
>>> text.format(name='Haato')
'Haato is a VTuber'
Example case
As the example case, I’ll make bulk member cards (kind of). This is the dataset I’ll be working on:
number | name | generation |
---|---|---|
93 | Haato | 1st generation |
94 | Marine | 3rd generation |
I’ll use the word tuple(s) to refer to the element(s) of the dataset.
1. Preparing the data
Sure, the data source can be anything and can take any form, but I want to keep this simple by giving the raw data directly in Python as constants and lists of tuples:
>>> first_num = 93
>>> people = [
... ('Haato', '1st generation'),
... ('Marine', '3rd generation')
... ]
Thanks to the magic of Python, the data can be processed easily according to needs:
>>> data = list(enumerate(people, start=first_num))
>>> data
[(93, ('Haato', '1st generation')), (94, ('Marine', '3rd generation'))]
2. Designing the template
This step is done in Inkscape. The main idea is to put the replacement fields in the design. It can be inside the texts, linked object source, or even as SVG styling parameters such as object’s color.
Here’s the look of the template used in this example:
3. Inserting data into the template
The template file is then opened and read:
>>> with open('template.svg', 'r') as f:
... template = f.read()
The fields of each tuple are inserted into the template by using str.format()
. The result is saved into the disk afterward:
>>> for num, (name, gen) in data:
... formatted_svg = template.format(number=num, name=name, generation=gen)
... filename = f'design_{num}.svg'
... with open(filename, 'w') as f:
... f.write(formatted_svg)
4. Finishing
At this point, the SVG files of every tuple have been created.
$ ls
design_93.svg design_94.svg template.svg
Here’s how they look:
Generally, I export SVG files to PDFs because it’s easier to print in my working environment. Inkscape provides a command-line API to do this job:
$ inkscape --export-pdf="output.pdf" "input.svg"
Bash exists to make life harder easier:
$ for i in design_*.svg
> do inkscape --export-pdf="${i%.*}.pdf" "$i.svg"
> done
$ ls
design_93.pdf design_93.svg design_94.pdf design_94.svg template.svg
Finally, all the PDF files can be compiled into a single file by
using pdfunite
(available in the package poppler-utils
in the
Ubuntu repository):
$ pdfunite "design_*.pdf" "compiled.pdf"
じぁ, また!
Jaa, mata! (Bye!)