Where to Discuss?

Local Group

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

Inkscape Extension - Menu

After a click, your template will be as below:

Certificate Duplicated Layers

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

Inkscape Extension Folder

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.

Layer Template ID 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.