Python para Desenvolvedores

2ª edição, revisada e ampliada

Apêndice D: Inkscape


O editor de imagens vetoriais Inkscape permite o uso do Python como linguagem script, para a criação de extensões. O aplicativo utiliza o SVG como formato nativo e implementa vários recursos previstos no padrão.

As extensões para o Inkscape servem principalmente para a implementação de filtros e efeitos. Enquanto outros aplicativos (como o Blender) apresentam uma API na forma de módulos para o interpretador, o Inkscape passa argumentos pela linha de comando e transfere informações pela entrada e saída padrão do sistema operacional, de forma similar aos utilitários de tratamento de texto encontrados em sistemas UNIX. Com isso, a extensão tem acesso apenas aos elementos que fazem parte do documento, e não a interface gráfica do aplicativo. Qualquer interação com o usuário durante a execução fica por conta da extensão.

A criação e manipulação das estruturas de dados é feita usando XML, como prevê a especificação do formato SVG, permitindo com isso o uso de módulos como o ElementTree.

Para simplificar o processo, o Inkscape provê o módulo chamado inkex, que define estruturas básicas para extensões. Com esse módulo, novas extensões podem ser criadas por herança a partir de uma classe chamada Effect.

Exemplo (randomtext.py):

In [ ]:
import random
import inkex
import simplestyle


class RandomText(inkex.Effect):
    """
    Repete um texto aleatoriamente dentro de uma área.
    """
    def __init__(self):

        # Evoca a inicialização da superclasse
        inkex.Effect.__init__(self)

        # Adiciona um parâmetro para ser recebido do Inkscape
        self.OptionParser.add_option('-t', '--texto',
            action = 'store', type = 'string',
            dest = 'texto', default = 'Python',
            help = 'Texto para ser randomizado')

        self.OptionParser.add_option('-q', '--quantidade',
            action='store', type='int',
            dest='quantidade', default=20,
            help='Quantidade de vezes que o texto irá aparecer')

        self.OptionParser.add_option('-l', '--largura',
            action='store', type='int',
            dest='largura', default=1000,
            help='Largura da área')

        self.OptionParser.add_option('-c', '--altura',
            action='store', type='int',
            dest='altura', default=1000,
            help='Altura da área')

    def effect(self):

        # Pega as variáveis que foram passadas como
        # opções de linha de comando pelo Inkscape
        texto = self.options.texto
        quantidade = self.options.quantidade
        largura = self.options.largura
        altura = self.options.altura

        # Raiz do SVG
        svg = self.document.getroot()

        # Altura e largura do documento
        doc_largura  = inkex.unittouu(svg.attrib['width'])
        doc_altura = inkex.unittouu(svg.attrib['height'])

        # Cria uma camada no documento
        camada = inkex.etree.SubElement(svg, 'g')
        camada.set(inkex.addNS('label', 'inkscape'), 'randomtext')
        camada.set(inkex.addNS('groupmode', 'inkscape'), 'camada')

        for i in xrange(quantidade):

            # Cria um elemento para o texto
            xmltexto = inkex.etree.Element(inkex.addNS('text','svg'))
            xmltexto.text = texto

            # Posiciona o elemento no documento
            x = random.randint(0, largura) + (doc_largura -  largura) / 2
            xmltexto.set('x', str(x))
            y = random.randint(0, altura) + (doc_altura -  altura) / 2
            xmltexto.set('y', str(y))

            # Centraliza na vertical e na horizontal
            # e muda a cor de preenchimento usando CSS
            c = random.randint(100, 255)
            style = {'text-align' : 'center',
                'text-anchor': 'middle',
                'fill': '#%02x%02x%02x' % (c, c  - 30, c - 60)}
            xmltexto.set('style', simplestyle.formatStyle(style))

            # Coloca o texto na camada
            camada.append(xmltexto)


if __name__ == '__main__':
    rt = RandomText()
    rt.affect()

Para que o Inkscape reconheça a nova extensão, é necessário criar um arquivo XML com a configuração, que informa ao aplicativo os módulos e os parâmetros usados pela extensão, incluindo os tipos, limites e valores padrão desses parâmetros, para que ele possa interagir com o usuário através de uma caixa de dialogo antes da execução para obter os valores desejados. Os parâmetros são passados como argumentos na linha de comando quando o script é executado.

Arquivo de configuração (randomtext.inx):

In [ ]:
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
    <_name>RandomText</_name>
      <id>org.ekips.filter.randomtext</id>
    <dependency type="executable" location="extensions">randomtext.py</dependency>
    <dependency type="executable" location="extensions">inkex.py</dependency>
    <param name="texto" type="string" _gui-text="Texto">Python</param>
    <param name="quantidade" type="int" min="1" max="500" _gui-text="Quantidade">20</param>
    <param name="largura" type="int" min="1" max="10000" _gui-text="Largura">1000</param>
    <param name="altura" type="int" min="1" max="10000" _gui-text="Altura">1000</param>
    <effect>
    <object-type>all</object-type>
        <effects-menu>
            <submenu _name="Render"/>
        </effects-menu>
    </effect>
    <script>
        <command reldir="extensions" interpreter="python">randomtext.py</command>
    </script>
</inkscape-extension>

Janela com os parâmetros da extensão:

Inkscape

Exemplo de saída (quantidade igual a 100, largura igual a 600 e altura igual a 200):

Inkscape

Tanto o programa quanto o arquivo de configuração precisam ficar na pasta de extensões (share\extensions, dentro da pasta de instalação, para a versão Windows) para que sejam encontrados pelo aplicativo e o nome do arquivo de configuração precisa ter extensão .inx.

In [1]:
 
Out[1]: