-
Notifications
You must be signed in to change notification settings - Fork 33
Derived extensions
It is straightforward to create a menu item under Inkscape's Extensions menu that invokes a hardwired Simple Inkscape Scripting script. The approach is to write an INX file that specifies default parameters for the script filename and/or literal code and invokes Simple Inkscape Scripting without prompting for any parameters.
As an example, let's say we want to create an extension that recolors all selected objects green using the following Simple Inkscape Scripting script:
for obj in selected_shapes():
clr = inkex.Color(obj.style()['fill'])
hsl = clr.to('hsl')
hsl.hue = 85 # ...because 120/360 = 85/255
rgb = hsl.to('rgb')
obj.style(fill=rgb)
As the first step in making that a standalone extension, identify your Inkscape extensions directory. From within Inkscape, go to Edit → Preferences → System and note the value of the User extensions field. (On my Linux system the Inkscape extensions directory is ~/.config/inkscape/extensions/
.) Assuming you've already installed Simple Inkscape Scripting, this directory should contain a simple_inkscape_scripting
subdirectory.
Examine the simple_inkscape_scripting.inx
file that lies within the subdirectory. It is an XML file that supplies all of the information that Inkscape needs to incorporate Simple Inkscape Scripting's functionality, including the complete layout for Simple Inkscape Scripting's user interface.
Create a new INX in the top level of your extensions directory. For our green-recoloring example, let's call this file make-green.inx
. Give the file these contents:
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Make Green</name>
<id>com.example.make-green</id>
<param name="program" type="string" gui-hidden="true">
<![CDATA[
for obj in selected_shapes():
clr = inkex.Color(obj.style()['fill'])
hsl = clr.to('hsl')
hsl.hue = 85
rgb = hsl.to('rgb')
obj.style(fill=rgb)
]]>
</param>
<effect needs-live-preview="false">
<object-type>all</object-type>
<effects-menu>
<submenu name="Color" />
</effects-menu>
</effect>
<script>
<command location="inx" interpreter="python">simple_inkscape_scripting/simpinkscr/simple_inkscape_scripting.py</command>
</script>
</inkscape-extension>
The following is a description of the interesting components of that XML. Everything else can be consider mundane boilerplate.
-
<submenu name="Color" />
specifies that the extension should appear in the Inkscape GUI under Extensions → Color. -
<name>Make Green</name>
specifies that the menu item in the Inkscape GUI should be called "Make Green". Combined with the preceding bullet, this implies that the user will invoke the extension via Extensions → Color → Make Green. -
<id>com.example.make-green</id>
is a unique ID for the extension. Among other uses, the ID serves as an action name for use in scripting Inkscape from the command line or from other extensions. Traditionally, the ID is specified as your computer's DNS name with its components in reverse order followed by the extension name. However, any string of letters, numbers, periods, dashes, and underscores should work as long as no two extensions use the same ID. (Using a DNS name helps maintain uniqueness. If your computer doesn't have its own DNS name, you might consider using your own name:last.middle.first.extension-name
.) -
<param name="program" type="string" gui-hidden="true">
…</param>
provides the code to run. It tells Simple Inkscape Scripting that the "…" is a Python program represented as a string and that Inkscape should not bring up a dialog box in the GUI to prompt the user for the program. To provide code via a file rather than literally, use instead<param type="path" name="py-source" mode="file" gui-hidden="true">
…</param>
. Literal code should be bracketed by<![CDATA[
…]]>
to avoid having to escape "<", ">", and "&" as<
,>
, and&
respectively. -
<command location="inx" interpreter="python">simple_inkscape_scripting/simpinkscr/simple_inkscape_scripting.py</command>
tells Inkscape that Simple Inkscape Scripting's main source file,simple_inkscape_scripting.py
, is written in Python and where this file can be found relative to the INX file's location.
It is possible to use Simple Inkscape Scripting as a library. This enables other Inkscape extensions to invoke Simple Inkscape Scripting functions. For example, an extension might rely primarily on the lower-level
inkex
routines but benefit occasionally from the simplicity provided by Simple Inkscape Scripting.
The recommended approach is to derive an EffectExtension
from the SimpleInkscapeScripting class. The following is the minimal code needed to implement such an extension:
from simple_inkscape_scripting.simpinkscr import *
class ExampleEffect(SimpleInkscapeScripting):
def effect(self):
# Initialize Simple Inkscape Scripting.
super().effect()
globals().update(self.script_globals)
# Use inkex and Simple Inkscape Scripting features here.
# ...
if __name__ == "__main__":
ExampleEffect().run()
The from
…import
line imports all Simple Inkscape Scripting functions, such as ellipse
, polyline
, and text
, into the extension's namespace.
Most of Simple Inkscape Scripting's initialization takes place as part of SimpleInkscapeScripting
's effect
method. In a derived extension this must be invoked explicitly with super().effect()
.
When Simple Inkscape Scripting is launched explicitly, it passes a set of global variables (canvas
, guides
, etc.) to the user's script. These are not normally available to the extension itself. If needed, they can be accessed individually through the script_global
dictionary (as in self.script_globals['canvas']
). Or, as in the code listed above, they can be imported en masse into the extension's namespace and accessed directly.
A minimal INX file corresponding to the extension code looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Example Effect</name>
<id>com.example.example-effect</id>
<effect needs-live-preview="false">
<object-type>all</object-type>
<effects-menu>
<submenu name="Render" />
</effects-menu>
</effect>
<script>
<command location="inx" interpreter="python">example-effect.py</command>
</script>
</inkscape-extension>
See the description of the INX file presented under Menu-launched scripts for a more detailed walk-through. In short, this INX file launches the example-effect.py
Python script whenever the user selects the Extensions → Render → Example Effect menu item.
Although the approach just described is the recommended way to leverage Simple Inkscape Scripting in extensions, it is also possible to use Simple Inkscape Scripting functions in extensions that are not derived from SimpleInkscapeScripting
. For example, the following extension derives from InputExtension
, a class used for importing non-SVG files into Inkscape:
import inkex
from lxml import etree
from simple_inkscape_scripting.simpinkscr import *
class ExampleInput(inkex.InputExtension):
def load(self, stream):
# Create a base SVG file.
svg = inkex.load_svg(b'''\
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://web.resource.org/cc/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
width="10cm"
height="10cm"
viewBox="0 0 1000 1000">
<defs />
<g inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" />
</svg>
''').getroot()
sis = SimpleInkscapeScripting(svg)
sis.effect()
globals().update(sis.script_globals)
# Use inkex and Simple Inkscape Scripting features here.
# ...
# Return the constructed document as an SVG string.
return etree.tostring(svg)
if __name__ == "__main__":
ExampleInput().run()
Following some code to create an empty Inkscape SVG file, the load
method creates a SimpleInkscapeScripting
object and invokes effect
on that object. Doing so suffices for initializing Simple Inkscape Scripting and granting it access to the ExampleInput
extension's SVG.
For completeness, a corresponding INX file is listed below. This tells Inkscape that files of type text/x-example
or named with a .example
extension can be loaded into Inkscape by passing them to example-input.py
.
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Example Input</name>
<id>com.example.input</id>
<input>
<extension>.example</extension>
<mimetype>text/x-example</mimetype>
<filetypename>Example input (*.example)</filetypename>
<filetypetooltip>Import an example input file</filetypetooltip>
</input>
<script>
<command location="inx" interpreter="python">example-input.py</command>
</script>
</inkscape-extension>