Added inx-pathops from Maren :)
This commit is contained in:
parent
bd136efd3a
commit
bb0028c255
30
extensions/fablabchemnitz_pathops.inx
Normal file
30
extensions/fablabchemnitz_pathops.inx
Normal file
@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
|
||||
<name>PathOps Custom</name>
|
||||
<id>fablabchemnitz.de.pathops</id>
|
||||
<param name="ink_verb" type="optiongroup" gui-text="Path operation:">
|
||||
<option value="SelectionUnion">Union</option>
|
||||
<option value="SelectionDiff">Difference</option>
|
||||
<option value="SelectionIntersect">Intersection</option>
|
||||
<option value="SelectionSymDiff">Exclusion</option>
|
||||
<option value="SelectionDivide">Division</option>
|
||||
<option value="SelectionCutPath">Cut Path</option>
|
||||
<option value="SelectionCombine">Combine</option>
|
||||
</param>
|
||||
<param name="max_ops" type="int" min="2" max="9999" gui-text="Max operations per run">500</param>
|
||||
<param name="recursive_sel" type="boolean" gui-text="Recurse into groups" gui-description="If unchecked, only direct children of top-level groups in the selection will be processed for inclusion.">true</param>
|
||||
<param name="keep_top" type="boolean" gui-text="Keep top element when done">true</param>
|
||||
<param name="dry_run" type="boolean" gui-text="Dry run">false</param>
|
||||
<effect needs-document="true" needs-live-preview="false">
|
||||
<effects-menu hidden="false">
|
||||
<submenu name="FabLab Chemnitz">
|
||||
<submenu name="PathOps"/>
|
||||
</submenu>
|
||||
</effects-menu>
|
||||
<menu-tip>Apply Inkscape path operations to multiple objects.</menu-tip>
|
||||
<object-type>all</object-type>
|
||||
</effect>
|
||||
<script>
|
||||
<command location="inx" interpreter="python">fablabchemnitz_pathops.py</command>
|
||||
</script>
|
||||
</inkscape-extension>
|
390
extensions/fablabchemnitz_pathops.py
Normal file
390
extensions/fablabchemnitz_pathops.py
Normal file
@ -0,0 +1,390 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
pathops.py - Inkscape extension to apply multiple path operations
|
||||
|
||||
This extension takes a selection of path and a group of paths, or several
|
||||
paths, and applies a path operation with the top-most path in the z-order, and
|
||||
each selected path or each child of a selected group underneath.
|
||||
|
||||
Copyright (C) 2014 Ryan Lerch (multiple difference)
|
||||
2016 Maren Hachmann <marenhachmannATyahoo.com>
|
||||
(refactoring, extend to multibool)
|
||||
2017 su_v <suv-sf@users.sf.net>
|
||||
Rewrite to support large selections (process in chunks), to
|
||||
improve performance (support groups, z-sort ids with python
|
||||
instead of external query), and to extend GUI options.
|
||||
2020 Maren Hachmann <marenhachmann@yahoo.com>
|
||||
Update to make it work with Inkscape 1.0's new inx scheme, extensions API and command line API.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
"""
|
||||
# pylint: disable=too-many-ancestors
|
||||
|
||||
# standard library
|
||||
import os
|
||||
from shutil import copy2
|
||||
from subprocess import Popen, PIPE
|
||||
import time
|
||||
from lxml import etree
|
||||
|
||||
# local library
|
||||
import inkex
|
||||
from inkex.command import inkscape
|
||||
|
||||
__version__ = '0.4'
|
||||
|
||||
|
||||
# Global "constants"
|
||||
SVG_SHAPES = ('rect', 'circle', 'ellipse', 'line', 'polyline', 'polygon')
|
||||
|
||||
|
||||
# ----- general helper functions
|
||||
|
||||
def timed(f):
|
||||
"""Minimalistic timer for functions."""
|
||||
# pylint: disable=invalid-name
|
||||
start = time.time()
|
||||
ret = f()
|
||||
elapsed = time.time() - start
|
||||
return ret, elapsed
|
||||
|
||||
|
||||
# ----- SVG element helper functions
|
||||
|
||||
def get_defs(node):
|
||||
"""Find <defs> in children of *node*, return first one found."""
|
||||
path = '/svg:svg//svg:defs'
|
||||
try:
|
||||
return node.xpath(path, namespaces=inkex.NSS)[0]
|
||||
except IndexError:
|
||||
return etree.SubElement(node, inkex.addNS('defs', 'svg'))
|
||||
|
||||
|
||||
def is_group(node):
|
||||
"""Check node for group tag."""
|
||||
return node.tag == inkex.addNS('g', 'svg')
|
||||
|
||||
|
||||
def is_path(node):
|
||||
"""Check node for path tag."""
|
||||
return node.tag == inkex.addNS('path', 'svg')
|
||||
|
||||
|
||||
def is_basic_shape(node):
|
||||
"""Check node for SVG basic shape tag."""
|
||||
return node.tag in (inkex.addNS(tag, 'svg') for tag in SVG_SHAPES)
|
||||
|
||||
|
||||
def is_custom_shape(node):
|
||||
"""Check node for Inkscape custom shape type."""
|
||||
return inkex.addNS('type', 'sodipodi') in node.attrib
|
||||
|
||||
|
||||
def is_shape(node):
|
||||
"""Check node for SVG basic shape tag or Inkscape custom shape type."""
|
||||
return is_basic_shape(node) or is_custom_shape(node)
|
||||
|
||||
|
||||
def has_path_effect(node):
|
||||
"""Check node for Inkscape path-effect attribute."""
|
||||
return inkex.addNS('path-effect', 'inkscape') in node.attrib
|
||||
|
||||
|
||||
def is_modifiable_path(node):
|
||||
"""Check node for editable path data."""
|
||||
return is_path(node) and not (has_path_effect(node) or
|
||||
is_custom_shape(node))
|
||||
|
||||
|
||||
def is_image(node):
|
||||
"""Check node for image tag."""
|
||||
return node.tag == inkex.addNS('image', 'svg')
|
||||
|
||||
|
||||
def is_text(node):
|
||||
"""Check node for text tag."""
|
||||
return node.tag == inkex.addNS('text', 'svg')
|
||||
|
||||
|
||||
def does_pathops(node):
|
||||
"""Check whether node is supported by Inkscape path operations."""
|
||||
return (is_path(node) or
|
||||
is_shape(node) or
|
||||
is_text(node))
|
||||
|
||||
|
||||
# ----- list processing helper functions
|
||||
|
||||
def recurse_selection(node, id_list, level=0, current=0):
|
||||
"""Recursively process selection, add checked elements to id list."""
|
||||
current += 1
|
||||
if not level or current <= level:
|
||||
if is_group(node):
|
||||
for child in node:
|
||||
id_list = recurse_selection(child, id_list, level, current)
|
||||
if does_pathops(node):
|
||||
id_list.append(node.get('id'))
|
||||
return id_list
|
||||
|
||||
|
||||
def z_sort(node, alist):
|
||||
"""Return new list sorted in document order (depth-first traversal)."""
|
||||
ordered = []
|
||||
id_list = list(alist)
|
||||
count = len(id_list)
|
||||
for element in node.iter():
|
||||
element_id = element.get('id')
|
||||
if element_id is not None and element_id in id_list:
|
||||
id_list.remove(element_id)
|
||||
ordered.append(element_id)
|
||||
count -= 1
|
||||
if not count:
|
||||
break
|
||||
return ordered
|
||||
|
||||
|
||||
def z_iter(node, alist):
|
||||
"""Return iterator over ids in document order (depth-first traversal)."""
|
||||
id_list = list(alist)
|
||||
for element in node.iter():
|
||||
element_id = element.get('id')
|
||||
if element_id is not None and element_id in id_list:
|
||||
id_list.remove(element_id)
|
||||
yield element_id
|
||||
|
||||
|
||||
def chunks(alist, max_len):
|
||||
"""Chunk a list into sublists of max_len length."""
|
||||
for i in range(0, len(alist), max_len):
|
||||
yield alist[i:i+max_len]
|
||||
|
||||
|
||||
# ----- process external command, files
|
||||
|
||||
# def run(cmd_format, stdin_str=None, verbose=False):
|
||||
# """Run command"""
|
||||
# if verbose:
|
||||
# inkex.utils.debug(cmd_format)
|
||||
# out = err = None
|
||||
# myproc = Popen(cmd_format, shell=False,
|
||||
# stdin=PIPE, stdout=PIPE, stderr=PIPE)
|
||||
# out, err = myproc.communicate(stdin_str)
|
||||
# if myproc.returncode == 0:
|
||||
# return out
|
||||
# elif err is not None:
|
||||
# inkex.errormsg(err)
|
||||
|
||||
|
||||
# ----- PathOps() class, methods
|
||||
|
||||
class PathOps(inkex.Effect):
|
||||
"""Effect-based class to apply Inkscape path operations."""
|
||||
|
||||
def __init__(self):
|
||||
"""Init base class."""
|
||||
inkex.Effect.__init__(self)
|
||||
|
||||
# options
|
||||
self.arg_parser.add_argument("--ink_verb", default="SelectionDiff", help="Inkscape verb for path op")
|
||||
self.arg_parser.add_argument("--max_ops", type=int, default=500, help="Max ops per external run")
|
||||
self.arg_parser.add_argument("--recursive_sel", type=inkex.Boolean, help="Recurse beyond one group level")
|
||||
self.arg_parser.add_argument("--keep_top", type=inkex.Boolean, help="Keep top element when done")
|
||||
self.arg_parser.add_argument("--dry_run", type=inkex.Boolean, default=False, help="Dry-run without exec")
|
||||
|
||||
def get_selected_ids(self):
|
||||
"""Return a list of valid ids for inkscape path operations."""
|
||||
id_list = []
|
||||
if not len(self.svg.selected):
|
||||
pass
|
||||
else:
|
||||
# level = 0: unlimited recursion into groups
|
||||
# level = 1: process top-level groups only
|
||||
level = 0 if self.options.recursive_sel else 1
|
||||
for node in self.svg.selected.values():
|
||||
recurse_selection(node, id_list, level)
|
||||
if len(id_list) < 2:
|
||||
inkex.errormsg("This extension requires at least 2 elements " +
|
||||
"of type path, shape or text. " +
|
||||
"The elements can be part of selected groups, " +
|
||||
"or directly selected.")
|
||||
return None
|
||||
else:
|
||||
return id_list
|
||||
|
||||
def get_sorted_ids(self):
|
||||
"""Return id of top-most object, and a list with z-sorted ids."""
|
||||
top_path = None
|
||||
sorted_ids = None
|
||||
id_list = self.get_selected_ids()
|
||||
if id_list is not None:
|
||||
sorted_ids = list(z_iter(self.document.getroot(), id_list))
|
||||
top_path = sorted_ids.pop()
|
||||
return (top_path, sorted_ids)
|
||||
|
||||
|
||||
def run_pathops(self, svgfile, top_path, id_list, ink_verb, dry_run=False):
|
||||
"""Run path ops with top_path on a list of other object ids."""
|
||||
# build list with command line arguments
|
||||
actions_list = []
|
||||
for node_id in id_list:
|
||||
actions_list.append("select-by-id:" + top_path)
|
||||
actions_list.append("EditDuplicate")
|
||||
actions_list.append("select-by-id:" + node_id)
|
||||
actions_list.append(ink_verb)
|
||||
actions_list.append("EditDeselect")
|
||||
actions_list.append("FileSave")
|
||||
actions_list.append("FileQuit")
|
||||
actions = ";".join(actions_list)
|
||||
# process command list
|
||||
if dry_run:
|
||||
inkex.utils.debug(" ".join(["inkscape", "--with-gui", "--actions=" + "\"" + actions + "\"", svgfile]))
|
||||
else:
|
||||
inkscape(svgfile, "--with-gui", actions=actions)
|
||||
|
||||
def loop_pathops(self, top_path, other_paths):
|
||||
"""Loop through selected items and run external command(s)."""
|
||||
# init variables
|
||||
count = 0
|
||||
max_ops = self.options.max_ops or 500
|
||||
ink_verb = self.options.ink_verb or "SelectionDiff"
|
||||
dry_run = self.options.dry_run
|
||||
tempfile = self.options.input_file + "-pathops.svg"
|
||||
# prepare
|
||||
if dry_run:
|
||||
inkex.utils.debug("# Top object id: {}".format(top_path))
|
||||
inkex.utils.debug("# Other objects total: {}".format(len(other_paths)))
|
||||
else:
|
||||
# we need to do this because command line Inkscape with gui
|
||||
# gives lots of info dialogs when the file extension isn't 'svg'
|
||||
# so the inkscape() call cannot open the file without user
|
||||
# interaction, and fails in the end when trying to save
|
||||
copy2(self.options.input_file, tempfile)
|
||||
# loop through sorted id list, process in chunks
|
||||
for chunk in chunks(other_paths, max_ops):
|
||||
count += 1
|
||||
if dry_run:
|
||||
inkex.utils.debug("\n# Processing {}. chunk ".format(count) +
|
||||
"with {} objects ...".format(len(chunk)))
|
||||
self.run_pathops(tempfile, top_path, chunk, ink_verb, dry_run)
|
||||
# finish up
|
||||
if dry_run:
|
||||
inkex.utils.debug("\n# {} chunks processed, ".format(count) +
|
||||
"with {} total objects.".format(len(other_paths)))
|
||||
else:
|
||||
# replace current document with content of temp copy file
|
||||
self.document = inkex.load_svg(tempfile)
|
||||
# update self.svg
|
||||
self.svg = self.document.getroot()
|
||||
|
||||
# optionally delete top-most element when done
|
||||
if not self.options.keep_top:
|
||||
top_node = self.svg.getElementById(top_path)
|
||||
if top_node is not None:
|
||||
top_node.getparent().remove(top_node)
|
||||
# purge missing tagrefs (see below)
|
||||
self.update_tagrefs()
|
||||
# clean up
|
||||
self.cleanup(tempfile)
|
||||
|
||||
def cleanup(self, tempfile):
|
||||
"""Clean up tempfile."""
|
||||
try:
|
||||
os.remove(tempfile)
|
||||
except Exception: # pylint: disable=broad-except
|
||||
pass
|
||||
|
||||
def effect(self):
|
||||
"""Main entry point to process current document."""
|
||||
if self.has_tagrefs():
|
||||
# unsafe to use with extensions ...
|
||||
inkex.errormsg("This document uses Inkscape selection sets. " +
|
||||
"Modifying the content with a PathOps extension " +
|
||||
"may cause Inkscape to crash on reload or close. " +
|
||||
"Please delete the selection sets, " +
|
||||
"save the document under a new name and " +
|
||||
"try again in a new Inkscape session.")
|
||||
else:
|
||||
# process selection
|
||||
top_path, other_paths = self.get_sorted_ids()
|
||||
if top_path is None or other_paths is None:
|
||||
return
|
||||
else:
|
||||
self.loop_pathops(top_path, other_paths)
|
||||
|
||||
# ----- workaround to avoid crash on quit
|
||||
|
||||
# If selection set tagrefs have been deleted as a result of the
|
||||
# extension's modifications of the drawing content, inkscape will
|
||||
# crash when closing the document window later on unless the tagrefs
|
||||
# are checked and cleaned up manually by the extension script.
|
||||
|
||||
# NOTE: crash on reload in the main process (after the extension has
|
||||
# finished) still happens if Selection Sets dialog was actually
|
||||
# opened and used in the current session ... the extension could
|
||||
# create fake (invisible) objects which reuse the ids?
|
||||
# No, fake placeholder elements do not prevent the crash on reload
|
||||
# if the dialog was opened before.
|
||||
|
||||
# TODO: these checks (and the purging of obsolete tagrefs) probably
|
||||
# should be applied in Effect() itself, instead of relying on
|
||||
# workarounds in derived classes that modify drawing content.
|
||||
|
||||
def has_tagrefs(self):
|
||||
"""Check whether document has selection sets with tagrefs."""
|
||||
defs = get_defs(self.document.getroot())
|
||||
inkscape_tagrefs = defs.findall(
|
||||
"inkscape:tag/inkscape:tagref", namespaces=inkex.NSS)
|
||||
return True if len(inkscape_tagrefs) else False
|
||||
|
||||
def update_tagrefs(self, mode='purge'):
|
||||
"""Check tagrefs for deleted objects."""
|
||||
defs = get_defs(self.document.getroot())
|
||||
inkscape_tagrefs = defs.findall(
|
||||
"inkscape:tag/inkscape:tagref", namespaces=inkex.NSS)
|
||||
if len(inkscape_tagrefs):
|
||||
for tagref in inkscape_tagrefs:
|
||||
href = tagref.get(inkex.addNS('href', 'xlink'))[1:]
|
||||
if self.svg.getElementById(href) is None:
|
||||
if mode == 'purge':
|
||||
tagref.getparent().remove(tagref)
|
||||
elif mode == 'placeholder':
|
||||
temp = etree.Element(inkex.addNS('path', 'svg'))
|
||||
temp.set('id', href)
|
||||
temp.set('d', 'M 0,0 Z')
|
||||
self.document.getroot().append(temp)
|
||||
|
||||
# ----- workaround to fix Effect() performance with large selections
|
||||
|
||||
def collect_ids(self, doc=None):
|
||||
"""Iterate all elements, build id dicts (doc_ids, selected)."""
|
||||
doc = self.document if doc is None else doc
|
||||
id_list = list(self.options.ids)
|
||||
for node in doc.getroot().iter(tag=etree.Element):
|
||||
if 'id' in node.attrib:
|
||||
node_id = node.get('id')
|
||||
self.doc_ids[node_id] = 1
|
||||
if node_id in id_list:
|
||||
self.svg.selected[node_id] = node
|
||||
id_list.remove(node_id)
|
||||
|
||||
def getselected(self):
|
||||
"""Overload Effect() method."""
|
||||
self.collect_ids()
|
||||
|
||||
def getdocids(self):
|
||||
"""Overload Effect() method."""
|
||||
pass
|
||||
|
||||
PathOps().run()
|
18
extensions/fablabchemnitz_pathops_combine.inx
Normal file
18
extensions/fablabchemnitz_pathops_combine.inx
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
|
||||
<name>Combine</name>
|
||||
<id>fablabchemnitz.de.pathops_combine</id>
|
||||
<param name="ink_verb" gui-hidden="true" type="string" gui-text="Path operation">SelectionCombine</param>
|
||||
<effect needs-document="true" needs-live-preview="false">
|
||||
<effects-menu hidden="false">
|
||||
<submenu name="FabLab Chemnitz">
|
||||
<submenu name="PathOps" />
|
||||
</submenu>
|
||||
</effects-menu>
|
||||
<menu-tip>Apply Inkscape 'Combine' path operation to multiple objects.</menu-tip>
|
||||
<object-type>all</object-type>
|
||||
</effect>
|
||||
<script>
|
||||
<command location="inx" interpreter="python">fablabchemnitz_pathops.py</command>
|
||||
</script>
|
||||
</inkscape-extension>
|
18
extensions/fablabchemnitz_pathops_cutpath.inx
Normal file
18
extensions/fablabchemnitz_pathops_cutpath.inx
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
|
||||
<name>Cut Path</name>
|
||||
<id>fablabchemnitz.de.pathops_cutpath</id>
|
||||
<param name="ink_verb" gui-hidden="true" type="string" gui-text="Path operation">SelectionCutPath</param>
|
||||
<effect needs-document="true" needs-live-preview="false">
|
||||
<effects-menu hidden="false">
|
||||
<submenu name="FabLab Chemnitz">
|
||||
<submenu name="PathOps" />
|
||||
</submenu>
|
||||
</effects-menu>
|
||||
<menu-tip>Apply Inkscape 'Cut Path' path operation to multiple objects.</menu-tip>
|
||||
<object-type>all</object-type>
|
||||
</effect>
|
||||
<script>
|
||||
<command location="inx" interpreter="python">fablabchemnitz_pathops.py</command>
|
||||
</script>
|
||||
</inkscape-extension>
|
18
extensions/fablabchemnitz_pathops_difference.inx
Normal file
18
extensions/fablabchemnitz_pathops_difference.inx
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
|
||||
<name>Difference</name>
|
||||
<id>fablabchemnitz.de.pathops_difference</id>
|
||||
<param name="ink_verb" gui-hidden="true" type="string" gui-text="Path operation">SelectionDiff</param>
|
||||
<effect needs-document="true" needs-live-preview="false">
|
||||
<effects-menu hidden="false">
|
||||
<submenu name="FabLab Chemnitz">
|
||||
<submenu name="PathOps" />
|
||||
</submenu>
|
||||
</effects-menu>
|
||||
<menu-tip>Apply Inkscape 'Difference' path operation to multiple objects.</menu-tip>
|
||||
<object-type>all</object-type>
|
||||
</effect>
|
||||
<script>
|
||||
<command location="inx" interpreter="python">fablabchemnitz_pathops.py</command>
|
||||
</script>
|
||||
</inkscape-extension>
|
18
extensions/fablabchemnitz_pathops_division.inx
Normal file
18
extensions/fablabchemnitz_pathops_division.inx
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
|
||||
<name>Division</name>
|
||||
<id>fablabchemnitz.de.pathops_division</id>
|
||||
<param name="ink_verb" gui-hidden="true" type="string" gui-text="Path operation">SelectionDivide</param>
|
||||
<effect needs-document="true" needs-live-preview="false">
|
||||
<effects-menu hidden="false">
|
||||
<submenu name="FabLab Chemnitz">
|
||||
<submenu name="PathOps" />
|
||||
</submenu>
|
||||
</effects-menu>
|
||||
<menu-tip>Apply Inkscape 'Division' path operation to multiple objects.</menu-tip>
|
||||
<object-type>all</object-type>
|
||||
</effect>
|
||||
<script>
|
||||
<command location="inx" interpreter="python">fablabchemnitz_pathops.py</command>
|
||||
</script>
|
||||
</inkscape-extension>
|
18
extensions/fablabchemnitz_pathops_exclusion.inx
Normal file
18
extensions/fablabchemnitz_pathops_exclusion.inx
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
|
||||
<name>Exclusion</name>
|
||||
<id>fablabchemnitz.de.pathops_exclusion</id>
|
||||
<param name="ink_verb" gui-hidden="true" type="string" gui-text="Path operation">SelectionSymDiff</param>
|
||||
<effect needs-document="true" needs-live-preview="false">
|
||||
<effects-menu hidden="false">
|
||||
<submenu name="FabLab Chemnitz">
|
||||
<submenu name="PathOps" />
|
||||
</submenu>
|
||||
</effects-menu>
|
||||
<menu-tip>Apply Inkscape 'Exclusion' path operation to multiple objects.</menu-tip>
|
||||
<object-type>all</object-type>
|
||||
</effect>
|
||||
<script>
|
||||
<command location="inx" interpreter="python">fablabchemnitz_pathops.py</command>
|
||||
</script>
|
||||
</inkscape-extension>
|
18
extensions/fablabchemnitz_pathops_intersection.inx
Normal file
18
extensions/fablabchemnitz_pathops_intersection.inx
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
|
||||
<name>Intersection</name>
|
||||
<id>fablabchemnitz.de.pathops_intersect</id>
|
||||
<param name="ink_verb" gui-hidden="true" type="string" gui-text="Path operation">SelectionIntersect</param>
|
||||
<effect needs-document="true" needs-live-preview="false">
|
||||
<effects-menu hidden="false">
|
||||
<submenu name="FabLab Chemnitz">
|
||||
<submenu name="PathOps" />
|
||||
</submenu>
|
||||
</effects-menu>
|
||||
<menu-tip>Apply Inkscape 'Intersection' path operation to multiple objects.</menu-tip>
|
||||
<object-type>all</object-type>
|
||||
</effect>
|
||||
<script>
|
||||
<command location="inx" interpreter="python">fablabchemnitz_pathops.py</command>
|
||||
</script>
|
||||
</inkscape-extension>
|
18
extensions/fablabchemnitz_pathops_union.inx
Normal file
18
extensions/fablabchemnitz_pathops_union.inx
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
|
||||
<name>Union</name>
|
||||
<id>fablabchemnitz.depath_union</id>
|
||||
<param name="ink_verb" gui-hidden="true" type="string" gui-text="Path operation">SelectionUnion</param>
|
||||
<effect needs-document="true" needs-live-preview="false">
|
||||
<effects-menu hidden="false">
|
||||
<submenu name="FabLab Chemnitz">
|
||||
<submenu name="PathOps" />
|
||||
</submenu>
|
||||
</effects-menu>
|
||||
<menu-tip>Apply Inkscape 'Union' path operation to multiple objects.</menu-tip>
|
||||
<object-type>all</object-type>
|
||||
</effect>
|
||||
<script>
|
||||
<command location="inx" interpreter="python">fablabchemnitz_pathops.py</command>
|
||||
</script>
|
||||
</inkscape-extension>
|
Reference in New Issue
Block a user