Preface
Mail merge feature alternative using inkscape extension.
It is a good time to go in depth, with code made for this inkscape extension.
How to Use
The menu is simple
After a click, your template will be as below:
But how does it works under the hood? Consider have a look at the code.
Fill Layer Extension
Artefacts
We need three files.
fill_layer.inx
fill_layer.py
trainees\testbed.py
Fill Layers XML
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension
xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Fill Layers</name>
<id>epsi.fill_layers</id>
<effect>
<object-type>all</object-type>
<effects-menu>
<submenu name="Epsi"/>
</effects-menu>
</effect>
<script>
<command location="inx"
interpreter="python">fill_layers.py</command>
</script>
</inkscape-extension>
Duplicate Layers
The fill layers is pretty much the same with,
my previous duplicate layers
article.
Except that I bundle the process in a loop.
Fill Layers Python
The structure of the extension is shown as below.
#!/usr/bin/env python
import inkex
import sys, copy
from trainees.testbed import people
class FillLayers(inkex.EffectExtension):
...
if __name__ == '__main__':
FillLayers().run()
Notice the trainees.testbed
as we put the data in a subdirectory.
Why would I want to do that?
Because in my real life certificate mass production,
I have different template for each training.
Thus I separate hundreds of data into different files,
depend on the region and training session.
Effect Method
This is the main method in our inkscape extension.
Remember the show-people.py
in CLI?
Now we have them bundled in an extension.
def effect(self):
all_layers = self.get_layers()
container = self.find_layer(all_layers, 'Container')
source_layer = self.find_layer(all_layers, 'Template')
for key, person in people.items():
number = str(key).zfill(2)
name_only = person[1]
name_title = person[2]
# layer related
layer_id = 'person-id-' + number
layer_label = number + ' - ' + name_only
target_layer = self.duplicate_layer(
container, source_layer, layer_id, layer_label)
# display name
text_id = 'display-id-' + number
self.modify_display_name(target_layer, text_id, name_title)
Just a reminder, we are using hardcoded layer name,
such as Container
and Template
.
Finding Layers
The same as previous article.
def get_layers(self):
return {g
for g in self.svg.xpath('//svg:g')
if g.get('inkscape:groupmode') == 'layer'
}
def find_layer(self, layers, label_name):
for layer in layers:
name = layer.get('inkscape:label')
if name == label_name:
return layer
return None
and use the method in below assignment
def effect(self):
all_layers = self.get_layers()
container = self.find_layer(all_layers, 'Container')
source_layer = self.find_layer(all_layers, 'Template')
Processing Each Data
The same as previous article. But this time refactored as two separated methods.
- Duplicate Layer
- Modify Display Name
Duplicate Layer
def duplicate_layer(self, container, source_layer, layer_id, layer_label):
new_layer = copy.deepcopy(source_layer)
new_layer.label = layer_label
new_layer.set('id', layer_id)
new_layer.style = 'display:none'
container.append(new_layer)
return new_layer
We use it as
# layer related
layer_id = 'person-id-' + number
layer_label = number + ' - ' + name_only
target_layer = self.duplicate_layer(
container, source_layer, layer_id, layer_label)
Modify Display Name
def modify_display_name(self, target_layer, text_id, display_name):
text_node = target_layer[0]
if isinstance(text_node, inkex.TextElement):
tspan = text_node[0]
tspan.set('id', text_id)
tspan.text = display_name
We use it as
# display name
text_id = 'display-id-' + number
self.modify_display_name(target_layer, text_id, name_title)
The Result
Again, you can examine the result in XML editor.
Save the SVG. We are going to use it for further processing.
What is Next ?
Consider continue reading [ Inkscape Automation - Part Three ].
Thank you for reading.