Where to Discuss?

Local Group

Preface

Duplicate layer using inkscape extension.

Here we are going to learn to alter XML tree.


Preparation

Put your replacable text in a layer named Template. This template layer is belong to to level layer called Container.

Template inside Container Layer

Now, also consider to have a look at the XML properties:

Layer Template in XML Editor

Notice that there is a child the text, and a grandchild the tspan. We are going to alter the text inside the tspan.


Duplicate Layer Extension

Artefacts

We need two files.

  • duplicate_layer.inx
  • duplicate_layer.py

Duplicate Layer XML

<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension
  xmlns="http://www.inkscape.org/namespace/inkscape/extension">
  <name>Duplicate Layers</name>
  <id>epsi.duplicate</id>
  <effect>
    <object-type>all</object-type>
    <effects-menu>
       <submenu name="Epsi"/>
    </effects-menu>
  </effect>
  <script>
    <command location="inx"
             interpreter="python">duplicate_layer.py</command>
  </script>
</inkscape-extension>

Duplicate Layer Python

Now we are ready to go to the coding part using python.

The structure of the extension is shown as below.

#!/usr/bin/env python

import inkex
import sys, copy

class DuplicateLayer(inkex.EffectExtension):
  ...

if __name__ == '__main__':
  DuplicateLayer().run()

Notice that we need to import copy for object duplication.

Effect Method

This is the main method in our inkscape extension.

  def effect(self):
    all_layers   = self.get_layers()
    container    = self.find_layer(all_layers,'Container')
    source_layer = self.find_layer(all_layers, 'Template')

    self.duplicate_layer(
      container, source_layer,
      'Zeta Mataharani', 'Zeta Mataharani, MD')

We are using hardcoded layer name, such as Container and Template.

I will explain the duplicate_layer method, later. For now, we discuss the result first.

Result

Disclaimer: I’have been using the Mataharani name, as example mockup for more than one decades. even my home wifi SSID is Mataharani.

The name Zeta Mataharani will be used for layer name

Certificate - ZM

and the Zeta Mataharani, MD will be used for display text in tspan.

Certificate Test Bed 04 - ZM MD

Now, consider go back to see how the code works under the hood. Consider begin with the get_layers method, then duplicate_layer method, and the rest following.


Finding Layers

Consider begin with the get_layers method, then duplicate_layer method, and the rest following.

Get Layers

It is simply list comprehension. Or to be exact, dict comprehension.

This look for any layer object.

  def get_layers(self):
    return {g
        for g in self.svg.xpath('//svg:g')
        if g.get('inkscape:groupmode') == 'layer'
      }

We will be using this as:

    def effect(self):
    all_layers   = self.get_layers()
    container    = self.find_layer(all_layers,'Container')
    source_layer = self.find_layer(all_layers, 'Template')

So how to find the layer?

First Occurence

This is just about finding first occurence of layer label.

  def find_layer(self, layers, label_name):
    for layer in layers:
      name = layer.get('inkscape:label')
      if name == label_name:
        return layer

    return None

I’m not sure about my code. This above works, but I guess there is better way to do this. After all this is not a javascript DOM library.


Duplicate Layer

Deepcopy

The main course is just a deepcopy

    new_layer = copy.deepcopy(source_layer)
    container.append(new_layer)

Then append it to Container layer.

Alter Name and Visibility

But I begin to think about, changing the layer label and the visibility. So I have this code

  def duplicate_layer(self, container, source_layer, layer_label):
    new_layer = copy.deepcopy(source_layer)
    new_layer.label = layer_label
    new_layer.style = 'display:none'
    container.append(new_layer)

Alter Text

And more feature, change the tspan text. The Template layer might has one or a few TextElement, but each TextElement only has one tspan.

    for node in new_layer:
      if isinstance(node, inkex.TextElement):
        tspan = node[0]
        tspan.text = text_span

Final Method

Finally we have this

    def duplicate_layer(self, container, source_layer, layer_label, text_span):
    new_layer = copy.deepcopy(source_layer)
    new_layer.label = layer_label
    new_layer.style = 'display:none'
    container.append(new_layer)

    for node in new_layer:
      if isinstance(node, inkex.TextElement):
        tspan = node[0]
        tspan.text = text_span

Complete Code

For your comfort, this below is the complete code.

#!/usr/bin/env python

import inkex
import sys, copy

class DuplicateLayer(inkex.EffectExtension):
  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

  def duplicate_layer(self, container, source_layer, layer_label, text_span):
    new_layer = copy.deepcopy(source_layer)
    new_layer.label = layer_label
    new_layer.style = 'display:none'
    container.append(new_layer)

    for node in new_layer:
      if isinstance(node, inkex.TextElement):
        tspan = node[0]
        tspan.text = text_span

  def effect(self):
    all_layers   = self.get_layers()
    container    = self.find_layer(all_layers,'Container')
    source_layer = self.find_layer(all_layers, 'Template')

    self.duplicate_layer(
      container, source_layer,
      'Zeta Mataharani', 'Zeta Mataharani, MD')

if __name__ == '__main__':
  DuplicateLayer().run()

Looks not pretty useful? Wogh.. you should read the next automation part. This short code above really save my working hours.


What is Next ?

Consider continue reading [ Inkscape Automation - Part One ].

Thank you for reading.