Форум программистов, компьютерный форум, киберфорум
diadiavova
Войти
Регистрация
Восстановить пароль
Рейтинг: 4.50. Голосов: 2.

Расширение для Inkscape. Дополнение.

Запись от diadiavova размещена 02.06.2020 в 09:00

  1. Расширение XSLT 2.0 с использованием Saxon
  2. Расширение на Node.js
В предыдущем посте я рассмотрел общий подход к созданию расширения для Inkscape на примере приложения C#, которое выполняет преобразование XSLT. Сейчас мне просто хотелось бы добавить пару примеров.
Расширение XSLT 2.0 с использованием Saxon

Приложение, запускающее преобразование XSLT, рассмотренное в предыдущем посте, может быть полезно в том случае, когда оно добавляет в преобразование ряд полезных для нашей задачи функций. Это могут быть специфические для работы с SVG функции (матричные операции, работа с путями, тригонометрия и т. д.) либо специфичные для решения конкретной задачи. В том виде, в котором это было реализовано там, данный пример мог быть полезен преимущественно для демонстрационных целей и, если уж в качестве языка расширения выбран XSLT, то лучше использовать его более продвинутую версию. В качестве примера покажу как можно выполнить преобразование с помощью XSLT-процессора Saxon. Процессор нужно установить в систему. Он существует в Java и .Net версии, нам для наших целей это без разницы, просто там могут быть нюансы в использовании. У меня установлена .Net-версия. Также там есть различные редакции. Редакция HE (Home Edition) бесплатна и ее нам тоже будет вполне достаточно.
Правила, по которым Saxon запускается из командной строки, несколько отличаются от тех, на которые рассчитан код на Python, написанный нами ранее. Поэтому в модуль apprunner.py мы дополним функцией, с помощью которой будем запускать преобразования на Saxon. Теперь этот модуль будет выглядеть так
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import sys
import subprocess
import os
 
def run_ext(app, script = ""):
    tmppath = sys.argv[-1] + ".tmp"
    tmp = open(tmppath, "w+")
    tmp.write(open(sys.argv[-1], "r").read())
    tmp.close()
    subprocess.call([app, script] + sys.argv[1:-1] + [tmppath], stdout= sys.stdout, stderr=sys.stderr, shell=True)
    os.remove(tmppath)
 
def saxon_xslt(xsltfile):
    saxonpath = "C:\\Program Files\\Saxonica\\SaxonHE9.9N\\bin\\Transform.exe"
    svgpath = sys.argv[-1]
    tmppath = svgpath + ".xml"
    tmp = open(tmppath, "w+")
    tmp.write(open(svgpath, "r").read())
    tmp.close()
    ids = "ids=" + ",".join(map(lambda s: s[5:], filter(lambda s: s.startswith("--id="), sys.argv)))
    params = map(lambda x: x[2:], filter(lambda s: "=" in s and not s.startswith("--id="), sys.argv[1:-1]))
    arglist = [saxonpath,"-s:" + tmppath , "-xsl:" + xsltfile, ids] + list(params)
    subprocess.call(arglist, stdout = sys.stdout, stderr = sys.stderr, shell = True)
    os.remove(tmppath)

Здесь в функции saxon_xslt есть переменная saxonpath, ей я присвоил значение полного пути к Transform.exe установленного процессора. Если реальный путь другой (другая версия, редакция, выбрана ява, а не дотнет, или вообще выбран другой каталог установки), то реальный путь к этому файлу нужно указать именно здесь.
Создаем test-saxon.inx файл. Расширение будет выполнять ту же функцию, что и прежде, поэтому мы просто переделаем немного файл, созданный ранее.
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
  <_name>Testing saxon transform</_name>
  <id>org.inkscape.effect.saxontest</id>
 
  <param name="fill" type="string" _gui-text="Fill color"/>
  <param name="stroke" type="string" _gui-text="Stroke color"/>
  <effect>
    <object-type>all</object-type>
    <effects-menu>
      <submenu _name="Developer Examples"/>
    </effects-menu>
  </effect>
  <script>
<command reldir="extensions" interpreter="python">testsaxon.py</command>
</script>
</inkscape-extension>
Файл testsaxon.py
Python
1
2
3
4
5
# -*- coding: utf-8 -*-
 
from apprunner import saxon_xslt
 
saxon_xslt( "C:\\Users\\username\\AppData\\Roaming\\inkscape\\extensions\\xslt\\saxon.xslt")
Здесь мы просто вызываем функцию и передаем ей полный путь к XSLT-файлу.
Ну и собственно само преобразование.
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:fn="http://www.w3.org/2005/xpath-functions"
                xmlns:f="urn:this-document-functions"
>
  <xsl:output method="xml" indent="yes"/>
 
  <xsl:param name="ids" />
  <xsl:param name="fill" />
  <xsl:param name="stroke" />
 
  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>
 
  <xsl:function name="f:containsId">
    <xsl:param name="id"/>
    <xsl:param name="ids" />
    <xsl:value-of select="fn:index-of(fn:tokenize($ids, ','), $id) != 0"/>
  </xsl:function>
 
  <xsl:template match="*[@id]">
    <xsl:choose>
      <xsl:when test="f:containsId(@id, $ids)">
        <xsl:copy>
          <xsl:for-each select="@*">
              <xsl:choose>
                <xsl:when test="local-name() = 'style'">
                  <xsl:variable name="filled" select="fn:replace(., 'fill\s*:[^;]+;', concat('fill:', $fill, ';'))"/>
                  <xsl:attribute name="style" select="fn:replace($filled, 'stroke\s*:[^;]+;', concat('stroke:', $stroke, ';'))"/>
                </xsl:when>
                <xsl:otherwise>
                  <xsl:copy-of select="."/>
                </xsl:otherwise>
              </xsl:choose>
          </xsl:for-each>
          <xsl:apply-templates select="node()"/>
        </xsl:copy>
      </xsl:when>
      <xsl:otherwise>
        <xsl:copy>
          <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

Здесь благодаря возможностям XSLT 2 мы функцию containsId создали прямо на месте, ну и стили установили также средствами поддерживаемых функций с использованием регулярных выражений.

Расширение на Node.js

XSLT – язык не очень популярный, достаточно многословный и имеет ряд ограничений, из-за которых его не всегда удобно использовать. В то же время SVG, являющийся веб-форматом, наверное многие могут захотеть обрабатывать с помощью JavaScript. В этом смысле повествование было бы не полным, если бы я не привел пример с использованием Node.js.
Буду краток.
test-node.inx
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
  <_name>Testing node extension</_name>
  <id>org.inkscape.effect.nodetest</id>
 
  <param name="fill" type="string" _gui-text="Fill color"/>
  <param name="stroke" type="string" _gui-text="Stroke color"/>
  <effect>
    <object-type>all</object-type>
    <effects-menu>
      <submenu _name="Developer Examples"/>
    </effects-menu>
  </effect>
  <script>
<command reldir="extensions" interpreter="python">testnode.py</command>
</script>
</inkscape-extension>
testnode.py
Python
1
2
3
4
5
# -*- coding: utf-8 -*-
 
from apprunner import run_ext
 
run_ext("node", "C:\\Users\\laet\\AppData\\Roaming\\inkscape\\extensions\\node\\testnode.js")
Само собой, подразумевается, что Node.js установлен в системе, а путь к testnode.js указан правильно. Папку node я создал в папке расширений Inkscape. В эту папку надо установить пакет
Windows Batch file
1
npm install cheerio
testnode.js
Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const cheerio = require("cheerio");
const fs = require("fs")
const process = require("process")
fs.readFile(process.argv[process.argv.length - 1], function (err, data)
{
    let doc = cheerio.load(data,{xmlMode:true});
    let ids = process.argv.filter(a => a.startsWith("--id="))
        .map(id => "#" + id.substring(5))
        .join(",");
    let fill = process.argv.find(a => a.startsWith("--fill=")).substring(7);
    let stroke = process.argv.find(a => a.startsWith("--stroke=")).substring(9);
    doc(ids).css("fill", fill).css("stroke", stroke);
    process.stdout.write(doc.html({ xmlMode: true }));    
})

Все три расширения делают одну и ту же работу: всем выделенным объектам добавляют заливку и обводку. Тестировались только простые объекты, то есть это были пути, прямоугольники и т.д., но не группы. Все это было сделано в демонстрационных целях, так что, если начать экспериментировать, то можно получить ошибки. Но все расширения работают, если все правильно указать, а также не следует забывать о том, что при установке нового расширения, доступно оно станет после перезапуска приложения.


>>
Размещено в Без категории
Просмотров 262 Комментарии 2
Всего комментариев 2
Комментарии
  1. Старый комментарий
    Аватар для mozgotron
    Вау. Я не знал, что к Инкскейпу можно, как в Оффисе, прикручивать макросы. Обалденное приложение. Сегодня только очередные картинки делал в Inkscape.
    Запись от mozgotron размещена 02.06.2020 в 21:58 mozgotron вне форума
  2. Старый комментарий
    Аватар для diadiavova
    Цитата:
    Сообщение от mozgotron Просмотреть комментарий
    Вау. Я не знал, что к Инкскейпу можно, как в Оффисе, прикручивать макросы. Обалденное приложение. Сегодня только очередные картинки делал в Inkscape.
    Ну это не совсем то же самое. В офисе очень мощная инфраструктура расширений, они встраиваются прямо в программу, для макросов редактор есть, возможность записи и еще куча всего, что и не перечислишь. Здесь все намного проще и скромнее, но все же это лучше чем вообще ничего.
    Запись от diadiavova размещена 02.06.2020 в 22:43 diadiavova вне форума
    Обновил(-а) diadiavova 02.06.2020 в 22:47
 
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2021, vBulletin Solutions, Inc.