summaryrefslogtreecommitdiffstats
path: root/share/extensions/docs/tutorial
diff options
context:
space:
mode:
Diffstat (limited to 'share/extensions/docs/tutorial')
-rw-r--r--share/extensions/docs/tutorial/index.rst49
-rw-r--r--share/extensions/docs/tutorial/my-first-effect-extension.rst130
-rw-r--r--share/extensions/docs/tutorial/my-first-import-extension.rst305
-rw-r--r--share/extensions/docs/tutorial/my-first-text-extension.rst223
-rw-r--r--share/extensions/docs/tutorial/resources/Call_Extension_Failure.pngbin0 -> 54314 bytes
-rw-r--r--share/extensions/docs/tutorial/resources/Inkscape_Recognizes_Our_Extension.gifbin0 -> 457560 bytes
-rw-r--r--share/extensions/docs/tutorial/resources/Smiles_Format.gifbin0 -> 1015850 bytes
-rw-r--r--share/extensions/docs/tutorial/resources/Text_Ext_Moment_of_truth.gifbin0 -> 941111 bytes
-rw-r--r--share/extensions/docs/tutorial/resources/call_extension_success.gifbin0 -> 489312 bytes
-rw-r--r--share/extensions/docs/tutorial/resources/make_red_extension.zipbin0 -> 1201 bytes
-rw-r--r--share/extensions/docs/tutorial/resources/stroke-parity-widget.pngbin0 -> 20362 bytes
-rw-r--r--share/extensions/docs/tutorial/resources/stroke_parity_extension.zipbin0 -> 1628 bytes
-rw-r--r--share/extensions/docs/tutorial/resources/template_effect.zipbin0 -> 2582 bytes
-rw-r--r--share/extensions/docs/tutorial/resources/vanillin.smi1
-rw-r--r--share/extensions/docs/tutorial/simple-path-extension.rst162
15 files changed, 870 insertions, 0 deletions
diff --git a/share/extensions/docs/tutorial/index.rst b/share/extensions/docs/tutorial/index.rst
new file mode 100644
index 0000000..0e9551a
--- /dev/null
+++ b/share/extensions/docs/tutorial/index.rst
@@ -0,0 +1,49 @@
+.. _authors-tutorial:
+
+Tutorial
+========
+
+Introduction
+------------
+
+Extensions are small programs that extend Inkscape’s functionality. They
+can provide features for specific tasks, experimentation, or art styles.
+They can also add support for special hardware or different export
+formats. While many extensions are included with Inkscape, you can also
+install extensions written by third parties or write your own.
+
+This guide aims to provide you with enough information to write your own
+extension using the python programming language and a text editor. You
+should have the latest version of Inkscape installed. While Inkscape
+extensions generally work across platforms, this guide will use Linux as
+its default operating system. Linux is not required for writing
+extensions, but you may need to modify the location of files on your
+computer as you follow along. You may also need to use other “helper”
+programs, for example a different text editor, depending on your
+operating system.
+
+When a user selects an extension from the menu within Inkscape, Inkscape
+opens a pop-up dialog that presents information to the user and/or
+allows the user to enter any parameters that are necessary to use the
+extension. An extension can also be configured to run immediately
+without an intermediate dialog, for example if it does not have any
+adjustable parameters. When the user clicks **Apply** in the dialog, the
+parameters are passed to the extension program along with the svg file
+and a list of selected objects. The extension program itself then runs,
+and the data that it returns will be used to update Inkscape’s svg
+canvas or to save data to disk.
+
+We’ve made this process easier for you by creating a set of template
+extensions which you can download as the starting point for this
+tutorial. If you are a beginner we recommend that you start with the
+Effect Extension Template. Input, Output and other types of extensions
+will be covered in the more advanced topics later.
+
+.. toctree::
+ :maxdepth: 1
+
+ my-first-effect-extension
+ simple-path-extension
+ my-first-text-extension
+ my-first-import-extension
+
diff --git a/share/extensions/docs/tutorial/my-first-effect-extension.rst b/share/extensions/docs/tutorial/my-first-effect-extension.rst
new file mode 100644
index 0000000..7666288
--- /dev/null
+++ b/share/extensions/docs/tutorial/my-first-effect-extension.rst
@@ -0,0 +1,130 @@
+.. _first-effect-extension:
+
+My first effect extension
+=========================
+
+Resources
+---------
+
+- :download:`Template Files <resources/template_effect.zip>`
+- :download:`Example solution <resources/make_red_extension.zip>`
+
+Introduction
+------------
+Effect extensions take the svg from Inkscape, modify it in some way and
+pass the modified version back to Inkscape to be rendered onto the
+canvas. This can be very powerful, allowing everything from randomising
+colours to manipulating path elements in external programs.
+
+We are going to write an effect extension that will simply change the fill
+any selected object to red.
+
+Step One
+---------
+
+Extract the ``Effect Extension Template`` files into a folder on your
+computer. You should have two files, one inx file and one python file.
+Move or link these files into your extensions directory as you would
+when installing extensions manually. This is the directory listed at
+``Edit > Preferences > System: User extensions``.
+
+Edit the inx file in a text editor and change the name of the extension
+to ``Make Red Extension`` and the id to
+``org.inkscape.tutorial.make_red_extension`` by changing these lines near
+the top:
+
+.. code:: xml
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
+ <name>Make Red Extension</name>
+ <id>org.inkscape.tutorial.make_red_extension</id>
+ [...]
+
+Toward the end of the .inx file, change the submenu to ``Color``
+specify that this extension will be listed in the ``Color`` submenu
+under the ``Extensions`` menu:
+
+.. code:: xml
+
+ [...]
+ <effect>
+ <!--object-type>path</object-type-->
+ <effects-menu>
+ <submenu name="Color"/>
+ </effects-menu>
+ </effect>
+ [...]
+
+Step Two
+---------
+
+Next, open ``my_effect_extension.py`` in your text editor. You can see
+this is an inkex extension because it contains a python class that
+inherits from ``inkex.EffectExtension``. Change the class name to
+``MakeRedExtension``:
+
+.. code:: python
+
+ [...]
+ class MakeRedExtension(inkex.EffectExtension):
+ [...]
+
+Reflect this change down in the ``__main__`` section of the code by
+changing the class name to ``MakeRedExtension`` there:
+
+.. code:: python
+
+ [...]
+ if __name__ == '__main__':
+ MakeRedExtension().run()
+
+When a standard inkex-based python effect extension is run, it will call
+a method called :func:`~inkex.base.InkscapeExtension.effect` on your extension's class. So, most of the
+code you need to write will go there. Edit
+``my_effect_extension.py``\ 's ``effect()`` method to look like the
+following, **being sure that the indentation is correct so that**
+``effect()`` **is recognized as a method of the MakeRedExtension class**:
+
+.. code:: python
+
+ for elem in self.svg.selection:
+ elem.style['fill'] = 'red'
+ elem.style['fill-opacity'] = 1
+ elem.style['opacity'] = 1
+
+
+
+Code Explanation
+~~~~~~~~~~~~~~~~
+
+We want to change the color of all selected objects to red. For this we need to loop
+through each of the selected paths. The first line of :func:`inkex.base.InkscapeExtension.effect`
+does this. The :attr:`~inkex.elements._svg.SvgDocumentElement.selection` attribute of ``self.svg``
+contains the currently selected objects.
+
+.. hint::
+ ``self.svg`` contains the SVG document in its current state - passed by Inkscape - and
+ is already parsed for us, so we don't have to manipulate the XML manually. Instead, inkex offers
+ an object-oriented interface to all the SVG element types.
+
+Each element has a ``style`` attribute: it's one of the
+:attr:`~inkex.elements._base.BaseElement.WRAPPED_ATTRS` of each element, so ``elem.style`` is a
+:class:`~inkex.styles.Style` object (you can think of it as a dictionary). And in this dictionary,
+we set the value for ``'fill'`` to ``'red'``. We also set the ``'fill-opacity'`` to ``1``, in case
+the object was transparent previously.
+
+
+Final Step
+------------
+
+That’s it! There’s no need to set, save or do anything else as we’ve
+modified the style in place.
+
+Save your python script, and re-launch Inkscape. If inkscape was already
+open, close it first. You should find your new extension available in
+the ``Effect`` menu.
+
+Draw some shapes in Inkscape. Select some of the shapes and use the extension.
+The fill of all objects should change to red.
+
diff --git a/share/extensions/docs/tutorial/my-first-import-extension.rst b/share/extensions/docs/tutorial/my-first-import-extension.rst
new file mode 100644
index 0000000..6c296bd
--- /dev/null
+++ b/share/extensions/docs/tutorial/my-first-import-extension.rst
@@ -0,0 +1,305 @@
+My first import extension
+=========================
+
+Resources
+---------
+
+:download:`vanillin.smi <resources/vanillin.smi>`
+
+Introduction
+------------
+
+This article will teach you the basics of writing an Import Extension for
+Inkscape using the ``inkex`` Extensions API.
+
+Import Extensions are used to add support for a file format that
+Inkscape does not support out of the box. It is rather complex to implement an entire file format,
+but often there are already tools available to convert the file to a format that Inkscape can read,
+such as SVG or PDF. In this case, the extension depends on external programs that convert that
+specific file format to a svg that Inkscape can then read.
+
+The way these extensions are used is not through the ``Extensions`` menu
+but instead these provide options in\ ``File``>\ ``Open`` or
+``File``>\ ``Import`` dialog.
+
+The general flow of a Import Extension is as follows:
+
+1) You select the specific file format from the File Format drop down
+ menu of the ``File``>\ ``Open`` or ``File``>\ ``Import``\ dialog.
+2) You select one or more files of that specific file format. You click
+ on ``Open`` button.
+3) Inkscape will then either open the svg file in a new window or will
+ import it depending on which dialog of ``File`` menu was used.
+
+.. hint::
+
+ You can entirely skip 1) and 2) and just double click on
+ the file. If your extension is set up correctly, you won’t notice the
+ difference between opening a regular file and your file.
+
+In this article we will create an extension named
+``Organic Bond Visualizer``. It will produce the bond structure of an
+organic compound given a
+`SMILES <https://en.wikipedia.org/wiki/Simplified_molecular-input_line-entry_system>`__
+file. The extension uses an external program called ``indigo-depict``
+which has its GitHub repository
+`here <https://github.com/epam/Indigo>`__. (It is a part of the
+``indigo-utils`` package in Debian/Ubuntu repositories. For other
+platforms you might need to build it from source.)
+
+.. note:: A note on **PATH**
+
+ Since the extension depends on external programs, it is important that
+ these external programs can be found by our extensions. For that to
+ happen, either these programs **should** be on your *system PATH* or an
+ absolute path for the program should be used in the code.
+
+ Depending on the operating system, the steps required for adding
+ programs to the PATH might vary.
+
+ You can get away with setting the PATH variable entirely by using an
+ absolute path for the program but it is almost always a bad idea to
+ hard-code paths in the code.
+
+.. hint::
+
+ This article assumes you create all extension related files in the
+ User Extensions directory which is listed at
+ ``Edit``>\ ``Preferences``>\ ``System`` - ``User Extensions:`` in
+ Inkscape.
+
+Step 0 : The Boilerplate
+------------------------
+
+Like any other Inkscape extension, this extension will also have two
+files. So, create a ``organic_bond_vis.inx`` file and a
+``organic_bond_vis.py`` file.
+
+- ``organic_bond_vis.inx`` - It will have the necessary information for
+ Inkscape to be able to recognize your extension as a valid Inkscape
+ extension. It will also have the info required to present the
+ extension as an option in ``File``>\ ``Open`` or
+ ``File``>\ ``Import`` dialog.
+- ``organic_bond_vis.py`` - It will have the actual Python code your
+ extension will execute.
+
+.. hint::
+
+ There is another file that is worth mentioning here - the **test**
+ file which in our case will be ``test_organic_bond_vis.py``. It is
+ not required to be present for an extension per se, but as a best
+ practice, the extension code should always be accompanied by test
+ code.
+
+Step 1 : Populate the ``*.inx`` file
+------------------------------------
+
+.. code:: xml
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
+ <name>Organic Bond Visualizer</name>
+ <id>org.inkscape.input.bondvisualizer</id>
+ <dependency type="executable" location="path">indigo-depict</dependency>
+ <input>
+ <extension>.smi</extension>
+ <mimetype>chemical/x-daylight-smiles</mimetype>
+ <filetypename>SMILES(*.smi)</filetypename>
+ <filetypetooltip>SMILES</filetypetooltip>
+ </input>
+ <script>
+ <command location="inx" interpreter="python">organic_bond_vis.py</command>
+ </script>
+ </inkscape-extension>
+
+The lines below help Inkscape uniquely identify our extension. You
+should modify these two lines for your own extension:
+
+.. code:: xml
+
+ [...]
+ <name>Organic Bond Visualizer</name>
+ <id>org.inkscape.input.bondvisualizer</id>
+ [...]
+
+Now we declare the dependency of our extension:
+
+.. code:: xml
+
+ <dependency type="executable" location="path">indigo-depict</dependency>
+
+The ``<input>`` tag is what specifies that this extension should add a
+new format in the list of available file formats Inkscape can
+open/import.
+
+.. code:: xml
+
+ [...]
+ <input>
+ <extension>.smi</extension>
+ <mimetype>chemical/x-daylight-smiles</mimetype>
+ <filetypename>SMILES(*.smi)</filetypename>
+ <filetypetooltip>SMILES</filetypetooltip>
+ </input>
+ [...]
+
+Inside the ``<extension>`` tag we declare the suffix of our new format
+(including ``.``)
+
+Some more examples of suffixes could be ``.ps`` , ``.fig``, etc. The
+text inside ``<filetypename>`` is what appears in the
+``File``>\ ``Open`` or ``File``>\ ``Import`` dialogs.
+
+Towards the end, we add the name of our ``.py`` file inside the
+``<command>`` tag.
+
+.. code:: xml
+
+ [...]
+ <script>
+ <command location="inx" interpreter="python">organic_bond_vis.py</command>
+ </script>
+ [...]
+
+Now
+
+- Save the file
+- Close any open Inkscape windows
+- Relaunch Inkscape
+
+You should now see a ``SMILES(*.smi)`` option in the drop down menu.
+
+.. figure:: resources/Smiles_Format.gif
+ :alt: Smiles_Format
+
+ Smiles_Format
+
+If you were to select the ``vanillin.smi`` file and open it, you would
+get an error saying:
+
+.. figure:: resources/Call_Extension_Failure.png
+ :alt: Call_Extension_Failure
+
+ Call_Extension_Failure
+
+This is because we haven’t written anything in the ``.py`` file of our
+extension.
+
+Step 2 : Write the code in ``*.py`` file
+----------------------------------------
+
+First Things First
+~~~~~~~~~~~~~~~~~~
+
+To be able to use any extension functionality, you need to import the
+``inkex`` module.
+
+.. code:: python
+
+ import inkex
+
+There is a specialized class for input extensions that only call an external program to
+convert the file. Every Call Extension inherits from the :class:`~inkex.extensions.CallExtension`
+class provided by the ``inkex`` API. Let’s name our class ``OrganicBondVisualizer`` (an
+arbitrary name) and inherit the :class:`~inkex.extensions.CallExtension` class.
+
+.. code:: python
+
+ import inkex
+
+ class OrganicBondVisualizer(inkex.CallExtension):
+ #implement functionality here
+ pass
+
+Specify the formats
+~~~~~~~~~~~~~~~~~~~
+
+We now specify the input file format and output file format. (both
+without the ``.``). The output file format by default is ``svg``.
+
+.. code:: python
+
+ import inkex
+ from inkex import command
+
+ class OrganicBondVisualizer(inkex.CallExtension):
+
+ input_ext = 'smi'
+ output_ext = 'svg'
+
+ #implement functionality here
+ pass
+
+Override
+~~~~~~~~
+
+To be able to call an another program, we need to override the :func:`~inkex.extensions.CallExtension.call`
+function in our class. We need to import the :mod:`~inkex.command` module as it
+contains the actual implementation to call external programs. Inside the
+overridden method we call :mod:`~inkex.command` module’s :func:`~inkex.command.call` function.
+
+.. code:: python
+
+ import inkex
+ from inkex import command
+
+ class OrganicBondVisualizer(inkex.CallExtension):
+
+ input_ext = 'smi'
+ output_ext = 'svg'
+
+ def call(self, input_file, output_file):
+ command.call('indigo-depict', input_file, output_file)
+
+The first argument for :mod:`~inkex.command`\ ’s :func:`~inkex.command.call` function is the name of
+the external program. It can also be a path string like
+``'/usr/bin/program'``. Although internally ``inkex`` converts the names
+to path strings like these, you should not use them yourself. The reason
+being that the user of this extension may not have the command at the
+same path as the absolute path specified by us, the extension author.
+
+ **Note**: *Windows* users don’t need to specify ``.exe`` in the name
+ of the program.
+
+The ``input_file`` and ``output_file`` are path strings received by the
+:func:`~inkex.extensions.CallExtension.call` function from inkex. We then just pass these to
+:mod:`~inkex.command`\ ’s :func:`~inkex.command.call` function.
+
+Make it all happen
+~~~~~~~~~~~~~~~~~~
+
+We now just add the ``__main__`` part where the extension runs.
+
+.. code:: python
+
+ import inkex
+ from inkex import command
+
+ class OrganicBondVisualizer(inkex.CallExtension):
+
+ input_ext = 'smi'
+ output_ext = 'svg'
+
+ def call(self, input_file, output_file):
+ command.call('indigo-depict', input_file, output_file)
+
+ if __name__ == '__main__':
+ OrganicBondVisualizer().run()
+
+Now save the file.
+
+Moment of Truth
+---------------
+
+Now, we should test our extension to see in action.
+
+- Open a new Inkscape window
+- Click on the ``File``>\ ``Import`` dialog
+- Select the ``.smi`` file (An example ``.smi`` file is attached above
+ under `Resources <#resources>`__) and double-click on it. You should
+ see the following output:
+
+.. figure:: resources/call_extension_success.gif
+ :alt: Did_It_Work
+
+ Did_It_Work
diff --git a/share/extensions/docs/tutorial/my-first-text-extension.rst b/share/extensions/docs/tutorial/my-first-text-extension.rst
new file mode 100644
index 0000000..7fb9a31
--- /dev/null
+++ b/share/extensions/docs/tutorial/my-first-text-extension.rst
@@ -0,0 +1,223 @@
+My first text extension
+=======================
+
+This article will teach you the basics of writing a Text Extension for
+Inkscape using the ``inkex`` Extensions API.
+
+Introduction
+------------
+
+Any extension that manipulates text in any way is a Text Extension. Text
+Extensions can broadly be categorized in two types based on the type of
+manipulation they do to text:
+
+1. **Manipulating the text itself** - like changing case.
+2. **Changing attributes of the text** like font, color, size, etc.
+ There can also be extensions which are a hybrid of both the above
+ types.
+
+In this article we will create an extension named ``Stronger Text``. It
+is a *Type 2* Text Extension and it will make the selected text bold and
+it will also italicize it. We will also see an example of creating a
+*Type 1* Text Extension.
+
+.. hint::
+
+ This article assumes you create all files in the User Extensions
+ directory which is listed at
+ ``Edit``>\ ``Preferences``>\ ``System`` - ``User Extensions:`` in
+ Inkscape.
+
+Step 0 : The Boilerplate
+------------------------
+
+Like any other Inkscape extension, this extension will also have two files.
+So, create a ``stronger_text.inx`` file and a ``stronger_text.py`` file.
+
+- ``stronger_text.inx``: It will have the necessary information
+ for Inkscape to be able to recognize your extension as a valid
+ Inkscape extension. It will also have the declaration of any
+ interface for your extension.
+- ``stronger_text.py``: It will have
+ the actual Python code your extension will execute.
+
+.. hint::
+
+ There is another file that is worth mentioning here - the **test**
+ file which in our case will be ``test_stronger_text.py``. It is not
+ required to be present for an extension per se, but as a best
+ practice, the extension code should always be accompanied by test
+ code.
+
+Step 1 : Populate the ``*.inx`` file
+------------------------------------
+
+Our extension **Stronger Text** will have a very basic ``.inx`` file.
+
+.. code:: xml
+
+ <?xml version="1.0" encoding="UTF-8"?>
+ <inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
+ <name>Stronger Text</name>
+ <id>org.inkscape.text.strongertext</id>
+ <effect>
+ <object-type>all</object-type>
+ <effects-menu>
+ <submenu name="Text">
+ </submenu>
+ </effects-menu>
+ </effect>
+ <script>
+ <command location="inx" interpreter="python">stronger_text.py</command>
+ </script>
+ </inkscape-extension>
+
+Explanation
+~~~~~~~~~~~
+
+The lines below help Inkscape uniquely identify our extension so that it
+can be displayed under the **Extensions** menu. You should modify these
+two lines for your own extension:
+
+.. code:: xml
+
+ [...]
+ <name>Stronger Text</name>
+ <id>org.inkscape.text.strongertext</id>
+ [...]
+
+Towards the end of the ``.inx`` file, we change the submenu to ``Text``.
+It specifies that this extension should be listed in the **Text**
+submenu under the **Extensions** menu in Inkscape UI:
+
+.. code:: xml
+
+ [...]
+ <effect>
+ <object-type>all</object-type>
+ <effects-menu>
+ <submenu name="Text">
+ </submenu>
+ </effects-menu>
+ </effect>
+ [...]
+
+Now - Save the file - Close any open Inkscape windows - Relaunch
+Inkscape
+
+After successfuly completing these steps, you should see the Stronger
+Text extension in Inkscape UI.
+
+.. figure:: resources/Inkscape_Recognizes_Our_Extension.gif
+ :alt: Inkscape Recognizes Our Extension
+
+ Inkscape Recognizes Our Extension
+
+Currently it doesn’t do anything as we haven’t written anything in the
+``stronger_text.py`` file.
+
+Step 2 : Write the code in ``*.py`` file
+----------------------------------------
+
+First Things First
+^^^^^^^^^^^^^^^^^^
+
+To be able to use any extension functionality, you need to import the
+``inkex`` module.
+
+.. code:: py
+
+ import inkex
+
+Every Text Extension inherits from the :class:`~inkex.extensions.TextExtension` class provided
+by the ``inkex`` API. Let’s name our class ``StrongerText`` (an
+arbitrary name) and inherit the :class:`~inkex.extensions.TextExtension` class.
+
+.. code:: py
+
+ import inkex
+
+ class StrongerText(inkex.TextExtension):
+ #implement functionality here
+ pass
+
+The ``TextExtension`` class provides us with methods to manipulate the
+text.
+
+Our extension ``Stronger Text`` is a *Type 2* extension as it needs to
+change the attributes of text. To get access to the attributes of the
+text, we need to override the :func:`~inkex.extensions.TextExtension.process_element` method.
+
+.. code:: py
+
+ import inkex
+
+ class StrongerText(inkex.TextExtension):
+ def process_element(self, text_node):
+ text_node.style["font-weight"] = "bold"
+ text_node.style["font-style"] = "italic"
+ #text_node.style["attribute-name"] = "value"
+
+ StrongerText().run()
+
+.. _explanation-1:
+
+Explanation
+~~~~~~~~~~~
+
+| ``text_node.style`` provides us with a dictionary (technically it’s an
+ ``OrderedDict``) which we can use to change the attributes of the text
+ by using the attribute name as a key.
+| When a standard inkex-based Text Extension is run, it will call a
+ method called :func:`~inkex.extensions.TextExtension.effect` on the ``TextExtension`` class which then
+ eventually calls our(``StrongerText``\ ’s) ``process_element``
+ function. \**\* If your extension is a *Type 1* Text Extension, i.e,
+ it’s purpose is to take text from Inkscape, transform/manipulate it,
+ and return it to Inkscape, you just need to override the
+ :func:`~inkex.extensions.TextExtension.process_chardata` function like so:
+
+.. code:: py
+
+ import inkex
+
+ class UpperCase(inkex.TextExtension):
+ def process_chardata(self, text):
+ # do some more manipulations here if you want
+ return text.upper()
+
+ UpperCase().run()
+
+.. _explanation-2:
+
+Explanation
+~~~~~~~~~~~
+
+The ``text`` parameter in the :func:`~inkex.extensions.TextExtension.process_chardata` function receives
+the text from Inkscape as a Python string. In the :func:`~inkex.extensions.TextExtension.process_chardata`
+function itself, we can manipulate the string in any way to our
+liking. We then return the manipulated string.
+
+Infact, if you’ve used the ``UPPERCASE`` extension in Inkscape from
+the ``Extensions``>\ ``Text``>\ ``Change Case`` submenu, it is
+identical to the Extension we just wrote!
+
+.. hint::
+
+ This *Type 1* Extension
+ will have an almost identical ``.inx`` file with changes in places
+ like the ``<name>`` and ``<id>`` tags and a separate ``.py`` file with
+ the above code in it.
+
+Moment of Truth
+---------------
+
+Now, we should test our extension to see it in action. - Select some
+Text (preferably a font that has bold and italics styles available) -
+Click on the **Stronger Text** extension
+
+The result should appear like this:
+
+.. figure:: resources/Text_Ext_Moment_of_truth.gif
+ :alt: A gif showing that the extension works.
+
+ Did It Work? (yes, indeed)
diff --git a/share/extensions/docs/tutorial/resources/Call_Extension_Failure.png b/share/extensions/docs/tutorial/resources/Call_Extension_Failure.png
new file mode 100644
index 0000000..33629c3
--- /dev/null
+++ b/share/extensions/docs/tutorial/resources/Call_Extension_Failure.png
Binary files differ
diff --git a/share/extensions/docs/tutorial/resources/Inkscape_Recognizes_Our_Extension.gif b/share/extensions/docs/tutorial/resources/Inkscape_Recognizes_Our_Extension.gif
new file mode 100644
index 0000000..677c90a
--- /dev/null
+++ b/share/extensions/docs/tutorial/resources/Inkscape_Recognizes_Our_Extension.gif
Binary files differ
diff --git a/share/extensions/docs/tutorial/resources/Smiles_Format.gif b/share/extensions/docs/tutorial/resources/Smiles_Format.gif
new file mode 100644
index 0000000..ab5bf4b
--- /dev/null
+++ b/share/extensions/docs/tutorial/resources/Smiles_Format.gif
Binary files differ
diff --git a/share/extensions/docs/tutorial/resources/Text_Ext_Moment_of_truth.gif b/share/extensions/docs/tutorial/resources/Text_Ext_Moment_of_truth.gif
new file mode 100644
index 0000000..3ecc301
--- /dev/null
+++ b/share/extensions/docs/tutorial/resources/Text_Ext_Moment_of_truth.gif
Binary files differ
diff --git a/share/extensions/docs/tutorial/resources/call_extension_success.gif b/share/extensions/docs/tutorial/resources/call_extension_success.gif
new file mode 100644
index 0000000..d1ed5c6
--- /dev/null
+++ b/share/extensions/docs/tutorial/resources/call_extension_success.gif
Binary files differ
diff --git a/share/extensions/docs/tutorial/resources/make_red_extension.zip b/share/extensions/docs/tutorial/resources/make_red_extension.zip
new file mode 100644
index 0000000..6873a9b
--- /dev/null
+++ b/share/extensions/docs/tutorial/resources/make_red_extension.zip
Binary files differ
diff --git a/share/extensions/docs/tutorial/resources/stroke-parity-widget.png b/share/extensions/docs/tutorial/resources/stroke-parity-widget.png
new file mode 100644
index 0000000..48d8b9b
--- /dev/null
+++ b/share/extensions/docs/tutorial/resources/stroke-parity-widget.png
Binary files differ
diff --git a/share/extensions/docs/tutorial/resources/stroke_parity_extension.zip b/share/extensions/docs/tutorial/resources/stroke_parity_extension.zip
new file mode 100644
index 0000000..06b0e7b
--- /dev/null
+++ b/share/extensions/docs/tutorial/resources/stroke_parity_extension.zip
Binary files differ
diff --git a/share/extensions/docs/tutorial/resources/template_effect.zip b/share/extensions/docs/tutorial/resources/template_effect.zip
new file mode 100644
index 0000000..be5a051
--- /dev/null
+++ b/share/extensions/docs/tutorial/resources/template_effect.zip
Binary files differ
diff --git a/share/extensions/docs/tutorial/resources/vanillin.smi b/share/extensions/docs/tutorial/resources/vanillin.smi
new file mode 100644
index 0000000..964c95e
--- /dev/null
+++ b/share/extensions/docs/tutorial/resources/vanillin.smi
@@ -0,0 +1 @@
+COc1cc(C=O)ccc1O
diff --git a/share/extensions/docs/tutorial/simple-path-extension.rst b/share/extensions/docs/tutorial/simple-path-extension.rst
new file mode 100644
index 0000000..143b6b1
--- /dev/null
+++ b/share/extensions/docs/tutorial/simple-path-extension.rst
@@ -0,0 +1,162 @@
+A more complex example: reading path data and changing the style
+================================================================
+
+.. note::
+ This tutorial builds on the files created in :ref:`first-effect-extension`.
+
+Resources
+---------
+
+- :download:`Example solution <resources/stroke_parity_extension.zip>`
+
+Introduction
+------------------
+
+We are going to write an effect extension that will change any path with
+an even number of nodes to a user-defined stroke and any path with an odd
+number of nodes to a different user-defined stroke color.
+
+From :ref:`first-effect-extension`, we already know how to create the extension boilerplate
+(without parameters), how to iterate over the selection and how to modify the style.
+
+This extension adds two more things: Paths and Parameters.
+
+Setting up the inx file
+-----------------------
+
+Duplicate the files from the `make_red_extension`, and change all the filenames, class names
+and extension ID to `stroke_parity_extension`.
+
+Now for the parameters for the colors. We're also going to add a checkbox to remove the fill of the
+selected paths. The params are defined after ``<id>`` and before ``<effect>`` in the inx file:
+
+.. code:: xml
+
+ <param name="remove_fill" type="bool" gui-text="Remove fill">false</param>
+ <label appearance="header">Stroke color for path based on number of nodes</label>
+ <param name="tab" type="notebook">
+ <page name="even" gui-text="Even">
+ <param name="even_color" type="color" gui-text="Even stroke color:">4278190335</param>
+ </page>
+ <page name="odd" gui-text="Odd">
+ <param name="odd_color" type="color" gui-text="Odd stroke color:">65535</param>
+ </page>
+ </param>
+
+The default colors are defined as content of the ``<param>`` element, and they are passed as
+integers in RGBA format, so for a "red" color (i.e. ``#ff000ff``), one has to write
+``256^3*255 + 255 = 4278190335``.
+
+Color widgets are quite large, so the two widgets are added in a notebook (i.e. two tabs).
+
+You can also change the submenu the extension will be listed in to ``Modify Path``.
+
+Restart Inkscape. If you click the extension in the menu, it should look something like this:
+
+.. figure:: resources/stroke-parity-widget.png
+
+Adding the parameters to the python file
+----------------------------------------
+
+When Inkscape calls the extension in the current state, an error message is shown::
+
+ usage: stroke_parity_extension.py [-h] [--output OUTPUT] [--id IDS]
+ [--selected-nodes SELECTED_NODES]
+ [INPUT_FILE]
+ stroke_parity_extension.py: error: unrecognized arguments: --remove_fill=false --tab=even --even_color=4278190335 --odd_color=65535
+
+We also have to tell the ``MakeRedExtension`` class how to parse the parameters. This is done in the
+:func:`~inkex.base.InkscapeExtension.add_arguments` method. We have four parameters: the two colors,
+the checkbox and the currently selected tab.
+
+.. code:: python
+
+ def add_arguments(self, pars):
+ pars.add_argument("--even_color", type=inkex.Color, default=inkex.Color("red"))
+ pars.add_argument("--odd_color", type=inkex.Color, default=inkex.Color("blue"))
+ pars.add_argument("--remove_fill", type=inkex.Boolean, default=False)
+ pars.add_argument("--tab", type=str, default="stroke")
+
+In ``effect``, these parameters are available as e.g. ``self.options.even_color``.
+
+.. hint::
+
+ :func:`inkex.Boolean <inkex.utils.Boolean>` and :class:`inkex.Color <inkex.colors.Color>` are
+ special types that preprocess the parsed parameter, so that ``self.options.even_color`` is an
+ :class:`inkex.colors.Color` object and not the string ``"4278190335"``.
+
+Processing the paths
+--------------------
+
+Modify the ``effect`` method as follows:
+
+.. code-block:: python
+ :linenos:
+
+ def effect(self):
+ for elem in self.svg.selection.filter(inkex.PathElement):
+ elem.set('inkscape:modified_by_tutorial', 'Yes')
+ elem.style['stroke-width'] = 2.0
+ if len(elem.path) % 2: # odd number
+ elem.style.set_color(self.options.odd_color, 'stroke')
+ else:
+ elem.style.set_color(self.options.even_color, 'stroke')
+
+ if self.options.remove_fill:
+ elem.style["fill"] = None
+
+Code Explanation
+~~~~~~~~~~~~~~~~
+
+Firstly, we need to loop through each of the selected paths. We already now how to do this
+from the first tutorial, but we now filter the selection to only contain
+:class:`~inkex.elements._polygons.PathElement` objects - since we want to count the number of nodes.
+If other objects, such as a text object or rectangle, are selected, they are ignored. So, for each
+iteration of the loop, ``elem`` will contain one of the selected path
+objects.
+
+The second line sets an attribute ``inkscape:modified_by_tutorial`` on
+the xml element ``elem``. The attribute API will handle the ``inkscape``
+namespace for us, so we can use a simple colon to indicate the
+namespace. This is how all non-special attributes are set and gotten.
+But on top of this simple API we don’t have to worry about parsing the
+path, transform or the style attributes. Instead the inkex API does all
+the parsing for us and provides us with a way to change styles, modify
+paths and even do transformations without manual parsing.
+
+Then we set a stroke-width of 2, using the standard style API which is
+assigning into a type of ordered dictionary.
+
+Next we use the Path API to get the path data of the element, using
+:attr:`~inkex.elements._base.ShapeElement.path`. This is an :class:`inkex.paths.Path` object,
+which is a list of all path commands (such as Moveto, Lineto, Curveto...). We can use the length
+of the list to determine the number of segments, which is (for the simple case of open paths) equal
+to the number of nodes.
+
+We then use the Color API to assign the correct stroke color. The parameters are passed in RGBA format,
+and we can use the :func:`~inkex.styles.Style.set_color` function to set opacity and stroke at the same time.
+Stroke and stroke opacity are two different style properties, so we can’t set them with a simple
+assignment.
+
+Last we set the fill to ``None`` (``"none"`` would work as well) - if the checkbox for this is
+checked.
+
+Testing the extension
+---------------------
+
+As we've already learned, there’s no need to set, save or do anything else as we’ve
+modified the style in place.
+
+Save your python script, and re-launch Inkscape. If inkscape was already
+open, close it first. You should find your new extension available in
+the ``Modify Path`` menu.
+
+Draw some shapes with the pen tool, select some of the shapes and use the extension.
+You should see the stroke color change for each of the objects selected.
+
+.. note::
+ For closed paths, the extension gives incorrect results, because the closing "Z" / "z"
+ command does not add a new node, but is counted by ``len(path)``.
+
+ This can be avoided by manually counting segments except that are not ZoneClose (zoneClose) commands.
+