<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.inkscape.org/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Terry+brown</id>
	<title>Inkscape Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.inkscape.org/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Terry+brown"/>
	<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/Special:Contributions/Terry_brown"/>
	<updated>2026-04-08T08:02:23Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.36.1</generator>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=Working_with_Bazaar&amp;diff=55397</id>
		<title>Working with Bazaar</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=Working_with_Bazaar&amp;diff=55397"/>
		<updated>2009-12-28T20:22:45Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: update workflow to protect linearity of bzr log&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Bazaar concepts==&lt;br /&gt;
&lt;br /&gt;
It is assumed that you are familiar with the basic concepts of version control, like working copy, committing, updating, conflicts. There will be a section that explains the basic functionality soon.&lt;br /&gt;
&lt;br /&gt;
* '''Branch''' is a working copy of the project's code. Typically you use one branch per set of related changes, for example a new output extension. Once your work is finished, you ''merge'' your branch into a larger project. Initially, there is only one branch, the trunk; all other branches are its (probably indirect) descendants. To create a new branch, you have to copy an existing one.&lt;br /&gt;
* '''Checkout''' is a copy of code contained in a branch that is not stored on your computer (a remote branch). Committing to a checkout will immediately apply the changes to the remote branch. Commits will not succeed if somebody else&lt;br /&gt;
* When branches A and B have a common ancestor branch but each contain changes not present in the other, they have '''diverged'''. For example, when you work on an improved rectangle tool in your own branch and at the same time somebody else applies a PDF export bugfix to the trunk, your rectangle tool branch becomes diverged from the trunk.&lt;br /&gt;
* '''Trunk''' is the main branch, which represents cutting-edge working code. You should start from it when doing any new development.&lt;br /&gt;
* '''Merge''' is the process of reconciling changes made between two branches since they diverged. This operation is asymmetric. When you merge A into B, all changes made to A since it was branched from B are applied to B. A is not changed. When you work on some feature, you typically work in a branch, periodically merging the trunk into your branch (to sync up with the latest changes), then you merge your work into the trunk. Merging is similar to applying a patch - it only changes your working copy. To apply a merge, you need to commit the changes it introduced. Merging un-diverges branches.&lt;br /&gt;
* '''Repository''' is a place where branches of a project are stored. Having a shared repository reduces the storage requirements of multiple branches of one project. Instead of O(number of branches) space, they take roughly O(size of changes).&lt;br /&gt;
* '''Bind''' is the process of converting a non-diverged local branch into a checkout.&lt;br /&gt;
* '''Push''' is the process of publishing an exact copy (a mirror) of your branch in some other location. The difference between pushing and checkouts is that a checked out remote branch is updated every time you commit, while a pushed remote branch is updated only when you push to it. You can only push to a branch if the mirror has not diverged from your local copy. This can happen if more than 1 person can push to the same location.&lt;br /&gt;
* '''Pull''' is the process of creating a local exact copy (mirror) of a branch, in principle a reverse of pushing.&lt;br /&gt;
&lt;br /&gt;
==First steps==&lt;br /&gt;
&lt;br /&gt;
First you need to tell Bazaar your name. This will appear on all your commits. You should use your real e-mail, but you can obfuscate it if you are afraid of spam.&lt;br /&gt;
&lt;br /&gt;
Obfuscated e-mail example:&lt;br /&gt;
 $ bzr whoami &amp;quot;John Q. Public &amp;lt;john dot q dot public at-sign bigmail dot com&amp;gt;&amp;quot;&lt;br /&gt;
Unobfuscated e-mail example: &lt;br /&gt;
 $ bzr whoami &amp;quot;John Q. Public &amp;lt;john.q.public@bigmail.com&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If you have an account on launchpad and want to commit changes there, you need to specify your launchpad login:&lt;br /&gt;
 $ bzr launchpad-login johnq&lt;br /&gt;
&lt;br /&gt;
Then fetch Inkscape's trunk&lt;br /&gt;
 $ bzr branch lp:inkscape&lt;br /&gt;
&lt;br /&gt;
==Using a decentralized workflow==&lt;br /&gt;
&lt;br /&gt;
In this case you will be working locally until you are ready to publish your changes. This is the way git works by default.&lt;br /&gt;
&lt;br /&gt;
'''NOTE:''' this workflow has been updated to avoid the &amp;quot;other people's commits become subrevisions of your commit&amp;quot; problem.&lt;br /&gt;
&lt;br /&gt;
Get Inkscape's code&lt;br /&gt;
 $ bzr branch lp:inkscape myproject&lt;br /&gt;
&lt;br /&gt;
Now work:&lt;br /&gt;
 $ cd myproject&lt;br /&gt;
 &amp;lt;work work work&amp;gt;&lt;br /&gt;
 $ bzr commit -m &amp;quot;Some changes&amp;quot;&lt;br /&gt;
 &amp;lt;more work work work&amp;gt;&lt;br /&gt;
 $ bzr commit -m &amp;quot;More changes&amp;quot;&lt;br /&gt;
 &amp;lt;create new file&amp;gt;&lt;br /&gt;
 $ bzr add new-file.c&lt;br /&gt;
&lt;br /&gt;
Now, you need to merge your changes into a local copy of the trunk.  If you don't have one, do (in the directory ''containing'' myproject):&lt;br /&gt;
  $ bzr branch lp:inkscape trunk&lt;br /&gt;
  $ cd trunk&lt;br /&gt;
&lt;br /&gt;
If you already have one on hand, make sure it's up to date:&lt;br /&gt;
  $ cd trunk&lt;br /&gt;
  $ bzr pull&lt;br /&gt;
&lt;br /&gt;
Now that you have a local copy of the current trunk:&lt;br /&gt;
  $ bzr merge ../myproject&lt;br /&gt;
  $ bzr commit -m'added my feature'&lt;br /&gt;
  $ bzr push&lt;br /&gt;
&lt;br /&gt;
==Using a centralized (SVN-like) workflow==&lt;br /&gt;
&lt;br /&gt;
In this case, every commit that you make is immediately sent to the central repository. There are two ways of achieving this:&lt;br /&gt;
&lt;br /&gt;
===SVN-style checkout===&lt;br /&gt;
&lt;br /&gt;
Get the latest Inkscape code as a checkout, rather than a branch:&lt;br /&gt;
 $ bzr checkout lp:inkscape&lt;br /&gt;
&lt;br /&gt;
Now work as in SVN:&lt;br /&gt;
  &amp;lt;do work&amp;gt;&lt;br /&gt;
 $ bzr commit&lt;br /&gt;
  &amp;lt;error, someone else has changed things&amp;gt;&lt;br /&gt;
 $ bzr update&lt;br /&gt;
  &amp;lt;check all is okay&amp;gt;&lt;br /&gt;
 $ bzr commit&lt;br /&gt;
&lt;br /&gt;
If you add new files, add them to version control with &amp;lt;tt&amp;gt;bzr add ''file''&amp;lt;/tt&amp;gt;. You can recursively add all new files by writing simply &amp;lt;tt&amp;gt;bzr add&amp;lt;/tt&amp;gt; in the top level directory.&lt;br /&gt;
&lt;br /&gt;
===Binding a BZR branch===&lt;br /&gt;
&lt;br /&gt;
Get Inkscape's code the regular bzr way:&lt;br /&gt;
 $ bzr branch lp:inkscape&lt;br /&gt;
&lt;br /&gt;
Then transform your banch into a checkout:&lt;br /&gt;
 $ cd inkscape&lt;br /&gt;
 $ bzr bind lp:inkscape&lt;br /&gt;
&lt;br /&gt;
Now work as in SVN:&lt;br /&gt;
  &amp;lt;do work&amp;gt;&lt;br /&gt;
 $ bzr commit&lt;br /&gt;
  &amp;lt;error, someone else has changed things&amp;gt;&lt;br /&gt;
 $ bzr update&lt;br /&gt;
  &amp;lt;check all is okay&amp;gt;&lt;br /&gt;
 $ bzr commit&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Differences from SVN===&lt;br /&gt;
&lt;br /&gt;
* Commits will fail if your checkout is not up to date, even if there would be no conflicts.&lt;br /&gt;
* The revision number is an attribute of the working tree, not of individual files. It is not possible to have different portions of the working tree at different revisions.&lt;br /&gt;
* &amp;lt;tt&amp;gt;bzr commit ''file''&amp;lt;/tt&amp;gt; works like &amp;lt;tt&amp;gt; svn commit ''file'' &amp;amp;&amp;amp; svn update ''project-root''&amp;lt;/tt&amp;gt;. After a successful partial commit, the entire tree's revision is increased by 1.&lt;br /&gt;
* No special server is needed to publish a branch. You can push branches to anywhere, including simple FTP shares. In our scenario, Launchpad works like a central repository, but it's not required to publish everything there.&lt;br /&gt;
* You create branches locally. To simulate creating a SVN-style branch on Launchpad, you need to create a local branch, push it, then bind it to the pushed location.&lt;br /&gt;
&lt;br /&gt;
==Using branches==&lt;br /&gt;
This will asumme this directory layout:&lt;br /&gt;
 inkscape&lt;br /&gt;
  +-trunk&lt;br /&gt;
  |  +-doc&lt;br /&gt;
  |  +-share&lt;br /&gt;
  |  +-src&lt;br /&gt;
  |  +-po&lt;br /&gt;
  |  +-...&lt;br /&gt;
  +-my-branch&lt;br /&gt;
  +-some-other-branch&lt;br /&gt;
&lt;br /&gt;
===Repository setup===&lt;br /&gt;
If you are working on several things at once, it's better to use one branch per feature. Here is how to work with branches.&lt;br /&gt;
&lt;br /&gt;
* Create a shared repository for your branches. This brings significant space savings if you have several branches, but is not mandatory: &amp;lt;tt&amp;gt;bzr init-repo --rich-root inkscape&amp;lt;/tt&amp;gt;. This will create the directory &amp;lt;tt&amp;gt;inkscape&amp;lt;/tt&amp;gt;, which will contain a shared repository used by all branches within it, in the rich root format. This is the format currently used by our Launchpad repository - if you choose another format, you might not be able to push your branches to Launchpad. Enter the directory just created: &amp;lt;tt&amp;gt;cd inkscape&amp;lt;/tt&amp;gt;&lt;br /&gt;
* Create a new branch: &amp;lt;tt&amp;gt;bzr branch lp:inkscape my-branch&amp;lt;/tt&amp;gt;. This will retrieve the main branch of Inkscape and put it in the subdirectory &amp;lt;tt&amp;gt;my-branch&amp;lt;/tt&amp;gt;.&lt;br /&gt;
* Make changes. Commit them locally using &amp;lt;tt&amp;gt;bzr commit&amp;lt;/tt&amp;gt;. Note that this doesn't touch the trunk on Launchpad; you are only committing to your local repository.&lt;br /&gt;
* If somebody else modified the trunk, resync with it: &amp;lt;tt&amp;gt;bzr merge&amp;lt;/tt&amp;gt; (merging will automatically use the parent branch if none is specified)&lt;br /&gt;
&lt;br /&gt;
===Publishing branches on Launchpad===&lt;br /&gt;
To publish branches on Launchpad, you have to add an SSH key to your account. You can generate a key using the program &amp;lt;tt&amp;gt;ssh-keygen&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
When you added a key to your account, you can now use the Launchpad integration features.&lt;br /&gt;
&lt;br /&gt;
* First you have to log in: &amp;lt;tt&amp;gt;bzr launchpad-login ''launchpad-username''&amp;lt;/tt&amp;gt;. You need to use your username, not your display name.&lt;br /&gt;
* Publish your branch on Launchpad: &amp;lt;tt&amp;gt;bzr push lp:~''launchpad-username''/inkscape/my-branch&amp;lt;/tt&amp;gt;. Of course if you're working with a different project, substitute &amp;lt;tt&amp;gt;inkscape&amp;lt;/tt&amp;gt; with its Launchpad name.&lt;br /&gt;
* The push location will be saved. To update the branch on Launchpad, write &amp;lt;ttbzr push&amp;lt;/tt&amp;gt; after your commits.&lt;br /&gt;
* If you want to always commit directly to the remote branch on Launchpad, write &amp;lt;tt&amp;gt;bzr bind lp:~''launchpad-username''/inkscape/my-branch&amp;lt;/tt&amp;gt; after the initial push. This will convert your local branch into a checkout of the remote branch.&lt;br /&gt;
&lt;br /&gt;
===Merging branches into trunk===&lt;br /&gt;
&lt;br /&gt;
Way A: commit to a checkout of the trunk. This requires a trunk checkout, but for some people is more logical.&lt;br /&gt;
&lt;br /&gt;
* Navigate to a checkout of the trunk.&lt;br /&gt;
* Execute &amp;lt;tt&amp;gt;bzr merge ''branch-url''&amp;lt;/tt&amp;gt;. This will modify the files to receive the changes from the specified branch but will not modify the trunk.&lt;br /&gt;
* After verifying the changes (e.g. compile the program and see whether it doesn't crash on startup), execute &amp;lt;tt&amp;gt;bzr commit&amp;lt;/tt&amp;gt; to apply the changes from the merge to the trunk.&lt;br /&gt;
&lt;br /&gt;
Way B: merge from trunk, then push. It doesn't require a trunk checkout, but it may be less obvious what is happening.&lt;br /&gt;
&lt;br /&gt;
* Navigate to the branch you want to merge.&lt;br /&gt;
* Execute &amp;lt;tt&amp;gt;bzr merge ''trunk-location''&amp;lt;/tt&amp;gt;, where ''trunk-location'' might be your local trunk checkout or &amp;lt;tt&amp;gt;lp:inkscape&amp;lt;/tt&amp;gt;&lt;br /&gt;
* Verify the changes, then execute &amp;lt;tt&amp;gt;bzr commit&amp;lt;/tt&amp;gt;.&lt;br /&gt;
* Push to trunk: &amp;lt;tt&amp;gt;bzr push lp:inkscape&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Local branching===&lt;br /&gt;
&lt;br /&gt;
Naturally, all this also works locally. For example, when you're in the &amp;lt;tt&amp;gt;inkscape&amp;lt;/tt&amp;gt; directory, you can write &amp;lt;tt&amp;gt;bzr branch trunk export-dialog&amp;lt;/tt&amp;gt; to create a new branch of the trunk called &amp;lt;tt&amp;gt;export-dialog&amp;lt;/tt&amp;gt;, where you'll work only on improving the export dialog. Similarly, you can merge between local branches.&lt;br /&gt;
&lt;br /&gt;
==Best Practicals for Inkscape Project==&lt;br /&gt;
&lt;br /&gt;
===Registering Bugfixes===&lt;br /&gt;
&lt;br /&gt;
Launchpad will automatically mark a bug &amp;quot;Fix Available&amp;quot; (rather than &amp;quot;Fix Committed&amp;quot;) once somebody commits using the flag --fixes, e.g. (if you fix those two bugs in one commit):&lt;br /&gt;
 bzr commit --fixes lp:123456 --fixes lp:123457 -m 'patch description'&lt;br /&gt;
Then, bugs can be changed automatically from &amp;quot;Fix Available&amp;quot; to &amp;quot;Fix Released&amp;quot;&lt;br /&gt;
&lt;br /&gt;
[http://doc.bazaar-vcs.org/bzr.dev/en/tutorials/using_bazaar_with_launchpad.html#changing-the-state-in-launchpad-while-committing-in-bazaar Read more: &amp;quot;Changing the state in Launchpad while committing in Bazaar&amp;quot;]&lt;br /&gt;
&lt;br /&gt;
==External links==&lt;br /&gt;
* [http://doc.bazaar-vcs.org/latest/en/ Bazaar manual]&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=File:Extrudedemo.jpg&amp;diff=34444</id>
		<title>File:Extrudedemo.jpg</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=File:Extrudedemo.jpg&amp;diff=34444"/>
		<updated>2008-08-14T20:51:11Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=ExtrudeEffect&amp;diff=34434</id>
		<title>ExtrudeEffect</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=ExtrudeEffect&amp;diff=34434"/>
		<updated>2008-08-14T20:50:46Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: /* Extrude effect */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Extrude effect&lt;br /&gt;
&lt;br /&gt;
To use the extrude effect, you just select two paths and run the effect&lt;br /&gt;
from the effects menu.&lt;br /&gt;
&lt;br /&gt;
To get the extrude effect copy&lt;br /&gt;
[[http://www.greygreen.org/files/inkscape/extrude/extrude.inx extrude.inx]]&lt;br /&gt;
and&lt;br /&gt;
[[http://www.greygreen.org/files/inkscape/extrude/extrude.py extrude.py]]&lt;br /&gt;
into your Inkscape extensions directory.&lt;br /&gt;
&lt;br /&gt;
[[Image:extrudedoc.jpg]]&lt;br /&gt;
&lt;br /&gt;
Here's another example:&lt;br /&gt;
&lt;br /&gt;
[[Image:extrudedemo.jpg]]&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=ExtrudeTool&amp;diff=34424</id>
		<title>ExtrudeTool</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=ExtrudeTool&amp;diff=34424"/>
		<updated>2008-08-14T20:26:52Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Aim ==&lt;br /&gt;
To have a simple way to give a profile path depth, using the mouse to manipuate the type of extrusion, similar to functionality in the 3D Box tool and in Blender.&lt;br /&gt;
&lt;br /&gt;
== Status ==&lt;br /&gt;
&lt;br /&gt;
[[ExtrudeEffect]] implements something like this, although it doesn't handle&lt;br /&gt;
shading (as shown below) automatically.  At time of writing [[ExtrudeEffect]]&lt;br /&gt;
is in SVN and accessed as &amp;quot;Extrude2&amp;quot; on the &amp;quot;Generate from Path&amp;quot; Effects menu.&lt;br /&gt;
The older &amp;quot;Extrude&amp;quot; effect only connects points with lines, and the shape&lt;br /&gt;
at either end has to be identical.&lt;br /&gt;
&lt;br /&gt;
== Concept ==&lt;br /&gt;
&lt;br /&gt;
[[Image:Extrude.png]]&lt;br /&gt;
&lt;br /&gt;
== UI Bits and pieces ==&lt;br /&gt;
*Toggle button to specify if extrusion path replaces original or add to the document&lt;br /&gt;
*Radio buttons or drop-down select to choose shading options (darken extrusion, lighte extrusion&lt;br /&gt;
*Slider to control amount/subtlty of shading (how dark/light are the extrusions made, etc.)&lt;br /&gt;
* Options/defaults in preferences dialogue?&lt;br /&gt;
&lt;br /&gt;
== Useful code ==&lt;br /&gt;
tbnorth on IRC provided a [http://pastebin.com/f3f5c04d9 python extension] and [http://pastebin.com/f6fdc825d .inx file] which 'draws lines between vertices of two selected paths', which are archived at pastebin.&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=ExtrudeEffect&amp;diff=34414</id>
		<title>ExtrudeEffect</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=ExtrudeEffect&amp;diff=34414"/>
		<updated>2008-08-14T19:33:39Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: /* Extrude effect */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Extrude effect =&lt;br /&gt;
&lt;br /&gt;
To use the extrude effect, you just select two paths and run the effect&lt;br /&gt;
from the effects menu.&lt;br /&gt;
&lt;br /&gt;
To get the extrude effect copy&lt;br /&gt;
[[http://www.greygreen.org/files/inkscape/extrude/extrude.inx extrude.inx]]&lt;br /&gt;
and&lt;br /&gt;
[[http://www.greygreen.org/files/inkscape/extrude/extrude.py extrude.py]]&lt;br /&gt;
into your Inkscape extensions directory.&lt;br /&gt;
&lt;br /&gt;
[[Image:extrudedoc.jpg]]&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=ExtrudeEffect&amp;diff=34404</id>
		<title>ExtrudeEffect</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=ExtrudeEffect&amp;diff=34404"/>
		<updated>2008-08-14T19:22:39Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: /* Extrude effect */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Extrude effect =&lt;br /&gt;
&lt;br /&gt;
To use the extrude effect, you just select two paths and run the effect&lt;br /&gt;
from the effects menu.&lt;br /&gt;
&lt;br /&gt;
[[Image:extrudedoc.jpg]]&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=File:Extrudedoc.jpg&amp;diff=34394</id>
		<title>File:Extrudedoc.jpg</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=File:Extrudedoc.jpg&amp;diff=34394"/>
		<updated>2008-08-14T19:22:04Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=ExtrudeEffect&amp;diff=34384</id>
		<title>ExtrudeEffect</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=ExtrudeEffect&amp;diff=34384"/>
		<updated>2008-08-14T19:20:21Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: /* Extrude effect */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Extrude effect =&lt;br /&gt;
&lt;br /&gt;
To use the extrude effect, you just select two paths and run the effect&lt;br /&gt;
from the effects menu.&lt;br /&gt;
&lt;br /&gt;
[[Image:extrudedoc.png]]&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=ExtrudeEffect&amp;diff=34374</id>
		<title>ExtrudeEffect</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=ExtrudeEffect&amp;diff=34374"/>
		<updated>2008-08-14T19:20:00Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Extrude effect =&lt;br /&gt;
&lt;br /&gt;
To use the extrude effect, you just select two paths and run the effect&lt;br /&gt;
from the effects menu.&lt;br /&gt;
&lt;br /&gt;
[[Image:Example.jpg]]&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=ExtrudeEffect&amp;diff=34364</id>
		<title>ExtrudeEffect</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=ExtrudeEffect&amp;diff=34364"/>
		<updated>2008-08-14T19:19:27Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: New page: To use the extrude effect, you just select two paths and run the effect from the effects menu.  Image:Example.jpg&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;To use the extrude effect, you just select two paths and run the effect&lt;br /&gt;
from the effects menu.&lt;br /&gt;
&lt;br /&gt;
[[Image:Example.jpg]]&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=Extension_repository&amp;diff=34354</id>
		<title>Extension repository</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=Extension_repository&amp;diff=34354"/>
		<updated>2008-08-14T19:18:22Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;It would be very useful to have a central Internet repository for Inkscape extensions, similarly as Firefox has. This way, Inkscape could update installed extensions from the web site, with no need for the user to know if an extension is updated.&lt;br /&gt;
&lt;br /&gt;
What extensions are there? We can start listing them here:&lt;br /&gt;
* [http://www.ekips.org/comp/inkscape/ Inkscape Effects] - Aaron Spikes set of extensions is now included in Inkscape.&lt;br /&gt;
* [[ExtrudeEffect]] - extrude polygons to &amp;quot;3d&amp;quot;, also make &amp;quot;string art&amp;quot;&lt;br /&gt;
* [http://www.colibre.com.br/bin/view/Aurium/InkscapeCalendarShellScript InkscapeCalendarShellScript] - Create Calendars sucks or is impracticable if you do not have something that automatizes the process of to put the days organized in blocks of months to you. To format manually is easy, but this extension can help you on this too [Bash script, works for win32 with cygwin].&lt;br /&gt;
* [http://www.colibre.com.br/bin/view/Aurium/InkscapeAreaCuter InkscapeAreaCutter] - For Webdesigners, layout for sites, slice area into .png images. util! (pt_BR)&lt;br /&gt;
* [http://julvitard.free.fr/eqtexsvg/ EQTeXSVG] - EQTeXSVG is an extension for Inkscape used to convert an inline LATEX equation into SVG path using Python.&lt;br /&gt;
* [http://www.kono.cis.iwate-u.ac.jp/~arakit/inkscape/inklatex.html InkLaTeX] Insert LaTeX text or equations into Inkscape.&lt;br /&gt;
* [http://www.iki.fi/pav/software/textext/ TexText] Embed re-editable LaTeX objects in SVG drawings.&lt;br /&gt;
* [http://math.univ-lille1.fr/~barraud/Inkscape/pathdeform/ PathDeform] - Here is an Inkscape extension whose purpose is to bend a path according to another one. Note: it was added to Inkscape 0.45 as &amp;quot;Pattern along Path&amp;quot;.&lt;br /&gt;
* [http://www.inkbar.lineaire.net/ InkBar] - This is an Inkscape extension whose purpose is to draw EAN13 bar code.&lt;br /&gt;
* [http://technoargia.free.fr/swftools/ SWF output] - A little extension to save as Swf from Inkscape.&lt;br /&gt;
* [http://www.colivre.coop.br/bin/view/Aurium/Puff Puff] - Fluffs elements! :-D&lt;br /&gt;
* [http://www.colivre.coop.br/bin/view/Aurium/InkSudoku Sudoku Generator] - Generates Sudoku square with the solution ''(in a small square if you want)''.&lt;br /&gt;
* [[CalligraphedOutlineFill]] - helps to fill in the inside area of shapes drawn with the Calligraphy tool&lt;br /&gt;
* [http://edlab.die.unipd.it/Site4.html SlotStar] - helps to draw the star of slot of a winding of an electrical machine&lt;br /&gt;
* [http://richard.henwood.googlepages.com/inkscapelatexextension inkscapeLatexExtension] - Extract text in an inkscape drawing to a latex picture environment.&lt;br /&gt;
* [http://saws.googlecode.com s.a.w.s] - export the svg Inkscape file in valid xhtml / css files.&lt;br /&gt;
&lt;br /&gt;
== For Programers: ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.colivre.coop.br/bin/view/Aurium/RubyInk RubyInk] - Inkscape extension with Ruby&lt;br /&gt;
* [http://www.colivre.coop.br/bin/view/Aurium/InkBash Ink-Bash] - &amp;lt;nowiki&amp;gt;ShellScript&amp;lt;/nowiki&amp;gt; Forever!&lt;br /&gt;
* [http://www.colivre.coop.br/bin/view/Aurium/InkMoz InkMoz] - the Bridge from Inkscape to Mozilla (Inkscape extension with Javascript and more)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Developer Documentation]]&lt;br /&gt;
[[Category:Extensions]]&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=Extension_repository&amp;diff=34344</id>
		<title>Extension repository</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=Extension_repository&amp;diff=34344"/>
		<updated>2008-08-14T19:13:39Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;It would be very useful to have a central Internet repository for Inkscape extensions, similarly as Firefox has. This way, Inkscape could update installed extensions from the web site, with no need for the user to know if an extension is updated.&lt;br /&gt;
&lt;br /&gt;
What extensions are there? We can start listing them here:&lt;br /&gt;
* [http://www.ekips.org/comp/inkscape/ Inkscape Effects] - Aaron Spikes set of extensions is now included in Inkscape.&lt;br /&gt;
* [ExtrudeEffect] - extrude polygons to &amp;quot;3d&amp;quot;, also make &amp;quot;string art&amp;quot;&lt;br /&gt;
* [http://www.colibre.com.br/bin/view/Aurium/InkscapeCalendarShellScript InkscapeCalendarShellScript] - Create Calendars sucks or is impracticable if you do not have something that automatizes the process of to put the days organized in blocks of months to you. To format manually is easy, but this extension can help you on this too [Bash script, works for win32 with cygwin].&lt;br /&gt;
* [http://www.colibre.com.br/bin/view/Aurium/InkscapeAreaCuter InkscapeAreaCutter] - For Webdesigners, layout for sites, slice area into .png images. util! (pt_BR)&lt;br /&gt;
* [http://julvitard.free.fr/eqtexsvg/ EQTeXSVG] - EQTeXSVG is an extension for Inkscape used to convert an inline LATEX equation into SVG path using Python.&lt;br /&gt;
* [http://www.kono.cis.iwate-u.ac.jp/~arakit/inkscape/inklatex.html InkLaTeX] Insert LaTeX text or equations into Inkscape.&lt;br /&gt;
* [http://www.iki.fi/pav/software/textext/ TexText] Embed re-editable LaTeX objects in SVG drawings.&lt;br /&gt;
* [http://math.univ-lille1.fr/~barraud/Inkscape/pathdeform/ PathDeform] - Here is an Inkscape extension whose purpose is to bend a path according to another one. Note: it was added to Inkscape 0.45 as &amp;quot;Pattern along Path&amp;quot;.&lt;br /&gt;
* [http://www.inkbar.lineaire.net/ InkBar] - This is an Inkscape extension whose purpose is to draw EAN13 bar code.&lt;br /&gt;
* [http://technoargia.free.fr/swftools/ SWF output] - A little extension to save as Swf from Inkscape.&lt;br /&gt;
* [http://www.colivre.coop.br/bin/view/Aurium/Puff Puff] - Fluffs elements! :-D&lt;br /&gt;
* [http://www.colivre.coop.br/bin/view/Aurium/InkSudoku Sudoku Generator] - Generates Sudoku square with the solution ''(in a small square if you want)''.&lt;br /&gt;
* [[CalligraphedOutlineFill]] - helps to fill in the inside area of shapes drawn with the Calligraphy tool&lt;br /&gt;
* [http://edlab.die.unipd.it/Site4.html SlotStar] - helps to draw the star of slot of a winding of an electrical machine&lt;br /&gt;
* [http://richard.henwood.googlepages.com/inkscapelatexextension inkscapeLatexExtension] - Extract text in an inkscape drawing to a latex picture environment.&lt;br /&gt;
* [http://saws.googlecode.com s.a.w.s] - export the svg Inkscape file in valid xhtml / css files.&lt;br /&gt;
&lt;br /&gt;
== For Programers: ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.colivre.coop.br/bin/view/Aurium/RubyInk RubyInk] - Inkscape extension with Ruby&lt;br /&gt;
* [http://www.colivre.coop.br/bin/view/Aurium/InkBash Ink-Bash] - &amp;lt;nowiki&amp;gt;ShellScript&amp;lt;/nowiki&amp;gt; Forever!&lt;br /&gt;
* [http://www.colivre.coop.br/bin/view/Aurium/InkMoz InkMoz] - the Bridge from Inkscape to Mozilla (Inkscape extension with Javascript and more)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Developer Documentation]]&lt;br /&gt;
[[Category:Extensions]]&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=Extension_repository&amp;diff=34334</id>
		<title>Extension repository</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=Extension_repository&amp;diff=34334"/>
		<updated>2008-08-14T19:13:21Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;It would be very useful to have a central Internet repository for Inkscape extensions, similarly as Firefox has. This way, Inkscape could update installed extensions from the web site, with no need for the user to know if an extension is updated.&lt;br /&gt;
&lt;br /&gt;
What extensions are there? We can start listing them here:&lt;br /&gt;
* [http://www.ekips.org/comp/inkscape/ Inkscape Effects] - Aaron Spikes set of extensions is now included in Inkscape.&lt;br /&gt;
* ExtrudeEffect - extrude polygons to &amp;quot;3d&amp;quot;, also make &amp;quot;string art&amp;quot;&lt;br /&gt;
* [http://www.colibre.com.br/bin/view/Aurium/InkscapeCalendarShellScript InkscapeCalendarShellScript] - Create Calendars sucks or is impracticable if you do not have something that automatizes the process of to put the days organized in blocks of months to you. To format manually is easy, but this extension can help you on this too [Bash script, works for win32 with cygwin].&lt;br /&gt;
* [http://www.colibre.com.br/bin/view/Aurium/InkscapeAreaCuter InkscapeAreaCutter] - For Webdesigners, layout for sites, slice area into .png images. util! (pt_BR)&lt;br /&gt;
* [http://julvitard.free.fr/eqtexsvg/ EQTeXSVG] - EQTeXSVG is an extension for Inkscape used to convert an inline LATEX equation into SVG path using Python.&lt;br /&gt;
* [http://www.kono.cis.iwate-u.ac.jp/~arakit/inkscape/inklatex.html InkLaTeX] Insert LaTeX text or equations into Inkscape.&lt;br /&gt;
* [http://www.iki.fi/pav/software/textext/ TexText] Embed re-editable LaTeX objects in SVG drawings.&lt;br /&gt;
* [http://math.univ-lille1.fr/~barraud/Inkscape/pathdeform/ PathDeform] - Here is an Inkscape extension whose purpose is to bend a path according to another one. Note: it was added to Inkscape 0.45 as &amp;quot;Pattern along Path&amp;quot;.&lt;br /&gt;
* [http://www.inkbar.lineaire.net/ InkBar] - This is an Inkscape extension whose purpose is to draw EAN13 bar code.&lt;br /&gt;
* [http://technoargia.free.fr/swftools/ SWF output] - A little extension to save as Swf from Inkscape.&lt;br /&gt;
* [http://www.colivre.coop.br/bin/view/Aurium/Puff Puff] - Fluffs elements! :-D&lt;br /&gt;
* [http://www.colivre.coop.br/bin/view/Aurium/InkSudoku Sudoku Generator] - Generates Sudoku square with the solution ''(in a small square if you want)''.&lt;br /&gt;
* [[CalligraphedOutlineFill]] - helps to fill in the inside area of shapes drawn with the Calligraphy tool&lt;br /&gt;
* [http://edlab.die.unipd.it/Site4.html SlotStar] - helps to draw the star of slot of a winding of an electrical machine&lt;br /&gt;
* [http://richard.henwood.googlepages.com/inkscapelatexextension inkscapeLatexExtension] - Extract text in an inkscape drawing to a latex picture environment.&lt;br /&gt;
* [http://saws.googlecode.com s.a.w.s] - export the svg Inkscape file in valid xhtml / css files.&lt;br /&gt;
&lt;br /&gt;
== For Programers: ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.colivre.coop.br/bin/view/Aurium/RubyInk RubyInk] - Inkscape extension with Ruby&lt;br /&gt;
* [http://www.colivre.coop.br/bin/view/Aurium/InkBash Ink-Bash] - &amp;lt;nowiki&amp;gt;ShellScript&amp;lt;/nowiki&amp;gt; Forever!&lt;br /&gt;
* [http://www.colivre.coop.br/bin/view/Aurium/InkMoz InkMoz] - the Bridge from Inkscape to Mozilla (Inkscape extension with Javascript and more)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Developer Documentation]]&lt;br /&gt;
[[Category:Extensions]]&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=18954</id>
		<title>InkSlide</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=18954"/>
		<updated>2008-02-01T02:56:16Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: /* Code */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== InkSlide - quick and easy presentations using Inkscape ==&lt;br /&gt;
&lt;br /&gt;
InkSlide produces slides like this:&lt;br /&gt;
&lt;br /&gt;
[[Image:slide0004.jpg]]&lt;br /&gt;
&lt;br /&gt;
from simple text input like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
InkSlide: Features&lt;br /&gt;
++++++++++++++++++&lt;br /&gt;
&lt;br /&gt;
Features include wrapped top level text and&lt;br /&gt;
- mulitple&lt;br /&gt;
  - levels&lt;br /&gt;
    - of wrapped bulleted lists with bullets and font&lt;br /&gt;
      information taken from the template file.&lt;br /&gt;
&lt;br /&gt;
Slide specific content like this:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
which is updated when the template changes.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An Inkscape file is used as a template file to define the background, title position and font, fonts and positions for text at different levels of indentation, groups to be cloned and used as bullets, etc.&lt;br /&gt;
&lt;br /&gt;
Content specific to a particular slide can also be created in Inkscape, this content merged with the template and text input to make the final slide, so changes to the template after a particular slide is edited in Inkscape are included.&lt;br /&gt;
&lt;br /&gt;
=== Download ===&lt;br /&gt;
&lt;br /&gt;
Copy the [[InkSlide#Code|text below]] into a file called 'inkslide.py'.  (How do you attach a non-image file on this wiki?)&lt;br /&gt;
&lt;br /&gt;
Here is an [[Media:template0.svg|example template]].  Use Save Link As to stop your browser displaying the file instead of downloading it.&lt;br /&gt;
&lt;br /&gt;
=== How it works ===&lt;br /&gt;
&lt;br /&gt;
First InkSlide parses the text input, something like the example at the top of this page.  It looks for flowed text boxes in the template file with special @id attributes like ''fr_title'' for the title, and ''fr_tab2'' for the second level of bulleted lists.  Copies of these elements are made and placed in a layer called 'Texts'.  At this point, the text is all piled up at the top of the page.&lt;br /&gt;
&lt;br /&gt;
InkSlide saves the file, and uses Inkscape's command line ''--query-height'' and friends to find out how high the wrapped text pieces are.  At it then repositions them properly, adds clones of the bullet markers, etc.  Bullet markers have special @id attributes like ''bu_1'' for the first level of bulleted list.&lt;br /&gt;
&lt;br /&gt;
Finally, InkSlide checks to see if there's an id tag for the slide, and if so if there's a corresponding Inkscape SVG file with content for this particular slide. This content has to occur in layers whose name starts with ''Instance'', and these layers have to be present in the template file, but you can have more than one, so they can be above and below the regular content.  If such content is found, it's copied into the output slide.&lt;br /&gt;
&lt;br /&gt;
=== The Template File ===&lt;br /&gt;
&lt;br /&gt;
The template file is a regular Inkscape file.  If this list seems hard, just use the example template and modify it to suit your needs.&lt;br /&gt;
&lt;br /&gt;
Required contents:&lt;br /&gt;
&lt;br /&gt;
*A group or layer called ''gr_text'', into which text will be placed.&lt;br /&gt;
*A flowed text frame with an @id of ''fr_title'' to define the font and position for the slide title.  This should contain text like ''Title goes here Sly'' because ''Sly'' includes letters which ascend and descend from the baseline to the greatest extent.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*A group or layer called ''gr_title'' into which the title will be placed.  This makes it easier to do special effects on the title like shadows.&lt;br /&gt;
*Flowed text frames with an @id of ''fr_tabN'' for each N in 0, 1, 2, 3.  ''fr_tab0'' is used for top level non bulleted text.  These should also include ''Sly'' or similar.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*Objects or groups withan @id of ''bu_N'' for each N in 1, 2, 3, for the three levels of bulleted list.&lt;br /&gt;
&lt;br /&gt;
The fr_* elements define the font, font-size, color etc. for the different levels of text.  They also define the position and wrap width.  See the example template file for a possible arrangement.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;A group or layer called 'X'&amp;quot; indicates either a group with an '''@id'''&lt;br /&gt;
of 'X' ''or'' a layer with a '''label''' (name) of 'X'.  When both exist the group will take precedence.&lt;br /&gt;
 &lt;br /&gt;
Optional contents:&lt;br /&gt;
&lt;br /&gt;
*A background, which should be placed in a group or layer called `gr_bg`&lt;br /&gt;
*A foreground, which should be placed in a group or layer called `gr_fg`&lt;br /&gt;
*Affiliation text and logos you want to appear on every slide, this could go in the background, or another named group or layer.&lt;br /&gt;
*A blurred clone of the gr_title element, offset and behind it.  It's easier to do this is `gr_title` is a group rather than a layer.&lt;br /&gt;
*Layers whose name starts with ''Instance'' - these are the layers the user can edit directly in Inkscape for a particular slide.&lt;br /&gt;
&lt;br /&gt;
When you save the template file you should make sure that layers containing place holder text (the fr_* elements) and bullets are invisible.  Likewise the Texts layer should be visible, as well as any Instance layers.&lt;br /&gt;
=== Text Input ===&lt;br /&gt;
&lt;br /&gt;
InkSlide uses [http://docutils.sourceforge.net/rst.html reStructuredText] for markup, although it renders only a subset of rst.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Inkslide: an example&lt;br /&gt;
++++++++++++++++++++&lt;br /&gt;
&lt;br /&gt;
This text will appear as a top level paragraph.&lt;br /&gt;
&lt;br /&gt;
- Dash prefix indicates this text will appear as a bulleted item.&lt;br /&gt;
&lt;br /&gt;
  - Indented dash prefix -&amp;gt; second level bulleted list.&lt;br /&gt;
&lt;br /&gt;
.. is:pause&lt;br /&gt;
&lt;br /&gt;
Two slides will be produced for this input, one with everything up to the&lt;br /&gt;
&amp;quot;.. is:pause&amp;quot;, and one with that plus the remaining content.&lt;br /&gt;
Incremental list display can be done this way.&lt;br /&gt;
&lt;br /&gt;
The is:id tag below indicates that a file with a name like&lt;br /&gt;
'extra/is_intro.svg' should be inspected for extra content&lt;br /&gt;
for this particular slide.&lt;br /&gt;
&lt;br /&gt;
.. is:id is_intro&lt;br /&gt;
&lt;br /&gt;
Inkslide: the next slide...&lt;br /&gt;
+++++++++++++++++++++++++++&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== The GUI ===&lt;br /&gt;
&lt;br /&gt;
A GUI for InkSlide is available as a plug-in for the [http://webpages.charter.net/edreamleo/front.html Leo] outline editor.  This makes slide re-arranging easy, and adds some macro substitution capability.  It also adds buttons for editing slide specific content in Inkscape.&lt;br /&gt;
&lt;br /&gt;
FIXME: post plug-in on Leo wiki and link.&lt;br /&gt;
&lt;br /&gt;
=== To do / status ===&lt;br /&gt;
&lt;br /&gt;
InkSlide is very usable, but is missing some features.  Remember you can &lt;br /&gt;
use the slide specific editing feature to do any of these things and&lt;br /&gt;
anything else you can do in Inkscape already, these will just be more&lt;br /&gt;
convenient in the text input when they're done.&lt;br /&gt;
&lt;br /&gt;
*Inline text formatting (bold, color, etc.) for part of a piece of text&lt;br /&gt;
*Justification switching&lt;br /&gt;
*Verbatim code display&lt;br /&gt;
*&amp;lt;strike&amp;gt;Simple image inclusion&amp;lt;/strike&amp;gt;&lt;br /&gt;
*more helper tools to make PNG and PDF versions of slides&lt;br /&gt;
*configuring / using a directory for edited slides&lt;br /&gt;
*command line flags for generating specific slides&lt;br /&gt;
&lt;br /&gt;
Currently InkSlide is slow, because it must invoke Inkscape once for every single dimension it needs to retrieve.  I'll try and get a --query-all switch into 0.46.&lt;br /&gt;
&lt;br /&gt;
=== Bugs ===&lt;br /&gt;
&lt;br /&gt;
*There may be an issue with the way slide specific content is merged into the template producing conflicting @id attributes, but I haven't seen this happen yet.&lt;br /&gt;
&lt;br /&gt;
=== Author  / Credits ===&lt;br /&gt;
&lt;br /&gt;
InkSlide was written by Terry Brown, terry-n-brown@yahoo.com - but use underscore, not '-'.&lt;br /&gt;
&lt;br /&gt;
InkSlide was inspired by [http://member.wide.ad.jp/wg/mgp/ MagicPoint]&lt;br /&gt;
&lt;br /&gt;
Matt Harrison suggested using reStructured text as the markup language.&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# inkslide.py $Id$&lt;br /&gt;
# Author: Terry Brown&lt;br /&gt;
# Created: Thu Oct 11 2007&lt;br /&gt;
&lt;br /&gt;
import lxml.etree as ET&lt;br /&gt;
&lt;br /&gt;
import copy&lt;br /&gt;
import subprocess&lt;br /&gt;
import os.path&lt;br /&gt;
from docutils.core import publish_string&lt;br /&gt;
&lt;br /&gt;
import sys  # for debug&lt;br /&gt;
class inkSlide(object):&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
&lt;br /&gt;
        self.NS = { 'i': 'http://www.inkscape.org/namespaces/inkscape',&lt;br /&gt;
           's': 'http://www.w3.org/2000/svg',&lt;br /&gt;
           'xlink' : 'http://www.w3.org/1999/xlink'}&lt;br /&gt;
&lt;br /&gt;
        # number of defined tablevels, FIXME, could derive from template?&lt;br /&gt;
        self.tabLevels = 4  &lt;br /&gt;
&lt;br /&gt;
        self._nextId = 0&lt;br /&gt;
&lt;br /&gt;
        self.gap = {&lt;br /&gt;
            'bIMAGE': 15,   # gap before image&lt;br /&gt;
            'aIMAGE': 15,   # gap after image&lt;br /&gt;
            'aTAB0': 10,    # gap after top level text&lt;br /&gt;
            'aTAB1': 10,    # gap after level 1 bullet&lt;br /&gt;
            'aTAB2': 10,    # gap after level 2 bullet&lt;br /&gt;
            'aTAB3': 10,    # gap after level 3 bullet&lt;br /&gt;
            'fVCENTER': 1,  # flag - center vertically&lt;br /&gt;
            'dHEIGHT': 728, # dimension, bottom of page&lt;br /&gt;
            'fHCENTER': 1,  # flag - center vertically&lt;br /&gt;
            'dWIDTH': 1024, # dimension, bottom of page&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        self.dimCache = {}&lt;br /&gt;
        self.is_reads, self.is_cache = 0, 0&lt;br /&gt;
    def __del__(self):&lt;br /&gt;
        print '%d reads, %d cache hits' % (self.is_reads, self.is_cache)&lt;br /&gt;
    def nextId(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;return an unsed Id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        # FIXME, should inspect template doc.&lt;br /&gt;
        self._nextId += 1&lt;br /&gt;
        return 'is'+str(self._nextId)&lt;br /&gt;
&lt;br /&gt;
    def clearId(self, x, what='id', NS={}):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;recursively clear @id on element x and descendants&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if not NS: NS = self.NS&lt;br /&gt;
        if what in x.keys():&lt;br /&gt;
            del x.attrib[what]&lt;br /&gt;
        for i in x.xpath('.//*[@%s]'%what, NS):&lt;br /&gt;
            del i.attrib[what]&lt;br /&gt;
        return x&lt;br /&gt;
    def getDim(self, fn, Id, what):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;return dimension of element in fn with @id Id, what is&lt;br /&gt;
        x, y, width, height&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
        hsh = fn+Id+what&lt;br /&gt;
        if hsh in self.dimCache:&lt;br /&gt;
            self.is_cache += 1&lt;br /&gt;
            return self.dimCache[hsh]&lt;br /&gt;
&lt;br /&gt;
        cmd = ('inkscape', '--without-gui', '--query-id', Id, '--query-'+what, fn)&lt;br /&gt;
&lt;br /&gt;
        proc = subprocess.Popen(cmd, stdout = subprocess.PIPE,&lt;br /&gt;
                                stderr = subprocess.PIPE)&lt;br /&gt;
        # make new pipe for stderr to supress chatter from inkscape&lt;br /&gt;
        proc.wait()&lt;br /&gt;
        self.dimCache[hsh] = proc.stdout.read()&lt;br /&gt;
        if self.dimCache[hsh].strip() == '':&lt;br /&gt;
            print &amp;quot;Warning: '%s' not found&amp;quot; % Id&lt;br /&gt;
        self.is_reads += 1&lt;br /&gt;
        return self.dimCache[hsh]&lt;br /&gt;
    def textReader(self, text):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;NOT USED iterate parts in input text&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if not text: return&lt;br /&gt;
        for line in text.split('\n'):&lt;br /&gt;
            if line.startswith('%slide '):&lt;br /&gt;
                yield {'type':'TITLE', 's':line[7:].strip()}&lt;br /&gt;
                continue&lt;br /&gt;
            if line.startswith('%pause'):&lt;br /&gt;
                yield {'type':'PAUSE'}&lt;br /&gt;
                continue&lt;br /&gt;
            if line.startswith('%id '):&lt;br /&gt;
                yield {'type':'ID', 's':line[4:].strip()}&lt;br /&gt;
                continue&lt;br /&gt;
            if not line.strip():&lt;br /&gt;
                yield {'type':'BLANK'}&lt;br /&gt;
                continue&lt;br /&gt;
            tabs = len(line) - len(line.lstrip('\t'))&lt;br /&gt;
            yield {'type':'TAB', 'tabs':tabs, 's':line.strip()}&lt;br /&gt;
&lt;br /&gt;
    def textReaderRst(self, text):&lt;br /&gt;
&lt;br /&gt;
        xml = publish_string(text.replace('\t','    '), writer_name = 'xml')&lt;br /&gt;
        doc = ET.fromstring(xml)&lt;br /&gt;
        # yield {'type':'TITLE', 's':doc.xpath('/document/@title')[0]}&lt;br /&gt;
        nodes = 'paragraph', 'comment', 'title', 'image'&lt;br /&gt;
        for i in doc.xpath('//%s' % '|//'.join(nodes)):&lt;br /&gt;
&lt;br /&gt;
            if i.tag == 'title':&lt;br /&gt;
                yield {'type':'TITLE', 's':i.text}&lt;br /&gt;
            if i.tag == 'paragraph':&lt;br /&gt;
                tabs = len(i.xpath('ancestor::list_item'))&lt;br /&gt;
                yield {'type':'TAB', 'tabs':tabs, 's':i.text, 'e':i}&lt;br /&gt;
            if i.tag == 'image':&lt;br /&gt;
                tabs = len(i.xpath('ancestor::list_item'))&lt;br /&gt;
                yield {'type':'IMAGE', 'uri':i.get('uri')}&lt;br /&gt;
            if i.tag == 'comment':&lt;br /&gt;
&lt;br /&gt;
                comparts = i.text.split(None, 1)&lt;br /&gt;
&lt;br /&gt;
                if len(comparts) &amp;gt; 1:&lt;br /&gt;
                    ctag, cval = comparts&lt;br /&gt;
                elif len(comparts) == 1:&lt;br /&gt;
                    ctag = comparts[0]&lt;br /&gt;
                else:&lt;br /&gt;
                    continue  # empty comment had no meaning to InkView&lt;br /&gt;
&lt;br /&gt;
                if ctag == 'is:id':&lt;br /&gt;
                    try:&lt;br /&gt;
                        yield {'type':'ID', 's':cval}&lt;br /&gt;
                    except:&lt;br /&gt;
                        raise Exception('&amp;quot;id&amp;quot; comment requires a value')&lt;br /&gt;
                elif ctag == 'is:notitle':&lt;br /&gt;
                    yield {'type':'DROP', 'what':('gr_title',)}&lt;br /&gt;
                elif ctag == 'is:notext':&lt;br /&gt;
                    yield {'type':'DROP', 'what':('gr_text',)}&lt;br /&gt;
                elif ctag == 'is:nobackground':&lt;br /&gt;
                    yield {'type':'DROP', 'what':('gr_bg',)}&lt;br /&gt;
                elif ctag == 'is:noforeground':&lt;br /&gt;
                    yield {'type':'DROP', 'what':('gr_fg',)}&lt;br /&gt;
                elif ctag == 'is:blank':&lt;br /&gt;
                    yield {'type':'DROP', 'what':('gr_title','gr_text')}&lt;br /&gt;
                elif ctag == 'is:empty':&lt;br /&gt;
                    yield {'type':'DROP', 'what':('gr_bg','gr_title','gr_text','gr_fg')}&lt;br /&gt;
                elif ctag == 'is:no':&lt;br /&gt;
                    try:&lt;br /&gt;
                        yield {'type':'DROP', 'what':(cval,)}&lt;br /&gt;
                    except:&lt;br /&gt;
                        raise Exception('&amp;quot;no&amp;quot; comment requires a value')&lt;br /&gt;
&lt;br /&gt;
                if ctag == 'is:dim':&lt;br /&gt;
                    dim, val = cval.strip().split()&lt;br /&gt;
                    yield {'type':'DIM', 'dim':dim, 'val':val}&lt;br /&gt;
    def groupOrLayer(self, g):&lt;br /&gt;
        dst = self.doc.xpath('//s:g[@id=&amp;quot;%s&amp;quot;]'%g, self.NS)&lt;br /&gt;
        if not dst: dst = self.doc.xpath('//s:g[@i:label=&amp;quot;%s&amp;quot;]'%g, self.NS)&lt;br /&gt;
        if not dst: return dst  # let the caller test, don't raise exception here&lt;br /&gt;
        dst = dst[0]&lt;br /&gt;
        return dst&lt;br /&gt;
    def instanceFromTemplate(self, template, instance, text, pauseOk = True):&lt;br /&gt;
&lt;br /&gt;
        slide = 0&lt;br /&gt;
&lt;br /&gt;
        hlist = []&lt;br /&gt;
        self.doc = ET.parse(template)&lt;br /&gt;
        slideId = None&lt;br /&gt;
&lt;br /&gt;
        for t in self.textReaderRst(text):&lt;br /&gt;
&lt;br /&gt;
            if t['type'] == 'ID':&lt;br /&gt;
                slideId = t['s']&lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            if t['type'] == 'TITLE':&lt;br /&gt;
                if hlist:&lt;br /&gt;
                    self.process(instance, hlist, slideId, slide)&lt;br /&gt;
                    slide = slide + 1&lt;br /&gt;
                    hlist = [t]&lt;br /&gt;
                    self.doc = ET.parse(template)&lt;br /&gt;
                    slideId = None&lt;br /&gt;
                else:&lt;br /&gt;
                    hlist.append(t)&lt;br /&gt;
&lt;br /&gt;
                print t['s']&lt;br /&gt;
&lt;br /&gt;
                dst = self.groupOrLayer('gr_title')&lt;br /&gt;
                src = self.doc.xpath('//s:flowRoot[@id=&amp;quot;fr_title&amp;quot;]', self.NS)[0]&lt;br /&gt;
                cpy = self.clearId(copy.deepcopy(src))&lt;br /&gt;
                Id = self.nextId()&lt;br /&gt;
                cpy.set('id', Id)&lt;br /&gt;
                hlist[-1]['id']=Id&lt;br /&gt;
                cpy.xpath('s:flowPara',self.NS)[0].text = t['s']&lt;br /&gt;
                dst.append(cpy)&lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            if t['type'] == 'PAUSE':&lt;br /&gt;
                if pauseOk:&lt;br /&gt;
                    self.process(instance, hlist, slideId, slide)&lt;br /&gt;
                    slide = slide + 1&lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            if t['type'] == 'BLANK':&lt;br /&gt;
                hlist.append(t)&lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            if t['type'] == 'TAB':&lt;br /&gt;
                hlist.append(t)&lt;br /&gt;
                dst = self.groupOrLayer('gr_text')&lt;br /&gt;
                src = self.doc.xpath('//s:flowRoot[@id=&amp;quot;fr_tab%d&amp;quot;]' % t['tabs'], self.NS)[0]&lt;br /&gt;
                cpy = self.clearId(copy.deepcopy(src))&lt;br /&gt;
                Id = self.nextId()&lt;br /&gt;
                cpy.set('id', Id)&lt;br /&gt;
                hlist[-1]['id']=Id&lt;br /&gt;
                # cpy.xpath('s:flowPara',self.NS)[0].text = t['s']&lt;br /&gt;
                self.flowParaFromElement(&lt;br /&gt;
                    cpy.xpath('s:flowPara',self.NS)[0], t['e'])&lt;br /&gt;
                dst.append(cpy)&lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            if t['type'] == 'IMAGE':&lt;br /&gt;
                hlist.append(t)&lt;br /&gt;
                dst = self.groupOrLayer('gr_image')&lt;br /&gt;
                if not dst:&lt;br /&gt;
                    dst = self.groupOrLayer('gr_text')&lt;br /&gt;
                img = ET.Element('image')&lt;br /&gt;
                img.set('{%s}href'%self.NS['xlink'], t['uri'])&lt;br /&gt;
                Id = self.nextId()&lt;br /&gt;
                img.set('id', Id)&lt;br /&gt;
                hlist[-1]['id']=Id&lt;br /&gt;
                dst.append(img)&lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            hlist.append(t)  # whatever it was&lt;br /&gt;
&lt;br /&gt;
        self.process(instance, hlist, slideId, slide)&lt;br /&gt;
    def flowParaFromElement(self, fp, e):&lt;br /&gt;
        fp.text = e.xpath('string(.)')&lt;br /&gt;
    def process(self, instance, hlist, slideId, slide):&lt;br /&gt;
&lt;br /&gt;
        instanceName = instance.replace('%s', '%04d'%slide)&lt;br /&gt;
&lt;br /&gt;
        instance = 'tmp.svg'  # reusing allows caching in getDim, but relies on nextId ensuring&lt;br /&gt;
                              # that the changing parts have new ids&lt;br /&gt;
&lt;br /&gt;
        self.doc.write(file(instance, 'w'))&lt;br /&gt;
&lt;br /&gt;
        drop = []&lt;br /&gt;
&lt;br /&gt;
        bullDim = {}&lt;br /&gt;
        tabDim = {}&lt;br /&gt;
        for n in range(0,self.tabLevels):&lt;br /&gt;
            b = {}&lt;br /&gt;
            x = self.doc.xpath('//*[@id=&amp;quot;bu_%d&amp;quot;]'%n, self.NS)&lt;br /&gt;
            if x:&lt;br /&gt;
                # self.clearId(x[0], what='transform')&lt;br /&gt;
                for i in 'x', 'y', 'width', 'height':&lt;br /&gt;
                    b[i] = float(self.getDim(instance, 'bu_%d'%n, i))&lt;br /&gt;
                bullDim[n] = b&lt;br /&gt;
            b = {}&lt;br /&gt;
            x = self.doc.xpath('//*[@id=&amp;quot;fr_tab%d&amp;quot;]'%n, self.NS)&lt;br /&gt;
            if x:&lt;br /&gt;
                # clearId(x[0], what='transform')&lt;br /&gt;
                for i in 'x', 'y', 'width', 'height':&lt;br /&gt;
                    b[i] = float(self.getDim(instance, 'fr_tab%d'%n, i))&lt;br /&gt;
                x = x[0].xpath('.//s:rect',self.NS)[0]&lt;br /&gt;
                b['max-width'] = float(x.get('width'))&lt;br /&gt;
                b['max-height'] = float(x.get('height'))&lt;br /&gt;
                tabDim[n] = b&lt;br /&gt;
&lt;br /&gt;
        delta = 0.&lt;br /&gt;
        firstVert = True&lt;br /&gt;
        placed_xy = []&lt;br /&gt;
        placed_transform = []&lt;br /&gt;
&lt;br /&gt;
        for n, i in enumerate(hlist):&lt;br /&gt;
&lt;br /&gt;
            this = hlist[n]&lt;br /&gt;
            prev = hlist[n-1]&lt;br /&gt;
&lt;br /&gt;
            if this['type'] == 'DROP': drop += this['what']&lt;br /&gt;
&lt;br /&gt;
            if this['type'] == 'DIM':&lt;br /&gt;
                if this['dim'] not in self.gap or this['val'][0] not in '+-':&lt;br /&gt;
                    self.gap[this['dim']] = float(this['val'])&lt;br /&gt;
                else:&lt;br /&gt;
                    self.gap[this['dim']] += float(this['val'])&lt;br /&gt;
&lt;br /&gt;
            zeroHeight = ['TITLE', 'DROP', 'DIM']&lt;br /&gt;
&lt;br /&gt;
            # don't do this - need to add height from prev to delta&lt;br /&gt;
            #if this['type'] in zeroHeight:&lt;br /&gt;
            #    continue  # already in right place, not a vertical element&lt;br /&gt;
&lt;br /&gt;
            hgt = 0&lt;br /&gt;
            if prev['type'] == 'BLANK':&lt;br /&gt;
                hgt = 30&lt;br /&gt;
            elif prev['type'] not in zeroHeight:&lt;br /&gt;
                hgt = float(self.getDim(instance, prev['id'], 'height'))&lt;br /&gt;
&lt;br /&gt;
                # risky gambit to space bullets properly when some lines&lt;br /&gt;
                # have no ascenders etc.&lt;br /&gt;
                if prev['type'] == 'TAB' and prev['tabs'] &amp;gt; 0:&lt;br /&gt;
                    lnh = tabDim[prev['tabs']]['height']&lt;br /&gt;
                    hgt = lnh * int(hgt/lnh+0.5)&lt;br /&gt;
&lt;br /&gt;
            # TAB0, TAB1, TITLE, IMAGE, etc.&lt;br /&gt;
            # gap after previous&lt;br /&gt;
            hsh = 'a' + prev['type'] + str(prev.get('tabs',''))&lt;br /&gt;
            hgt += self.gap.get(hsh, 0)&lt;br /&gt;
            # gap before current&lt;br /&gt;
            hsh = 'b' + this['type'] + str(this.get('tabs',''))&lt;br /&gt;
            hgt += self.gap.get(hsh, 0)&lt;br /&gt;
&lt;br /&gt;
            if firstVert:&lt;br /&gt;
                hgt = 0&lt;br /&gt;
                firstVert = False&lt;br /&gt;
&lt;br /&gt;
            delta += hgt&lt;br /&gt;
&lt;br /&gt;
            if this['type'] == 'BLANK': continue&lt;br /&gt;
&lt;br /&gt;
            if this['type'] == 'TAB':&lt;br /&gt;
                tabs = this['tabs']&lt;br /&gt;
&lt;br /&gt;
                r = self.doc.xpath('//s:flowRoot[@id=&amp;quot;%s&amp;quot;]' % this['id'], self.NS)[0]&lt;br /&gt;
                self.clearId(r, what='transform')&lt;br /&gt;
                r = r.xpath('.//s:rect', self.NS)[0]&lt;br /&gt;
                r.set('x', str(tabDim[tabs]['x']))&lt;br /&gt;
                y = tabDim[tabs]['y'] + delta&lt;br /&gt;
                #print '%s -&amp;gt; %s' % (str(r.get('y')), str(y))&lt;br /&gt;
                #print tabDim[tabs]['y']&lt;br /&gt;
                r.set('y', str(y))&lt;br /&gt;
                placed_xy.append([r, this['id']])&lt;br /&gt;
&lt;br /&gt;
            if (this['type'] == 'TAB' and this['tabs'] in bullDim&lt;br /&gt;
                and 'isDone' not in this):&lt;br /&gt;
                this['isDone'] = True&lt;br /&gt;
                dx = (tabDim[tabs]['x'] &lt;br /&gt;
                      - bullDim[tabs]['x']&lt;br /&gt;
                      - 1.5*bullDim[tabs]['width']&lt;br /&gt;
                      )&lt;br /&gt;
                dy = (y &lt;br /&gt;
                      + tabDim[tabs]['height'] / 2.&lt;br /&gt;
                      - bullDim[tabs]['y'] &lt;br /&gt;
                      - bullDim[tabs]['height'] / 2.&lt;br /&gt;
                      )&lt;br /&gt;
                # dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
                dst = self.groupOrLayer('gr_text')&lt;br /&gt;
                #print y, tabDim[tabs]['height'], bullDim[tabs]['y'], bullDim[tabs]['height'], dy&lt;br /&gt;
                clone = ET.Element('use')&lt;br /&gt;
                clone.set('transform', 'translate(%f,%f)' % (dx,dy))&lt;br /&gt;
                clone.set('{%s}href'%self.NS['xlink'], '#bu_%d'%tabs)&lt;br /&gt;
                placed_transform.append([clone, dx, dy])&lt;br /&gt;
                dst.append(clone)&lt;br /&gt;
&lt;br /&gt;
            if this['type'] == 'IMAGE':&lt;br /&gt;
&lt;br /&gt;
                xpath = '//s:image[@id=&amp;quot;%s&amp;quot;]' % this['id']  # doesn't work?&lt;br /&gt;
                xpath = '//*[@id=&amp;quot;%s&amp;quot;]' % this['id']&lt;br /&gt;
                i = self.doc.xpath(xpath, self.NS)[0]&lt;br /&gt;
                w = float(self.getDim(instance, this['id'], 'width'))&lt;br /&gt;
                x = tabDim[0]['x']+tabDim[0]['max-width']/2-w/2&lt;br /&gt;
                i.set('x', str(x))&lt;br /&gt;
                y = tabDim[0]['y'] + delta&lt;br /&gt;
                i.set('y', str(y))&lt;br /&gt;
                placed_xy.append([i,this['id']])&lt;br /&gt;
&lt;br /&gt;
        if self.gap['fVCENTER']:&lt;br /&gt;
&lt;br /&gt;
            if 'id' in this:&lt;br /&gt;
                delta += float(self.getDim(instance, this['id'], 'height'))&lt;br /&gt;
&lt;br /&gt;
            recenter = (self.gap['dHEIGHT'] - delta - tabDim[0]['y']) * 0.45&lt;br /&gt;
&lt;br /&gt;
            for i in placed_transform:&lt;br /&gt;
                i[0].set('transform', 'translate(%f,%f)'%(i[1],i[2]+recenter))&lt;br /&gt;
                i[2] += recenter  # so it's not lost below&lt;br /&gt;
            for i in placed_xy:&lt;br /&gt;
                i[0].set('y', str(float(i[0].get('y'))+recenter))&lt;br /&gt;
&lt;br /&gt;
        if self.gap['fHCENTER']:&lt;br /&gt;
&lt;br /&gt;
            maxX = 0&lt;br /&gt;
            for i in placed_xy:&lt;br /&gt;
                x = float(self.getDim(instance, i[1], 'x'))&lt;br /&gt;
                x += float(self.getDim(instance, i[1], 'width'))&lt;br /&gt;
                if x &amp;gt; maxX: maxX = x&lt;br /&gt;
&lt;br /&gt;
            recenter = (self.gap['dWIDTH'] - maxX - tabDim[0]['x']) * 0.5&lt;br /&gt;
&lt;br /&gt;
            for i in placed_transform:&lt;br /&gt;
                i[0].set('transform', 'translate(%f,%f)'%(i[1]+recenter,i[2]))&lt;br /&gt;
                i[1] += recenter  # in case it's used again&lt;br /&gt;
            for i in placed_xy:&lt;br /&gt;
                if i[0].tag != 'image':  # images already centered&lt;br /&gt;
                    # print i[0].tag&lt;br /&gt;
                    i[0].set('x', str(float(i[0].get('x'))+recenter))&lt;br /&gt;
&lt;br /&gt;
        # look for instance specific parts&lt;br /&gt;
        if slideId:&lt;br /&gt;
            f = self.instancePath(slideId)&lt;br /&gt;
            if os.path.isfile(f):&lt;br /&gt;
                comp = ET.parse(f)&lt;br /&gt;
                svg = self.doc.xpath('//s:svg', self.NS)[0]&lt;br /&gt;
                g = &amp;quot;{%s}g&amp;quot;%self.NS['s']&lt;br /&gt;
                k = &amp;quot;{%s}label&amp;quot;%self.NS['i']&lt;br /&gt;
                for n, i in enumerate(svg.getchildren()):&lt;br /&gt;
                    if i.tag == g and i.get(k, '').startswith('Instance'):&lt;br /&gt;
                        x = comp.xpath('//s:svg//s:g[@i:label=&amp;quot;%s&amp;quot;]'%i.get(k), self.NS)&lt;br /&gt;
                        if x:&lt;br /&gt;
                            x = x[0]&lt;br /&gt;
                            cpy = self.clearId(copy.deepcopy(x))&lt;br /&gt;
                            # cpy = self.textCopy(x)&lt;br /&gt;
                            svg[n] = cpy&lt;br /&gt;
                defs = comp.xpath('//s:svg/s:defs/*', self.NS)&lt;br /&gt;
                dst = self.doc.xpath('//s:svg/s:defs', self.NS)[0]&lt;br /&gt;
                for d in defs:&lt;br /&gt;
                    Id = d.get('id')&lt;br /&gt;
                    current = self.doc.xpath('//s:svg/s:defs/*[@id=&amp;quot;%s&amp;quot;]'%Id, self.NS)&lt;br /&gt;
                    if not current:&lt;br /&gt;
                        cpy = copy.deepcopy(d)&lt;br /&gt;
                        dst.append(cpy)&lt;br /&gt;
&lt;br /&gt;
        for i in drop:&lt;br /&gt;
            e = self.groupOrLayer(i)&lt;br /&gt;
            if e:&lt;br /&gt;
                e.xpath('..')[0].remove(e)&lt;br /&gt;
&lt;br /&gt;
        # problem losing xlink ns?&lt;br /&gt;
        for i in self.doc.xpath('//*[@href]'):&lt;br /&gt;
            i.set('{%s}href'%self.NS['xlink'], i.get('href'))&lt;br /&gt;
&lt;br /&gt;
        self.doc.write(file(instanceName, 'w'))&lt;br /&gt;
&lt;br /&gt;
        return slideId&lt;br /&gt;
    def textCopy(self, ele):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;copy the element ele via xml-&amp;gt;text-&amp;gt;xml, as deepcopy seems to lose text&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        txt = ET.tostring(ele)&lt;br /&gt;
        # print txt&lt;br /&gt;
        return ET.fromstring(txt)&lt;br /&gt;
    def instancePath(self, slideId):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;return path to component for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        # return os.path.join(slideId, component)&lt;br /&gt;
        return slideId+'.svg'&lt;br /&gt;
    def writeComponents(self, inkscapeFile, slideId):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;write components from file for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if not slideId: return&lt;br /&gt;
        doc0 = ET.parse(inkscapeFile)&lt;br /&gt;
        hasComponents = False&lt;br /&gt;
        for i in doc0.xpath('//s:svg//s:g', self.NS):&lt;br /&gt;
            k = &amp;quot;{%s}label&amp;quot;%self.NS['i']&lt;br /&gt;
            # print i.keys(),k&lt;br /&gt;
            if (k in i.keys() and i.get(k).startswith('Instance')):&lt;br /&gt;
                if len(i) &amp;gt; 0:&lt;br /&gt;
                    hasComponents = True&lt;br /&gt;
                    break&lt;br /&gt;
                    # ET.ElementTree(i).write(f)&lt;br /&gt;
&lt;br /&gt;
        f = self.instancePath(slideId)&lt;br /&gt;
&lt;br /&gt;
        if hasComponents:&lt;br /&gt;
            if (os.path.abspath(os.path.realpath(inkscapeFile)) != &lt;br /&gt;
                os.path.abspath(os.path.realpath(f))):&lt;br /&gt;
                file(f, 'w').write(file(inkscapeFile).read())&lt;br /&gt;
        else:&lt;br /&gt;
            if os.path.isfile(f):&lt;br /&gt;
                os.remove(f)&lt;br /&gt;
&lt;br /&gt;
if __name__ == '__main__':&lt;br /&gt;
    import sys&lt;br /&gt;
    x = inkSlide()&lt;br /&gt;
    x.instanceFromTemplate(sys.argv[1], 'slide%s.svg', file(sys.argv[2]).read())&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16498</id>
		<title>InkSlide</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16498"/>
		<updated>2007-10-23T17:36:58Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: Undo revision 16497 by Terry brown (Talk)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== InkSlide - quick and easy presentations using Inkscape ==&lt;br /&gt;
&lt;br /&gt;
InkSlide produces slides like this:&lt;br /&gt;
&lt;br /&gt;
[[Image:slide0004.jpg]]&lt;br /&gt;
&lt;br /&gt;
from simple text input like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
InkSlide: Features&lt;br /&gt;
++++++++++++++++++&lt;br /&gt;
&lt;br /&gt;
Features include wrapped top level text and&lt;br /&gt;
- mulitple&lt;br /&gt;
  - levels&lt;br /&gt;
    - of wrapped bulleted lists with bullets and font&lt;br /&gt;
      information taken from the template file.&lt;br /&gt;
&lt;br /&gt;
Slide specific content like this:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
which is updated when the template changes.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An Inkscape file is used as a template file to define the background, title position and font, fonts and positions for text at different levels of indentation, groups to be cloned and used as bullets, etc.&lt;br /&gt;
&lt;br /&gt;
Content specific to a particular slide can also be created in Inkscape, this content merged with the template and text input to make the final slide, so changes to the template after a particular slide is edited in Inkscape are included.&lt;br /&gt;
&lt;br /&gt;
=== Download ===&lt;br /&gt;
&lt;br /&gt;
Copy the [[InkSlide#Code|text below]] into a file called 'inkslide.py'.  (How do you attach a non-image file on this wiki?)&lt;br /&gt;
&lt;br /&gt;
Here is an [[Media:template0.svg|example template]].  Use Save Link As to stop your browser displaying the file instead of downloading it.&lt;br /&gt;
&lt;br /&gt;
=== How it works ===&lt;br /&gt;
&lt;br /&gt;
First InkSlide parses the text input, something like the example at the top of this page.  It looks for flowed text boxes in the template file with special @id attributes like ''fr_title'' for the title, and ''fr_tab2'' for the second level of bulleted lists.  Copies of these elements are made and placed in a layer called 'Texts'.  At this point, the text is all piled up at the top of the page.&lt;br /&gt;
&lt;br /&gt;
InkSlide saves the file, and uses Inkscape's command line ''--query-height'' and friends to find out how high the wrapped text pieces are.  At it then repositions them properly, adds clones of the bullet markers, etc.  Bullet markers have special @id attributes like ''bu_1'' for the first level of bulleted list.&lt;br /&gt;
&lt;br /&gt;
Finally, InkSlide checks to see if there's an id tag for the slide, and if so if there's a corresponding Inkscape SVG file with content for this particular slide. This content has to occur in layers whose name starts with ''Instance'', and these layers have to be present in the template file, but you can have more than one, so they can be above and below the regular content.  If such content is found, it's copied into the output slide.&lt;br /&gt;
&lt;br /&gt;
=== The Template File ===&lt;br /&gt;
&lt;br /&gt;
The template file is a regular Inkscape file.  If this list seems hard, just use the example template and modify it to suit your needs.&lt;br /&gt;
&lt;br /&gt;
Required contents:&lt;br /&gt;
&lt;br /&gt;
*A group or layer called ''gr_text'', into which text will be placed.&lt;br /&gt;
*A flowed text frame with an @id of ''fr_title'' to define the font and position for the slide title.  This should contain text like ''Title goes here Sly'' because ''Sly'' includes letters which ascend and descend from the baseline to the greatest extent.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*A group or layer called ''gr_title'' into which the title will be placed.  This makes it easier to do special effects on the title like shadows.&lt;br /&gt;
*Flowed text frames with an @id of ''fr_tabN'' for each N in 0, 1, 2, 3.  ''fr_tab0'' is used for top level non bulleted text.  These should also include ''Sly'' or similar.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*Objects or groups withan @id of ''bu_N'' for each N in 1, 2, 3, for the three levels of bulleted list.&lt;br /&gt;
&lt;br /&gt;
The fr_* elements define the font, font-size, color etc. for the different levels of text.  They also define the position and wrap width.  See the example template file for a possible arrangement.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;A group or layer called 'X'&amp;quot; indicates either a group with an '''@id'''&lt;br /&gt;
of 'X' ''or'' a layer with a '''label''' (name) of 'X'.  When both exist the group will take precedence.&lt;br /&gt;
 &lt;br /&gt;
Optional contents:&lt;br /&gt;
&lt;br /&gt;
*A background, which should be placed in a group or layer called `gr_bg`&lt;br /&gt;
*A foreground, which should be placed in a group or layer called `gr_fg`&lt;br /&gt;
*Affiliation text and logos you want to appear on every slide, this could go in the background, or another named group or layer.&lt;br /&gt;
*A blurred clone of the gr_title element, offset and behind it.  It's easier to do this is `gr_title` is a group rather than a layer.&lt;br /&gt;
*Layers whose name starts with ''Instance'' - these are the layers the user can edit directly in Inkscape for a particular slide.&lt;br /&gt;
&lt;br /&gt;
When you save the template file you should make sure that layers containing place holder text (the fr_* elements) and bullets are invisible.  Likewise the Texts layer should be visible, as well as any Instance layers.&lt;br /&gt;
=== Text Input ===&lt;br /&gt;
&lt;br /&gt;
InkSlide uses [http://docutils.sourceforge.net/rst.html reStructuredText] for markup, although it renders only a subset of rst.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Inkslide: an example&lt;br /&gt;
++++++++++++++++++++&lt;br /&gt;
&lt;br /&gt;
This text will appear as a top level paragraph.&lt;br /&gt;
&lt;br /&gt;
- Dash prefix indicates this text will appear as a bulleted item.&lt;br /&gt;
&lt;br /&gt;
  - Indented dash prefix -&amp;gt; second level bulleted list.&lt;br /&gt;
&lt;br /&gt;
.. is:pause&lt;br /&gt;
&lt;br /&gt;
Two slides will be produced for this input, one with everything up to the&lt;br /&gt;
&amp;quot;.. is:pause&amp;quot;, and one with that plus the remaining content.&lt;br /&gt;
Incremental list display can be done this way.&lt;br /&gt;
&lt;br /&gt;
The is:id tag below indicates that a file with a name like&lt;br /&gt;
'extra/is_intro.svg' should be inspected for extra content&lt;br /&gt;
for this particular slide.&lt;br /&gt;
&lt;br /&gt;
.. is:id is_intro&lt;br /&gt;
&lt;br /&gt;
Inkslide: the next slide...&lt;br /&gt;
+++++++++++++++++++++++++++&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== The GUI ===&lt;br /&gt;
&lt;br /&gt;
A GUI for InkSlide is available as a plug-in for the [http://webpages.charter.net/edreamleo/front.html Leo] outline editor.  This makes slide re-arranging easy, and adds some macro substitution capability.  It also adds buttons for editing slide specific content in Inkscape.&lt;br /&gt;
&lt;br /&gt;
FIXME: post plug-in on Leo wiki and link.&lt;br /&gt;
&lt;br /&gt;
=== To do / status ===&lt;br /&gt;
&lt;br /&gt;
InkSlide is very usable, but is missing some features.  Remember you can &lt;br /&gt;
use the slide specific editing feature to do any of these things and&lt;br /&gt;
anything else you can do in Inkscape already, these will just be more&lt;br /&gt;
convenient in the text input when they're done.&lt;br /&gt;
&lt;br /&gt;
*Inline text formatting (bold, color, etc.) for part of a piece of text&lt;br /&gt;
*Justification switching&lt;br /&gt;
*Verbatim code display&lt;br /&gt;
*&amp;lt;strike&amp;gt;Simple image inclusion&amp;lt;/strike&amp;gt;&lt;br /&gt;
*more helper tools to make PNG and PDF versions of slides&lt;br /&gt;
*configuring / using a directory for edited slides&lt;br /&gt;
*command line flags for generating specific slides&lt;br /&gt;
&lt;br /&gt;
Currently InkSlide is slow, because it must invoke Inkscape once for every single dimension it needs to retrieve.  I'll try and get a --query-all switch into 0.46.&lt;br /&gt;
&lt;br /&gt;
=== Bugs ===&lt;br /&gt;
&lt;br /&gt;
*There may be an issue with the way slide specific content is merged into the template producing conflicting @id attributes, but I haven't seen this happen yet.&lt;br /&gt;
&lt;br /&gt;
=== Author  / Credits ===&lt;br /&gt;
&lt;br /&gt;
InkSlide was written by Terry Brown, terry-n-brown@yahoo.com - but use underscore, not '-'.&lt;br /&gt;
&lt;br /&gt;
InkSlide was inspired by [http://member.wide.ad.jp/wg/mgp/ MagicPoint]&lt;br /&gt;
&lt;br /&gt;
Matt Harrison suggested using reStructured text as the markup language.&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# inkslide.py $Id$&lt;br /&gt;
# Author: Terry Brown&lt;br /&gt;
# Created: Thu Oct 11 2007&lt;br /&gt;
&lt;br /&gt;
import lxml.etree as ET&lt;br /&gt;
&lt;br /&gt;
import copy&lt;br /&gt;
import subprocess&lt;br /&gt;
import os.path&lt;br /&gt;
&lt;br /&gt;
NS = { 'i': 'http://www.inkscape.org/namespaces/inkscape',&lt;br /&gt;
       's': 'http://www.w3.org/2000/svg',&lt;br /&gt;
       'xlink' : 'http://www.w3.org/1999/xlink'}&lt;br /&gt;
&lt;br /&gt;
# number of defined tablevels, FIXME, could derive from template?&lt;br /&gt;
tabLevels = 4  &lt;br /&gt;
def clearId(x, what='id', NS={}):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;recursively clear @id on element x and descendants&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if what in x.keys():&lt;br /&gt;
        del x.attrib[what]&lt;br /&gt;
    for i in x.xpath('.//*[@%s]'%what, NS):&lt;br /&gt;
        del i.attrib[what]&lt;br /&gt;
    return x&lt;br /&gt;
def getDim(fn, Id, what):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return dimension of element in fn with @id Id, what is&lt;br /&gt;
    x, y, width, height&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cmd = ('inkscape', '--without-gui', '--query-id', Id, '--query-'+what, fn)&lt;br /&gt;
    proc = subprocess.Popen(cmd, stdout = subprocess.PIPE,&lt;br /&gt;
                            stderr = subprocess.PIPE)&lt;br /&gt;
    # make new pipe for stderr to supress chatter from inkscape&lt;br /&gt;
    proc.wait()&lt;br /&gt;
    return proc.stdout.read()&lt;br /&gt;
def textReader(text):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;iterate parts in input text&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not text: return&lt;br /&gt;
    for line in text.split('\n'):&lt;br /&gt;
        if line.startswith('%slide '):&lt;br /&gt;
            yield {'type':'TITLE', 's':line[7:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%pause'):&lt;br /&gt;
            yield {'type':'PAUSE'}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%id '):&lt;br /&gt;
            yield {'type':'ID', 's':line[4:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if not line.strip():&lt;br /&gt;
            yield {'type':'BLANK'}&lt;br /&gt;
            continue&lt;br /&gt;
        tabs = len(line) - len(line.lstrip('\t'))&lt;br /&gt;
        yield {'type':'TAB', 'tabs':tabs, 's':line.strip()}&lt;br /&gt;
&lt;br /&gt;
nid = 0&lt;br /&gt;
def nextId():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return an unsed Id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # FIXME, should inspect templat doc.&lt;br /&gt;
    global nid&lt;br /&gt;
    nid += 1&lt;br /&gt;
    return 'is'+str(nid)&lt;br /&gt;
&lt;br /&gt;
def instanceFromTemplate(template, instance, text, pauseOk = True):&lt;br /&gt;
&lt;br /&gt;
    slide = 0&lt;br /&gt;
&lt;br /&gt;
    hlist = []&lt;br /&gt;
    doc0 = ET.parse(template)&lt;br /&gt;
    slideId = None&lt;br /&gt;
    &lt;br /&gt;
    for t in textReader(text):&lt;br /&gt;
        &lt;br /&gt;
        if t['type'] == 'ID':&lt;br /&gt;
            slideId = t['s']&lt;br /&gt;
            continue&lt;br /&gt;
            &lt;br /&gt;
        if t['type'] == 'TITLE':&lt;br /&gt;
            print t['s']&lt;br /&gt;
            if hlist:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
                hlist = [t]&lt;br /&gt;
                doc0 = ET.parse(template)&lt;br /&gt;
                slideId = None&lt;br /&gt;
            else:&lt;br /&gt;
                hlist.append(t)&lt;br /&gt;
            dst = doc0.xpath('//s:g[@id=&amp;quot;gr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'PAUSE':&lt;br /&gt;
            if pauseOk:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        hlist.append(t)&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'BLANK':&lt;br /&gt;
            continue&lt;br /&gt;
        if t['type'] == 'TAB':&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_tab%d&amp;quot;]' % t['tabs'], NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            #break&lt;br /&gt;
            continue&lt;br /&gt;
           &lt;br /&gt;
    process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
def process(instance, doc0, hlist, slideId, slide):&lt;br /&gt;
&lt;br /&gt;
    instance = instance.replace('%s', '%04d'%slide)&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
&lt;br /&gt;
    bullDim = {}&lt;br /&gt;
    tabDim = {}&lt;br /&gt;
    for n in range(0,tabLevels):&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;bu_%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'width', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'bu_%d'%n, i))&lt;br /&gt;
            bullDim[n] = b&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;fr_tab%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'fr_tab%d'%n, i))&lt;br /&gt;
            tabDim[n] = b&lt;br /&gt;
&lt;br /&gt;
    delta = 0.&lt;br /&gt;
    firstVert = True&lt;br /&gt;
    for n, i in enumerate(hlist):&lt;br /&gt;
        if hlist[n]['type'] in ['TITLE']:&lt;br /&gt;
            continue  # already in right place, not a vertical element&lt;br /&gt;
&lt;br /&gt;
        hgt = 0&lt;br /&gt;
        if hlist[n-1]['type'] == 'BLANK':&lt;br /&gt;
            hgt = 30&lt;br /&gt;
        else:&lt;br /&gt;
            hgt = float(getDim(instance, hlist[n-1]['id'], 'height'))&lt;br /&gt;
&lt;br /&gt;
        hgt += 5&lt;br /&gt;
&lt;br /&gt;
        if firstVert:&lt;br /&gt;
            hgt = 0&lt;br /&gt;
            firstVert = False&lt;br /&gt;
&lt;br /&gt;
        delta += hgt&lt;br /&gt;
&lt;br /&gt;
        if hlist[n]['type'] == 'BLANK': continue&lt;br /&gt;
&lt;br /&gt;
        tabs = hlist[n]['tabs']&lt;br /&gt;
&lt;br /&gt;
        r = doc0.xpath('//s:flowRoot[@id=&amp;quot;%s&amp;quot;]' % hlist[n]['id'], NS)[0]&lt;br /&gt;
        clearId(r, what='transform')&lt;br /&gt;
        r = r.xpath('.//s:rect', NS)[0]&lt;br /&gt;
        r.set('x', str(tabDim[tabs]['x']))&lt;br /&gt;
        y = tabDim[tabs]['y'] + delta&lt;br /&gt;
        #print '%s -&amp;gt; %s' % (str(r.get('y')), str(y))&lt;br /&gt;
        #print tabDim[tabs]['y']&lt;br /&gt;
        r.set('y', str(y))&lt;br /&gt;
&lt;br /&gt;
        if (hlist[n]['type'] == 'TAB' and hlist[n]['tabs'] in bullDim&lt;br /&gt;
            and 'isDone' not in hlist[n]):&lt;br /&gt;
            hlist[n]['isDone'] = True&lt;br /&gt;
            dx = (tabDim[tabs]['x'] &lt;br /&gt;
                  - bullDim[tabs]['x']&lt;br /&gt;
                  - 1.5*bullDim[tabs]['width']&lt;br /&gt;
                  )&lt;br /&gt;
            dy = (y &lt;br /&gt;
                  + tabDim[tabs]['height'] / 2.&lt;br /&gt;
                  - bullDim[tabs]['y'] &lt;br /&gt;
                  - bullDim[tabs]['height'] / 2.&lt;br /&gt;
                  )&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            #print y, tabDim[tabs]['height'], bullDim[tabs]['y'], bullDim[tabs]['height'], dy&lt;br /&gt;
            clone = ET.Element('svg:use')&lt;br /&gt;
            clone.set('transform', 'translate(%f,%f)' % (dx,dy))&lt;br /&gt;
            clone.set('{%s}href'%NS['xlink'], '#bu_%d'%tabs)&lt;br /&gt;
            dst.append(clone)&lt;br /&gt;
            &lt;br /&gt;
            #path = ET.Element('svg:path')&lt;br /&gt;
            #path.set('d', ('M %f,%f' + ' L %f,%f'*5 + ' z') % (&lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y'], &lt;br /&gt;
            #    bullDim[tabs]['x']+20, bullDim[tabs]['y']+bullDim[tabs]['height']/2., &lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y']+bullDim[tabs]['height'], &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y'], &lt;br /&gt;
            #    tabDim[tabs]['x']+20, tabDim[tabs]['y']+tabDim[tabs]['height']/2., &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y']+tabDim[tabs]['height'], &lt;br /&gt;
            #    ))&lt;br /&gt;
            #dst.append(path)&lt;br /&gt;
            #break&lt;br /&gt;
&lt;br /&gt;
    # look for instance specific parts&lt;br /&gt;
    if slideId:&lt;br /&gt;
        f = instancePath(slideId)&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            comp = ET.parse(f)&lt;br /&gt;
            svg = doc0.xpath('//s:svg', NS)[0]&lt;br /&gt;
            g = &amp;quot;{%s}g&amp;quot;%NS['s']&lt;br /&gt;
            k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
            for n, i in enumerate(svg.getchildren()):&lt;br /&gt;
                if i.tag == g and i.get(k, '').startswith('Instance'):&lt;br /&gt;
                    x = comp.xpath('//s:svg//s:g[@i:label=&amp;quot;%s&amp;quot;]'%i.get(k), NS)&lt;br /&gt;
                    if x:&lt;br /&gt;
                        x = x[0]&lt;br /&gt;
                        cpy = clearId(copy.deepcopy(x))&lt;br /&gt;
                        svg[n] = cpy&lt;br /&gt;
            defs = comp.xpath('//s:svg/s:defs/*', NS)&lt;br /&gt;
            dst = doc0.xpath('//s:svg/s:defs', NS)[0]&lt;br /&gt;
            for d in defs:&lt;br /&gt;
                Id = d.get('id')&lt;br /&gt;
                current = doc0.xpath('//s:svg/s:defs/*[@id=&amp;quot;%s&amp;quot;]'%Id, NS)&lt;br /&gt;
                if not current:&lt;br /&gt;
                    cpy = copy.deepcopy(d)&lt;br /&gt;
                    dst.append(cpy)&lt;br /&gt;
&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
    &lt;br /&gt;
    return slideId&lt;br /&gt;
def instancePath(slideId, component = None):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return path to component for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # return os.path.join(slideId, component)&lt;br /&gt;
    return slideId+'.svg'&lt;br /&gt;
def writeComponents(inkscapeFile, slideId):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;write components from file for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not slideId: return&lt;br /&gt;
    doc0 = ET.parse(inkscapeFile)&lt;br /&gt;
    hasComponents = False&lt;br /&gt;
    for i in doc0.xpath('//s:svg//s:g', NS):&lt;br /&gt;
        k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
        # print i.keys(),k&lt;br /&gt;
        if (k in i.keys() and i.get(k).startswith('Instance')):&lt;br /&gt;
            if len(i) &amp;gt; 0:&lt;br /&gt;
                hasComponents = True&lt;br /&gt;
                break&lt;br /&gt;
                # ET.ElementTree(i).write(f)&lt;br /&gt;
                &lt;br /&gt;
    f = instancePath(slideId, 'components')&lt;br /&gt;
    &lt;br /&gt;
    if hasComponents:&lt;br /&gt;
        if (os.path.abspath(os.path.realpath(inkscapeFile)) != &lt;br /&gt;
            os.path.abspath(os.path.realpath(f))):&lt;br /&gt;
            file(f, 'w').write(file(inkscapeFile).read())&lt;br /&gt;
    else:&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            os.remove(f)&lt;br /&gt;
&lt;br /&gt;
if __name__ == '__main__':&lt;br /&gt;
    import sys&lt;br /&gt;
    instanceFromTemplate(sys.argv[1], 'slide%s.svg', file(sys.argv[2]).read())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16497</id>
		<title>InkSlide</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16497"/>
		<updated>2007-10-23T17:30:58Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: /* Download */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== InkSlide - quick and easy presentations using Inkscape ==&lt;br /&gt;
&lt;br /&gt;
InkSlide produces slides like this:&lt;br /&gt;
&lt;br /&gt;
[[Image:slide0004.jpg]]&lt;br /&gt;
&lt;br /&gt;
from simple text input like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
InkSlide: Features&lt;br /&gt;
=== Download ===&lt;br /&gt;
&lt;br /&gt;
Copy the [[InkSlide#Code|text below]] into a file called 'inkslide.py'.  (How do you attach a non-image file on this wiki?)&lt;br /&gt;
&lt;br /&gt;
InkSlide is undergoing rapid development at the moment and the code below may be out of date and may not match these instructions.&lt;br /&gt;
&lt;br /&gt;
Here is an [[Media:template0.svg|example template]].  Use Save Link As to stop your browser displaying the file instead of downloading it.&lt;br /&gt;
&lt;br /&gt;
=== Download ===&lt;br /&gt;
&lt;br /&gt;
Copy the [[InkSlide#Code|text below]] into a file called 'inkslide.py'.  (How do you attach a non-image file on this wiki?)&lt;br /&gt;
&lt;br /&gt;
Here is an [[Media:template0.svg|example template]].  Use Save Link As to stop your browser displaying the file instead of downloading it.&lt;br /&gt;
&lt;br /&gt;
=== How it works ===&lt;br /&gt;
&lt;br /&gt;
First InkSlide parses the text input, something like the example at the top of this page.  It looks for flowed text boxes in the template file with special @id attributes like ''fr_title'' for the title, and ''fr_tab2'' for the second level of bulleted lists.  Copies of these elements are made and placed in a layer called 'Texts'.  At this point, the text is all piled up at the top of the page.&lt;br /&gt;
&lt;br /&gt;
InkSlide saves the file, and uses Inkscape's command line ''--query-height'' and friends to find out how high the wrapped text pieces are.  At it then repositions them properly, adds clones of the bullet markers, etc.  Bullet markers have special @id attributes like ''bu_1'' for the first level of bulleted list.&lt;br /&gt;
&lt;br /&gt;
Finally, InkSlide checks to see if there's an id tag for the slide, and if so if there's a corresponding Inkscape SVG file with content for this particular slide. This content has to occur in layers whose name starts with ''Instance'', and these layers have to be present in the template file, but you can have more than one, so they can be above and below the regular content.  If such content is found, it's copied into the output slide.&lt;br /&gt;
&lt;br /&gt;
=== The Template File ===&lt;br /&gt;
&lt;br /&gt;
The template file is a regular Inkscape file.  If this list seems hard, just use the example template and modify it to suit your needs.&lt;br /&gt;
&lt;br /&gt;
Required contents:&lt;br /&gt;
&lt;br /&gt;
*A group or layer called ''gr_text'', into which text will be placed.&lt;br /&gt;
*A flowed text frame with an @id of ''fr_title'' to define the font and position for the slide title.  This should contain text like ''Title goes here Sly'' because ''Sly'' includes letters which ascend and descend from the baseline to the greatest extent.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*A group or layer called ''gr_title'' into which the title will be placed.  This makes it easier to do special effects on the title like shadows.&lt;br /&gt;
*Flowed text frames with an @id of ''fr_tabN'' for each N in 0, 1, 2, 3.  ''fr_tab0'' is used for top level non bulleted text.  These should also include ''Sly'' or similar.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*Objects or groups withan @id of ''bu_N'' for each N in 1, 2, 3, for the three levels of bulleted list.&lt;br /&gt;
&lt;br /&gt;
The fr_* elements define the font, font-size, color etc. for the different levels of text.  They also define the position and wrap width.  See the example template file for a possible arrangement.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;A group or layer called 'X'&amp;quot; indicates either a group with an '''@id'''&lt;br /&gt;
of 'X' ''or'' a layer with a '''label''' (name) of 'X'.  When both exist the group will take precedence.&lt;br /&gt;
 &lt;br /&gt;
Optional contents:&lt;br /&gt;
&lt;br /&gt;
*A background, which should be placed in a group or layer called `gr_bg`&lt;br /&gt;
*A foreground, which should be placed in a group or layer called `gr_fg`&lt;br /&gt;
*Affiliation text and logos you want to appear on every slide, this could go in the background, or another named group or layer.&lt;br /&gt;
*A blurred clone of the gr_title element, offset and behind it.  It's easier to do this is `gr_title` is a group rather than a layer.&lt;br /&gt;
*Layers whose name starts with ''Instance'' - these are the layers the user can edit directly in Inkscape for a particular slide.&lt;br /&gt;
&lt;br /&gt;
When you save the template file you should make sure that layers containing place holder text (the fr_* elements) and bullets are invisible.  Likewise the Texts layer should be visible, as well as any Instance layers.&lt;br /&gt;
=== Text Input ===&lt;br /&gt;
&lt;br /&gt;
InkSlide uses [http://docutils.sourceforge.net/rst.html reStructuredText] for markup, although it renders only a subset of rst.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Inkslide: an example&lt;br /&gt;
====================&lt;br /&gt;
&lt;br /&gt;
This text will appear as a top level paragraph.&lt;br /&gt;
&lt;br /&gt;
- Dash prefix indicates this text will appear as a bulleted item.&lt;br /&gt;
&lt;br /&gt;
  - Indented dash prefix -&amp;gt; second level bulleted list.&lt;br /&gt;
&lt;br /&gt;
.. is:pause&lt;br /&gt;
&lt;br /&gt;
Two slides will be produced for this input, one with everything up to the&lt;br /&gt;
&amp;quot;.. is:pause&amp;quot;, and one with that plus the remaining content.&lt;br /&gt;
Incremental list display can be done this way.&lt;br /&gt;
&lt;br /&gt;
The is:id tag below indicates that a file with a name like&lt;br /&gt;
'extra/is_intro.svg' should be inspected for extra content&lt;br /&gt;
for this particular slide.&lt;br /&gt;
&lt;br /&gt;
.. is:id is_intro&lt;br /&gt;
&lt;br /&gt;
Inkslide: the next slide...&lt;br /&gt;
===========================&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== The GUI ===&lt;br /&gt;
&lt;br /&gt;
A GUI for InkSlide is available as a plug-in for the [http://webpages.charter.net/edreamleo/front.html Leo] outline editor.  This makes slide re-arranging easy, and adds some macro substitution capability.  It also adds buttons for editing slide specific content in Inkscape.&lt;br /&gt;
&lt;br /&gt;
FIXME: post plug-in on Leo wiki and link.&lt;br /&gt;
&lt;br /&gt;
=== To do / status ===&lt;br /&gt;
&lt;br /&gt;
InkSlide is very usable, but is missing some features.  Remember you can &lt;br /&gt;
use the slide specific editing feature to do any of these things and&lt;br /&gt;
anything else you can do in Inkscape already, these will just be more&lt;br /&gt;
convenient in the text input when they're done.&lt;br /&gt;
&lt;br /&gt;
*Inline text formatting (bold, color, etc.) for part of a piece of text&lt;br /&gt;
*Justification switching&lt;br /&gt;
*Verbatim code display&lt;br /&gt;
*&amp;lt;strike&amp;gt;Simple image inclusion&amp;lt;/strike&amp;gt;&lt;br /&gt;
*more helper tools to make PNG and PDF versions of slides&lt;br /&gt;
*configuring / using a directory for edited slides&lt;br /&gt;
*command line flags for generating specific slides&lt;br /&gt;
&lt;br /&gt;
Currently InkSlide is slow, because it must invoke Inkscape once for every single dimension it needs to retrieve.  I'll try and get a --query-all switch into 0.46.&lt;br /&gt;
&lt;br /&gt;
=== Bugs ===&lt;br /&gt;
&lt;br /&gt;
*There may be an issue with the way slide specific content is merged into the template producing conflicting @id attributes, but I haven't seen this happen yet.&lt;br /&gt;
&lt;br /&gt;
=== Author  / Credits ===&lt;br /&gt;
&lt;br /&gt;
InkSlide was written by Terry Brown, terry-n-brown@yahoo.com - but use underscore, not '-'.&lt;br /&gt;
&lt;br /&gt;
InkSlide was inspired by [http://member.wide.ad.jp/wg/mgp/ MagicPoint]&lt;br /&gt;
&lt;br /&gt;
Matt Harrison suggested using reStructured text as the markup language.&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# inkslide.py $Id$&lt;br /&gt;
# Author: Terry Brown&lt;br /&gt;
# Created: Thu Oct 11 2007&lt;br /&gt;
&lt;br /&gt;
import lxml.etree as ET&lt;br /&gt;
&lt;br /&gt;
import copy&lt;br /&gt;
import subprocess&lt;br /&gt;
import os.path&lt;br /&gt;
&lt;br /&gt;
NS = { 'i': 'http://www.inkscape.org/namespaces/inkscape',&lt;br /&gt;
       's': 'http://www.w3.org/2000/svg',&lt;br /&gt;
       'xlink' : 'http://www.w3.org/1999/xlink'}&lt;br /&gt;
&lt;br /&gt;
# number of defined tablevels, FIXME, could derive from template?&lt;br /&gt;
tabLevels = 4  &lt;br /&gt;
def clearId(x, what='id', NS={}):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;recursively clear @id on element x and descendants&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if what in x.keys():&lt;br /&gt;
        del x.attrib[what]&lt;br /&gt;
    for i in x.xpath('.//*[@%s]'%what, NS):&lt;br /&gt;
        del i.attrib[what]&lt;br /&gt;
    return x&lt;br /&gt;
def getDim(fn, Id, what):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return dimension of element in fn with @id Id, what is&lt;br /&gt;
    x, y, width, height&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cmd = ('inkscape', '--without-gui', '--query-id', Id, '--query-'+what, fn)&lt;br /&gt;
    proc = subprocess.Popen(cmd, stdout = subprocess.PIPE,&lt;br /&gt;
                            stderr = subprocess.PIPE)&lt;br /&gt;
    # make new pipe for stderr to supress chatter from inkscape&lt;br /&gt;
    proc.wait()&lt;br /&gt;
    return proc.stdout.read()&lt;br /&gt;
def textReader(text):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;iterate parts in input text&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not text: return&lt;br /&gt;
    for line in text.split('\n'):&lt;br /&gt;
        if line.startswith('%slide '):&lt;br /&gt;
            yield {'type':'TITLE', 's':line[7:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%pause'):&lt;br /&gt;
            yield {'type':'PAUSE'}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%id '):&lt;br /&gt;
            yield {'type':'ID', 's':line[4:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if not line.strip():&lt;br /&gt;
            yield {'type':'BLANK'}&lt;br /&gt;
            continue&lt;br /&gt;
        tabs = len(line) - len(line.lstrip('\t'))&lt;br /&gt;
        yield {'type':'TAB', 'tabs':tabs, 's':line.strip()}&lt;br /&gt;
&lt;br /&gt;
nid = 0&lt;br /&gt;
def nextId():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return an unsed Id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # FIXME, should inspect templat doc.&lt;br /&gt;
    global nid&lt;br /&gt;
    nid += 1&lt;br /&gt;
    return 'is'+str(nid)&lt;br /&gt;
&lt;br /&gt;
def instanceFromTemplate(template, instance, text, pauseOk = True):&lt;br /&gt;
&lt;br /&gt;
    slide = 0&lt;br /&gt;
&lt;br /&gt;
    hlist = []&lt;br /&gt;
    doc0 = ET.parse(template)&lt;br /&gt;
    slideId = None&lt;br /&gt;
    &lt;br /&gt;
    for t in textReader(text):&lt;br /&gt;
        &lt;br /&gt;
        if t['type'] == 'ID':&lt;br /&gt;
            slideId = t['s']&lt;br /&gt;
            continue&lt;br /&gt;
            &lt;br /&gt;
        if t['type'] == 'TITLE':&lt;br /&gt;
            print t['s']&lt;br /&gt;
            if hlist:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
                hlist = [t]&lt;br /&gt;
                doc0 = ET.parse(template)&lt;br /&gt;
                slideId = None&lt;br /&gt;
            else:&lt;br /&gt;
                hlist.append(t)&lt;br /&gt;
            dst = doc0.xpath('//s:g[@id=&amp;quot;gr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'PAUSE':&lt;br /&gt;
            if pauseOk:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        hlist.append(t)&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'BLANK':&lt;br /&gt;
            continue&lt;br /&gt;
        if t['type'] == 'TAB':&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_tab%d&amp;quot;]' % t['tabs'], NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            #break&lt;br /&gt;
            continue&lt;br /&gt;
           &lt;br /&gt;
    process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
def process(instance, doc0, hlist, slideId, slide):&lt;br /&gt;
&lt;br /&gt;
    instance = instance.replace('%s', '%04d'%slide)&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
&lt;br /&gt;
    bullDim = {}&lt;br /&gt;
    tabDim = {}&lt;br /&gt;
    for n in range(0,tabLevels):&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;bu_%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'width', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'bu_%d'%n, i))&lt;br /&gt;
            bullDim[n] = b&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;fr_tab%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'fr_tab%d'%n, i))&lt;br /&gt;
            tabDim[n] = b&lt;br /&gt;
&lt;br /&gt;
    delta = 0.&lt;br /&gt;
    firstVert = True&lt;br /&gt;
    for n, i in enumerate(hlist):&lt;br /&gt;
        if hlist[n]['type'] in ['TITLE']:&lt;br /&gt;
            continue  # already in right place, not a vertical element&lt;br /&gt;
&lt;br /&gt;
        hgt = 0&lt;br /&gt;
        if hlist[n-1]['type'] == 'BLANK':&lt;br /&gt;
            hgt = 30&lt;br /&gt;
        else:&lt;br /&gt;
            hgt = float(getDim(instance, hlist[n-1]['id'], 'height'))&lt;br /&gt;
&lt;br /&gt;
        hgt += 5&lt;br /&gt;
&lt;br /&gt;
        if firstVert:&lt;br /&gt;
            hgt = 0&lt;br /&gt;
            firstVert = False&lt;br /&gt;
&lt;br /&gt;
        delta += hgt&lt;br /&gt;
&lt;br /&gt;
        if hlist[n]['type'] == 'BLANK': continue&lt;br /&gt;
&lt;br /&gt;
        tabs = hlist[n]['tabs']&lt;br /&gt;
&lt;br /&gt;
        r = doc0.xpath('//s:flowRoot[@id=&amp;quot;%s&amp;quot;]' % hlist[n]['id'], NS)[0]&lt;br /&gt;
        clearId(r, what='transform')&lt;br /&gt;
        r = r.xpath('.//s:rect', NS)[0]&lt;br /&gt;
        r.set('x', str(tabDim[tabs]['x']))&lt;br /&gt;
        y = tabDim[tabs]['y'] + delta&lt;br /&gt;
        #print '%s -&amp;gt; %s' % (str(r.get('y')), str(y))&lt;br /&gt;
        #print tabDim[tabs]['y']&lt;br /&gt;
        r.set('y', str(y))&lt;br /&gt;
&lt;br /&gt;
        if (hlist[n]['type'] == 'TAB' and hlist[n]['tabs'] in bullDim&lt;br /&gt;
            and 'isDone' not in hlist[n]):&lt;br /&gt;
            hlist[n]['isDone'] = True&lt;br /&gt;
            dx = (tabDim[tabs]['x'] &lt;br /&gt;
                  - bullDim[tabs]['x']&lt;br /&gt;
                  - 1.5*bullDim[tabs]['width']&lt;br /&gt;
                  )&lt;br /&gt;
            dy = (y &lt;br /&gt;
                  + tabDim[tabs]['height'] / 2.&lt;br /&gt;
                  - bullDim[tabs]['y'] &lt;br /&gt;
                  - bullDim[tabs]['height'] / 2.&lt;br /&gt;
                  )&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            #print y, tabDim[tabs]['height'], bullDim[tabs]['y'], bullDim[tabs]['height'], dy&lt;br /&gt;
            clone = ET.Element('svg:use')&lt;br /&gt;
            clone.set('transform', 'translate(%f,%f)' % (dx,dy))&lt;br /&gt;
            clone.set('{%s}href'%NS['xlink'], '#bu_%d'%tabs)&lt;br /&gt;
            dst.append(clone)&lt;br /&gt;
            &lt;br /&gt;
            #path = ET.Element('svg:path')&lt;br /&gt;
            #path.set('d', ('M %f,%f' + ' L %f,%f'*5 + ' z') % (&lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y'], &lt;br /&gt;
            #    bullDim[tabs]['x']+20, bullDim[tabs]['y']+bullDim[tabs]['height']/2., &lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y']+bullDim[tabs]['height'], &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y'], &lt;br /&gt;
            #    tabDim[tabs]['x']+20, tabDim[tabs]['y']+tabDim[tabs]['height']/2., &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y']+tabDim[tabs]['height'], &lt;br /&gt;
            #    ))&lt;br /&gt;
            #dst.append(path)&lt;br /&gt;
            #break&lt;br /&gt;
&lt;br /&gt;
    # look for instance specific parts&lt;br /&gt;
    if slideId:&lt;br /&gt;
        f = instancePath(slideId)&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            comp = ET.parse(f)&lt;br /&gt;
            svg = doc0.xpath('//s:svg', NS)[0]&lt;br /&gt;
            g = &amp;quot;{%s}g&amp;quot;%NS['s']&lt;br /&gt;
            k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
            for n, i in enumerate(svg.getchildren()):&lt;br /&gt;
                if i.tag == g and i.get(k, '').startswith('Instance'):&lt;br /&gt;
                    x = comp.xpath('//s:svg//s:g[@i:label=&amp;quot;%s&amp;quot;]'%i.get(k), NS)&lt;br /&gt;
                    if x:&lt;br /&gt;
                        x = x[0]&lt;br /&gt;
                        cpy = clearId(copy.deepcopy(x))&lt;br /&gt;
                        svg[n] = cpy&lt;br /&gt;
            defs = comp.xpath('//s:svg/s:defs/*', NS)&lt;br /&gt;
            dst = doc0.xpath('//s:svg/s:defs', NS)[0]&lt;br /&gt;
            for d in defs:&lt;br /&gt;
                Id = d.get('id')&lt;br /&gt;
                current = doc0.xpath('//s:svg/s:defs/*[@id=&amp;quot;%s&amp;quot;]'%Id, NS)&lt;br /&gt;
                if not current:&lt;br /&gt;
                    cpy = copy.deepcopy(d)&lt;br /&gt;
                    dst.append(cpy)&lt;br /&gt;
&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
    &lt;br /&gt;
    return slideId&lt;br /&gt;
def instancePath(slideId, component = None):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return path to component for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # return os.path.join(slideId, component)&lt;br /&gt;
    return slideId+'.svg'&lt;br /&gt;
def writeComponents(inkscapeFile, slideId):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;write components from file for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not slideId: return&lt;br /&gt;
    doc0 = ET.parse(inkscapeFile)&lt;br /&gt;
    hasComponents = False&lt;br /&gt;
    for i in doc0.xpath('//s:svg//s:g', NS):&lt;br /&gt;
        k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
        # print i.keys(),k&lt;br /&gt;
        if (k in i.keys() and i.get(k).startswith('Instance')):&lt;br /&gt;
            if len(i) &amp;gt; 0:&lt;br /&gt;
                hasComponents = True&lt;br /&gt;
                break&lt;br /&gt;
                # ET.ElementTree(i).write(f)&lt;br /&gt;
                &lt;br /&gt;
    f = instancePath(slideId, 'components')&lt;br /&gt;
    &lt;br /&gt;
    if hasComponents:&lt;br /&gt;
        if (os.path.abspath(os.path.realpath(inkscapeFile)) != &lt;br /&gt;
            os.path.abspath(os.path.realpath(f))):&lt;br /&gt;
            file(f, 'w').write(file(inkscapeFile).read())&lt;br /&gt;
    else:&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            os.remove(f)&lt;br /&gt;
&lt;br /&gt;
if __name__ == '__main__':&lt;br /&gt;
    import sys&lt;br /&gt;
    instanceFromTemplate(sys.argv[1], 'slide%s.svg', file(sys.argv[2]).read())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16496</id>
		<title>InkSlide</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16496"/>
		<updated>2007-10-23T17:29:26Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: /* InkSlide - quick and easy presentations using Inkscape */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== InkSlide - quick and easy presentations using Inkscape ==&lt;br /&gt;
&lt;br /&gt;
InkSlide produces slides like this:&lt;br /&gt;
&lt;br /&gt;
[[Image:slide0004.jpg]]&lt;br /&gt;
&lt;br /&gt;
from simple text input like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
InkSlide: Features&lt;br /&gt;
==================&lt;br /&gt;
&lt;br /&gt;
Features include wrapped top level text and&lt;br /&gt;
- mulitple&lt;br /&gt;
  - levels&lt;br /&gt;
    - of wrapped bulleted lists with bullets and font&lt;br /&gt;
      information taken from the template file.&lt;br /&gt;
&lt;br /&gt;
Slide specific content like this:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
which is updated when the template changes.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An Inkscape file is used as a template file to define the background, title position and font, fonts and positions for text at different levels of indentation, groups to be cloned and used as bullets, etc.&lt;br /&gt;
&lt;br /&gt;
Content specific to a particular slide can also be created in Inkscape, this content merged with the template and text input to make the final slide, so changes to the template after a particular slide is edited in Inkscape are included.&lt;br /&gt;
&lt;br /&gt;
=== Download ===&lt;br /&gt;
&lt;br /&gt;
Copy the [[InkSlide#Code|text below]] into a file called 'inkslide.py'.  (How do you attach a non-image file on this wiki?)&lt;br /&gt;
&lt;br /&gt;
Here is an [[Media:template0.svg|example template]].  Use Save Link As to stop your browser displaying the file instead of downloading it.&lt;br /&gt;
&lt;br /&gt;
=== How it works ===&lt;br /&gt;
&lt;br /&gt;
First InkSlide parses the text input, something like the example at the top of this page.  It looks for flowed text boxes in the template file with special @id attributes like ''fr_title'' for the title, and ''fr_tab2'' for the second level of bulleted lists.  Copies of these elements are made and placed in a layer called 'Texts'.  At this point, the text is all piled up at the top of the page.&lt;br /&gt;
&lt;br /&gt;
InkSlide saves the file, and uses Inkscape's command line ''--query-height'' and friends to find out how high the wrapped text pieces are.  At it then repositions them properly, adds clones of the bullet markers, etc.  Bullet markers have special @id attributes like ''bu_1'' for the first level of bulleted list.&lt;br /&gt;
&lt;br /&gt;
Finally, InkSlide checks to see if there's an id tag for the slide, and if so if there's a corresponding Inkscape SVG file with content for this particular slide. This content has to occur in layers whose name starts with ''Instance'', and these layers have to be present in the template file, but you can have more than one, so they can be above and below the regular content.  If such content is found, it's copied into the output slide.&lt;br /&gt;
&lt;br /&gt;
=== The Template File ===&lt;br /&gt;
&lt;br /&gt;
The template file is a regular Inkscape file.  If this list seems hard, just use the example template and modify it to suit your needs.&lt;br /&gt;
&lt;br /&gt;
Required contents:&lt;br /&gt;
&lt;br /&gt;
*A group or layer called ''gr_text'', into which text will be placed.&lt;br /&gt;
*A flowed text frame with an @id of ''fr_title'' to define the font and position for the slide title.  This should contain text like ''Title goes here Sly'' because ''Sly'' includes letters which ascend and descend from the baseline to the greatest extent.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*A group or layer called ''gr_title'' into which the title will be placed.  This makes it easier to do special effects on the title like shadows.&lt;br /&gt;
*Flowed text frames with an @id of ''fr_tabN'' for each N in 0, 1, 2, 3.  ''fr_tab0'' is used for top level non bulleted text.  These should also include ''Sly'' or similar.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*Objects or groups withan @id of ''bu_N'' for each N in 1, 2, 3, for the three levels of bulleted list.&lt;br /&gt;
&lt;br /&gt;
The fr_* elements define the font, font-size, color etc. for the different levels of text.  They also define the position and wrap width.  See the example template file for a possible arrangement.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;A group or layer called 'X'&amp;quot; indicates either a group with an '''@id'''&lt;br /&gt;
of 'X' ''or'' a layer with a '''label''' (name) of 'X'.  When both exist the group will take precedence.&lt;br /&gt;
 &lt;br /&gt;
Optional contents:&lt;br /&gt;
&lt;br /&gt;
*A background, which should be placed in a group or layer called `gr_bg`&lt;br /&gt;
*A foreground, which should be placed in a group or layer called `gr_fg`&lt;br /&gt;
*Affiliation text and logos you want to appear on every slide, this could go in the background, or another named group or layer.&lt;br /&gt;
*A blurred clone of the gr_title element, offset and behind it.  It's easier to do this is `gr_title` is a group rather than a layer.&lt;br /&gt;
*Layers whose name starts with ''Instance'' - these are the layers the user can edit directly in Inkscape for a particular slide.&lt;br /&gt;
&lt;br /&gt;
When you save the template file you should make sure that layers containing place holder text (the fr_* elements) and bullets are invisible.  Likewise the Texts layer should be visible, as well as any Instance layers.&lt;br /&gt;
=== Text Input ===&lt;br /&gt;
&lt;br /&gt;
InkSlide uses [http://docutils.sourceforge.net/rst.html reStructuredText] for markup, although it renders only a subset of rst.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Inkslide: an example&lt;br /&gt;
====================&lt;br /&gt;
&lt;br /&gt;
This text will appear as a top level paragraph.&lt;br /&gt;
&lt;br /&gt;
- Dash prefix indicates this text will appear as a bulleted item.&lt;br /&gt;
&lt;br /&gt;
  - Indented dash prefix -&amp;gt; second level bulleted list.&lt;br /&gt;
&lt;br /&gt;
.. is:pause&lt;br /&gt;
&lt;br /&gt;
Two slides will be produced for this input, one with everything up to the&lt;br /&gt;
&amp;quot;.. is:pause&amp;quot;, and one with that plus the remaining content.&lt;br /&gt;
Incremental list display can be done this way.&lt;br /&gt;
&lt;br /&gt;
The is:id tag below indicates that a file with a name like&lt;br /&gt;
'extra/is_intro.svg' should be inspected for extra content&lt;br /&gt;
for this particular slide.&lt;br /&gt;
&lt;br /&gt;
.. is:id is_intro&lt;br /&gt;
&lt;br /&gt;
Inkslide: the next slide...&lt;br /&gt;
===========================&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== The GUI ===&lt;br /&gt;
&lt;br /&gt;
A GUI for InkSlide is available as a plug-in for the [http://webpages.charter.net/edreamleo/front.html Leo] outline editor.  This makes slide re-arranging easy, and adds some macro substitution capability.  It also adds buttons for editing slide specific content in Inkscape.&lt;br /&gt;
&lt;br /&gt;
FIXME: post plug-in on Leo wiki and link.&lt;br /&gt;
&lt;br /&gt;
=== To do / status ===&lt;br /&gt;
&lt;br /&gt;
InkSlide is very usable, but is missing some features.  Remember you can &lt;br /&gt;
use the slide specific editing feature to do any of these things and&lt;br /&gt;
anything else you can do in Inkscape already, these will just be more&lt;br /&gt;
convenient in the text input when they're done.&lt;br /&gt;
&lt;br /&gt;
*Inline text formatting (bold, color, etc.) for part of a piece of text&lt;br /&gt;
*Justification switching&lt;br /&gt;
*Verbatim code display&lt;br /&gt;
*&amp;lt;strike&amp;gt;Simple image inclusion&amp;lt;/strike&amp;gt;&lt;br /&gt;
*more helper tools to make PNG and PDF versions of slides&lt;br /&gt;
*configuring / using a directory for edited slides&lt;br /&gt;
*command line flags for generating specific slides&lt;br /&gt;
&lt;br /&gt;
Currently InkSlide is slow, because it must invoke Inkscape once for every single dimension it needs to retrieve.  I'll try and get a --query-all switch into 0.46.&lt;br /&gt;
&lt;br /&gt;
=== Bugs ===&lt;br /&gt;
&lt;br /&gt;
*There may be an issue with the way slide specific content is merged into the template producing conflicting @id attributes, but I haven't seen this happen yet.&lt;br /&gt;
&lt;br /&gt;
=== Author  / Credits ===&lt;br /&gt;
&lt;br /&gt;
InkSlide was written by Terry Brown, terry-n-brown@yahoo.com - but use underscore, not '-'.&lt;br /&gt;
&lt;br /&gt;
InkSlide was inspired by [http://member.wide.ad.jp/wg/mgp/ MagicPoint]&lt;br /&gt;
&lt;br /&gt;
Matt Harrison suggested using reStructured text as the markup language.&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# inkslide.py $Id$&lt;br /&gt;
# Author: Terry Brown&lt;br /&gt;
# Created: Thu Oct 11 2007&lt;br /&gt;
&lt;br /&gt;
import lxml.etree as ET&lt;br /&gt;
&lt;br /&gt;
import copy&lt;br /&gt;
import subprocess&lt;br /&gt;
import os.path&lt;br /&gt;
&lt;br /&gt;
NS = { 'i': 'http://www.inkscape.org/namespaces/inkscape',&lt;br /&gt;
       's': 'http://www.w3.org/2000/svg',&lt;br /&gt;
       'xlink' : 'http://www.w3.org/1999/xlink'}&lt;br /&gt;
&lt;br /&gt;
# number of defined tablevels, FIXME, could derive from template?&lt;br /&gt;
tabLevels = 4  &lt;br /&gt;
def clearId(x, what='id', NS={}):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;recursively clear @id on element x and descendants&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if what in x.keys():&lt;br /&gt;
        del x.attrib[what]&lt;br /&gt;
    for i in x.xpath('.//*[@%s]'%what, NS):&lt;br /&gt;
        del i.attrib[what]&lt;br /&gt;
    return x&lt;br /&gt;
def getDim(fn, Id, what):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return dimension of element in fn with @id Id, what is&lt;br /&gt;
    x, y, width, height&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cmd = ('inkscape', '--without-gui', '--query-id', Id, '--query-'+what, fn)&lt;br /&gt;
    proc = subprocess.Popen(cmd, stdout = subprocess.PIPE,&lt;br /&gt;
                            stderr = subprocess.PIPE)&lt;br /&gt;
    # make new pipe for stderr to supress chatter from inkscape&lt;br /&gt;
    proc.wait()&lt;br /&gt;
    return proc.stdout.read()&lt;br /&gt;
def textReader(text):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;iterate parts in input text&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not text: return&lt;br /&gt;
    for line in text.split('\n'):&lt;br /&gt;
        if line.startswith('%slide '):&lt;br /&gt;
            yield {'type':'TITLE', 's':line[7:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%pause'):&lt;br /&gt;
            yield {'type':'PAUSE'}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%id '):&lt;br /&gt;
            yield {'type':'ID', 's':line[4:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if not line.strip():&lt;br /&gt;
            yield {'type':'BLANK'}&lt;br /&gt;
            continue&lt;br /&gt;
        tabs = len(line) - len(line.lstrip('\t'))&lt;br /&gt;
        yield {'type':'TAB', 'tabs':tabs, 's':line.strip()}&lt;br /&gt;
&lt;br /&gt;
nid = 0&lt;br /&gt;
def nextId():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return an unsed Id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # FIXME, should inspect templat doc.&lt;br /&gt;
    global nid&lt;br /&gt;
    nid += 1&lt;br /&gt;
    return 'is'+str(nid)&lt;br /&gt;
&lt;br /&gt;
def instanceFromTemplate(template, instance, text, pauseOk = True):&lt;br /&gt;
&lt;br /&gt;
    slide = 0&lt;br /&gt;
&lt;br /&gt;
    hlist = []&lt;br /&gt;
    doc0 = ET.parse(template)&lt;br /&gt;
    slideId = None&lt;br /&gt;
    &lt;br /&gt;
    for t in textReader(text):&lt;br /&gt;
        &lt;br /&gt;
        if t['type'] == 'ID':&lt;br /&gt;
            slideId = t['s']&lt;br /&gt;
            continue&lt;br /&gt;
            &lt;br /&gt;
        if t['type'] == 'TITLE':&lt;br /&gt;
            print t['s']&lt;br /&gt;
            if hlist:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
                hlist = [t]&lt;br /&gt;
                doc0 = ET.parse(template)&lt;br /&gt;
                slideId = None&lt;br /&gt;
            else:&lt;br /&gt;
                hlist.append(t)&lt;br /&gt;
            dst = doc0.xpath('//s:g[@id=&amp;quot;gr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'PAUSE':&lt;br /&gt;
            if pauseOk:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        hlist.append(t)&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'BLANK':&lt;br /&gt;
            continue&lt;br /&gt;
        if t['type'] == 'TAB':&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_tab%d&amp;quot;]' % t['tabs'], NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            #break&lt;br /&gt;
            continue&lt;br /&gt;
           &lt;br /&gt;
    process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
def process(instance, doc0, hlist, slideId, slide):&lt;br /&gt;
&lt;br /&gt;
    instance = instance.replace('%s', '%04d'%slide)&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
&lt;br /&gt;
    bullDim = {}&lt;br /&gt;
    tabDim = {}&lt;br /&gt;
    for n in range(0,tabLevels):&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;bu_%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'width', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'bu_%d'%n, i))&lt;br /&gt;
            bullDim[n] = b&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;fr_tab%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'fr_tab%d'%n, i))&lt;br /&gt;
            tabDim[n] = b&lt;br /&gt;
&lt;br /&gt;
    delta = 0.&lt;br /&gt;
    firstVert = True&lt;br /&gt;
    for n, i in enumerate(hlist):&lt;br /&gt;
        if hlist[n]['type'] in ['TITLE']:&lt;br /&gt;
            continue  # already in right place, not a vertical element&lt;br /&gt;
&lt;br /&gt;
        hgt = 0&lt;br /&gt;
        if hlist[n-1]['type'] == 'BLANK':&lt;br /&gt;
            hgt = 30&lt;br /&gt;
        else:&lt;br /&gt;
            hgt = float(getDim(instance, hlist[n-1]['id'], 'height'))&lt;br /&gt;
&lt;br /&gt;
        hgt += 5&lt;br /&gt;
&lt;br /&gt;
        if firstVert:&lt;br /&gt;
            hgt = 0&lt;br /&gt;
            firstVert = False&lt;br /&gt;
&lt;br /&gt;
        delta += hgt&lt;br /&gt;
&lt;br /&gt;
        if hlist[n]['type'] == 'BLANK': continue&lt;br /&gt;
&lt;br /&gt;
        tabs = hlist[n]['tabs']&lt;br /&gt;
&lt;br /&gt;
        r = doc0.xpath('//s:flowRoot[@id=&amp;quot;%s&amp;quot;]' % hlist[n]['id'], NS)[0]&lt;br /&gt;
        clearId(r, what='transform')&lt;br /&gt;
        r = r.xpath('.//s:rect', NS)[0]&lt;br /&gt;
        r.set('x', str(tabDim[tabs]['x']))&lt;br /&gt;
        y = tabDim[tabs]['y'] + delta&lt;br /&gt;
        #print '%s -&amp;gt; %s' % (str(r.get('y')), str(y))&lt;br /&gt;
        #print tabDim[tabs]['y']&lt;br /&gt;
        r.set('y', str(y))&lt;br /&gt;
&lt;br /&gt;
        if (hlist[n]['type'] == 'TAB' and hlist[n]['tabs'] in bullDim&lt;br /&gt;
            and 'isDone' not in hlist[n]):&lt;br /&gt;
            hlist[n]['isDone'] = True&lt;br /&gt;
            dx = (tabDim[tabs]['x'] &lt;br /&gt;
                  - bullDim[tabs]['x']&lt;br /&gt;
                  - 1.5*bullDim[tabs]['width']&lt;br /&gt;
                  )&lt;br /&gt;
            dy = (y &lt;br /&gt;
                  + tabDim[tabs]['height'] / 2.&lt;br /&gt;
                  - bullDim[tabs]['y'] &lt;br /&gt;
                  - bullDim[tabs]['height'] / 2.&lt;br /&gt;
                  )&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            #print y, tabDim[tabs]['height'], bullDim[tabs]['y'], bullDim[tabs]['height'], dy&lt;br /&gt;
            clone = ET.Element('svg:use')&lt;br /&gt;
            clone.set('transform', 'translate(%f,%f)' % (dx,dy))&lt;br /&gt;
            clone.set('{%s}href'%NS['xlink'], '#bu_%d'%tabs)&lt;br /&gt;
            dst.append(clone)&lt;br /&gt;
            &lt;br /&gt;
            #path = ET.Element('svg:path')&lt;br /&gt;
            #path.set('d', ('M %f,%f' + ' L %f,%f'*5 + ' z') % (&lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y'], &lt;br /&gt;
            #    bullDim[tabs]['x']+20, bullDim[tabs]['y']+bullDim[tabs]['height']/2., &lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y']+bullDim[tabs]['height'], &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y'], &lt;br /&gt;
            #    tabDim[tabs]['x']+20, tabDim[tabs]['y']+tabDim[tabs]['height']/2., &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y']+tabDim[tabs]['height'], &lt;br /&gt;
            #    ))&lt;br /&gt;
            #dst.append(path)&lt;br /&gt;
            #break&lt;br /&gt;
&lt;br /&gt;
    # look for instance specific parts&lt;br /&gt;
    if slideId:&lt;br /&gt;
        f = instancePath(slideId)&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            comp = ET.parse(f)&lt;br /&gt;
            svg = doc0.xpath('//s:svg', NS)[0]&lt;br /&gt;
            g = &amp;quot;{%s}g&amp;quot;%NS['s']&lt;br /&gt;
            k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
            for n, i in enumerate(svg.getchildren()):&lt;br /&gt;
                if i.tag == g and i.get(k, '').startswith('Instance'):&lt;br /&gt;
                    x = comp.xpath('//s:svg//s:g[@i:label=&amp;quot;%s&amp;quot;]'%i.get(k), NS)&lt;br /&gt;
                    if x:&lt;br /&gt;
                        x = x[0]&lt;br /&gt;
                        cpy = clearId(copy.deepcopy(x))&lt;br /&gt;
                        svg[n] = cpy&lt;br /&gt;
            defs = comp.xpath('//s:svg/s:defs/*', NS)&lt;br /&gt;
            dst = doc0.xpath('//s:svg/s:defs', NS)[0]&lt;br /&gt;
            for d in defs:&lt;br /&gt;
                Id = d.get('id')&lt;br /&gt;
                current = doc0.xpath('//s:svg/s:defs/*[@id=&amp;quot;%s&amp;quot;]'%Id, NS)&lt;br /&gt;
                if not current:&lt;br /&gt;
                    cpy = copy.deepcopy(d)&lt;br /&gt;
                    dst.append(cpy)&lt;br /&gt;
&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
    &lt;br /&gt;
    return slideId&lt;br /&gt;
def instancePath(slideId, component = None):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return path to component for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # return os.path.join(slideId, component)&lt;br /&gt;
    return slideId+'.svg'&lt;br /&gt;
def writeComponents(inkscapeFile, slideId):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;write components from file for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not slideId: return&lt;br /&gt;
    doc0 = ET.parse(inkscapeFile)&lt;br /&gt;
    hasComponents = False&lt;br /&gt;
    for i in doc0.xpath('//s:svg//s:g', NS):&lt;br /&gt;
        k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
        # print i.keys(),k&lt;br /&gt;
        if (k in i.keys() and i.get(k).startswith('Instance')):&lt;br /&gt;
            if len(i) &amp;gt; 0:&lt;br /&gt;
                hasComponents = True&lt;br /&gt;
                break&lt;br /&gt;
                # ET.ElementTree(i).write(f)&lt;br /&gt;
                &lt;br /&gt;
    f = instancePath(slideId, 'components')&lt;br /&gt;
    &lt;br /&gt;
    if hasComponents:&lt;br /&gt;
        if (os.path.abspath(os.path.realpath(inkscapeFile)) != &lt;br /&gt;
            os.path.abspath(os.path.realpath(f))):&lt;br /&gt;
            file(f, 'w').write(file(inkscapeFile).read())&lt;br /&gt;
    else:&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            os.remove(f)&lt;br /&gt;
&lt;br /&gt;
if __name__ == '__main__':&lt;br /&gt;
    import sys&lt;br /&gt;
    instanceFromTemplate(sys.argv[1], 'slide%s.svg', file(sys.argv[2]).read())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16495</id>
		<title>InkSlide</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16495"/>
		<updated>2007-10-23T17:27:37Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: /* The Template File */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== InkSlide - quick and easy presentations using Inkscape ==&lt;br /&gt;
&lt;br /&gt;
InkSlide produces slides like this:&lt;br /&gt;
&lt;br /&gt;
[[Image:slide0004.jpg]]&lt;br /&gt;
&lt;br /&gt;
from simple text input like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
InkSlide: Features&lt;br /&gt;
==================&lt;br /&gt;
&lt;br /&gt;
Features include wrapped top level text and&lt;br /&gt;
- mulitple&lt;br /&gt;
  - levels&lt;br /&gt;
    - of wrapped bulleted lists with bullets and font&lt;br /&gt;
      information taken from the template file.&lt;br /&gt;
&lt;br /&gt;
Slide specific content like this:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
which is updated when the template changes.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An Inkscape file is used as a template file to define the background, title position and font, fonts and positions for text at different levels of indentation, groups to be cloned and used as bullets, etc.&lt;br /&gt;
&lt;br /&gt;
Content specific to a particular slide can also be created in Inkscape, this content merged with the template and text input to make the final slide, so changes to the template after a particular slide is edited in Inkscape are included.&lt;br /&gt;
&lt;br /&gt;
=== Download ===&lt;br /&gt;
&lt;br /&gt;
Copy the [[InkSlide#Code|text below]] into a file called 'inkslide.py'.  (How do you attach a non-image file on this wiki?)&lt;br /&gt;
&lt;br /&gt;
Here is an [[Media:template0.svg|example template]].  Use Save Link As to stop your browser displaying the file instead of downloading it.&lt;br /&gt;
&lt;br /&gt;
=== How it works ===&lt;br /&gt;
&lt;br /&gt;
First InkSlide parses the text input, something like the example at the top of this page.  It looks for flowed text boxes in the template file with special @id attributes like ''fr_title'' for the title, and ''fr_tab2'' for the second level of bulleted lists.  Copies of these elements are made and placed in a layer called 'Texts'.  At this point, the text is all piled up at the top of the page.&lt;br /&gt;
&lt;br /&gt;
InkSlide saves the file, and uses Inkscape's command line ''--query-height'' and friends to find out how high the wrapped text pieces are.  At it then repositions them properly, adds clones of the bullet markers, etc.  Bullet markers have special @id attributes like ''bu_1'' for the first level of bulleted list.&lt;br /&gt;
&lt;br /&gt;
Finally, InkSlide checks to see if there's an id tag for the slide, and if so if there's a corresponding Inkscape SVG file with content for this particular slide. This content has to occur in layers whose name starts with ''Instance'', and these layers have to be present in the template file, but you can have more than one, so they can be above and below the regular content.  If such content is found, it's copied into the output slide.&lt;br /&gt;
&lt;br /&gt;
=== The Template File ===&lt;br /&gt;
&lt;br /&gt;
The template file is a regular Inkscape file.  If this list seems hard, just use the example template and modify it to suit your needs.&lt;br /&gt;
&lt;br /&gt;
Required contents:&lt;br /&gt;
&lt;br /&gt;
*A group or layer called ''gr_text'', into which text will be placed.&lt;br /&gt;
*A flowed text frame with an @id of ''fr_title'' to define the font and position for the slide title.  This should contain text like ''Title goes here Sly'' because ''Sly'' includes letters which ascend and descend from the baseline to the greatest extent.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*A group or layer called ''gr_title'' into which the title will be placed.  This makes it easier to do special effects on the title like shadows.&lt;br /&gt;
*Flowed text frames with an @id of ''fr_tabN'' for each N in 0, 1, 2, 3.  ''fr_tab0'' is used for top level non bulleted text.  These should also include ''Sly'' or similar.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*Objects or groups withan @id of ''bu_N'' for each N in 1, 2, 3, for the three levels of bulleted list.&lt;br /&gt;
&lt;br /&gt;
The fr_* elements define the font, font-size, color etc. for the different levels of text.  They also define the position and wrap width.  See the example template file for a possible arrangement.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;A group or layer called 'X'&amp;quot; indicates either a group with an '''@id'''&lt;br /&gt;
of 'X' ''or'' a layer with a '''label''' (name) of 'X'.  When both exist the group will take precedence.&lt;br /&gt;
 &lt;br /&gt;
Optional contents:&lt;br /&gt;
&lt;br /&gt;
*A background, which should be placed in a group or layer called `gr_bg`&lt;br /&gt;
*A foreground, which should be placed in a group or layer called `gr_fg`&lt;br /&gt;
*Affiliation text and logos you want to appear on every slide, this could go in the background, or another named group or layer.&lt;br /&gt;
*A blurred clone of the gr_title element, offset and behind it.  It's easier to do this is `gr_title` is a group rather than a layer.&lt;br /&gt;
*Layers whose name starts with ''Instance'' - these are the layers the user can edit directly in Inkscape for a particular slide.&lt;br /&gt;
&lt;br /&gt;
When you save the template file you should make sure that layers containing place holder text (the fr_* elements) and bullets are invisible.  Likewise the Texts layer should be visible, as well as any Instance layers.&lt;br /&gt;
&lt;br /&gt;
=== The Template File ===&lt;br /&gt;
&lt;br /&gt;
The template file is a regular Inkscape file.  If this list seems hard, just use the example template and modify it to suit your needs.&lt;br /&gt;
&lt;br /&gt;
Required contents:&lt;br /&gt;
&lt;br /&gt;
*A layer called ''Texts'', into which text will be placed.&lt;br /&gt;
*A flowed text frame with an @id of ''fr_title'' to define the font and position for the slide title.  This should contain text like ''Title goes here Sly'' because ''Sly'' includes letters which ascend and descend from the baseline to the greatest extent.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*A group with an @id of ''gr_title'' into which the title will be placed.  This makes it easier to do special effects on the title like shadows.&lt;br /&gt;
*Flowed text frames with an @id of ''fr_tabN'' for each N in 0, 1, 2, 3.  ''fr_tab0'' is used for top level non bulleted text.  These should also include ''Sly'' or similar.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*Objects or groups withan @id of ''bu_N'' for each N in 1, 2, 3, for the three levels of bulleted list.&lt;br /&gt;
&lt;br /&gt;
The fr_* elements define the font, font-size, color etc. for the different levels of text.  They also define the position and wrap width.  See the example template file for a possible arrangement.&lt;br /&gt;
&lt;br /&gt;
Optional contents:&lt;br /&gt;
&lt;br /&gt;
*A background&lt;br /&gt;
*Affiliation text and logos you want to appear on every slide&lt;br /&gt;
*A blurred clone of the gr_title element, offset and behind it.&lt;br /&gt;
*Layers whose name starts with ''Instance'' - these are the layers the user can edit directly in Inkscape for a particular slide.&lt;br /&gt;
&lt;br /&gt;
When you save the template file you should make sure that layers containing place holder text (the fr_* elements) and bullets are invisible, or at least hidden below the background layer, which should be visible.  Likewise the Texts layer should be visible, as well as any Instance layers.&lt;br /&gt;
&lt;br /&gt;
=== Text Input ===&lt;br /&gt;
&lt;br /&gt;
InkSlide uses [http://docutils.sourceforge.net/rst.html reStructuredText] for markup, although it renders only a subset of rst.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Inkslide: an example&lt;br /&gt;
====================&lt;br /&gt;
&lt;br /&gt;
This text will appear as a top level paragraph.&lt;br /&gt;
&lt;br /&gt;
- Dash prefix indicates this text will appear as a bulleted item.&lt;br /&gt;
&lt;br /&gt;
  - Indented dash prefix -&amp;gt; second level bulleted list.&lt;br /&gt;
&lt;br /&gt;
.. is:pause&lt;br /&gt;
&lt;br /&gt;
Two slides will be produced for this input, one with everything up to the&lt;br /&gt;
&amp;quot;.. is:pause&amp;quot;, and one with that plus the remaining content.&lt;br /&gt;
Incremental list display can be done this way.&lt;br /&gt;
&lt;br /&gt;
The is:id tag below indicates that a file with a name like&lt;br /&gt;
'extra/is_intro.svg' should be inspected for extra content&lt;br /&gt;
for this particular slide.&lt;br /&gt;
&lt;br /&gt;
.. is:id is_intro&lt;br /&gt;
&lt;br /&gt;
Inkslide: the next slide...&lt;br /&gt;
===========================&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== The GUI ===&lt;br /&gt;
&lt;br /&gt;
A GUI for InkSlide is available as a plug-in for the [http://webpages.charter.net/edreamleo/front.html Leo] outline editor.  This makes slide re-arranging easy, and adds some macro substitution capability.  It also adds buttons for editing slide specific content in Inkscape.&lt;br /&gt;
&lt;br /&gt;
FIXME: post plug-in on Leo wiki and link.&lt;br /&gt;
&lt;br /&gt;
=== To do / status ===&lt;br /&gt;
&lt;br /&gt;
InkSlide is very usable, but is missing some features.  Remember you can &lt;br /&gt;
use the slide specific editing feature to do any of these things and&lt;br /&gt;
anything else you can do in Inkscape already, these will just be more&lt;br /&gt;
convenient in the text input when they're done.&lt;br /&gt;
&lt;br /&gt;
*Inline text formatting (bold, color, etc.) for part of a piece of text&lt;br /&gt;
*Justification switching&lt;br /&gt;
*Verbatim code display&lt;br /&gt;
*&amp;lt;strike&amp;gt;Simple image inclusion&amp;lt;/strike&amp;gt;&lt;br /&gt;
*more helper tools to make PNG and PDF versions of slides&lt;br /&gt;
*configuring / using a directory for edited slides&lt;br /&gt;
*command line flags for generating specific slides&lt;br /&gt;
&lt;br /&gt;
Currently InkSlide is slow, because it must invoke Inkscape once for every single dimension it needs to retrieve.  I'll try and get a --query-all switch into 0.46.&lt;br /&gt;
&lt;br /&gt;
=== Bugs ===&lt;br /&gt;
&lt;br /&gt;
*There may be an issue with the way slide specific content is merged into the template producing conflicting @id attributes, but I haven't seen this happen yet.&lt;br /&gt;
&lt;br /&gt;
=== Author  / Credits ===&lt;br /&gt;
&lt;br /&gt;
InkSlide was written by Terry Brown, terry-n-brown@yahoo.com - but use underscore, not '-'.&lt;br /&gt;
&lt;br /&gt;
InkSlide was inspired by [http://member.wide.ad.jp/wg/mgp/ MagicPoint]&lt;br /&gt;
&lt;br /&gt;
Matt Harrison suggested using reStructured text as the markup language.&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# inkslide.py $Id$&lt;br /&gt;
# Author: Terry Brown&lt;br /&gt;
# Created: Thu Oct 11 2007&lt;br /&gt;
&lt;br /&gt;
import lxml.etree as ET&lt;br /&gt;
&lt;br /&gt;
import copy&lt;br /&gt;
import subprocess&lt;br /&gt;
import os.path&lt;br /&gt;
&lt;br /&gt;
NS = { 'i': 'http://www.inkscape.org/namespaces/inkscape',&lt;br /&gt;
       's': 'http://www.w3.org/2000/svg',&lt;br /&gt;
       'xlink' : 'http://www.w3.org/1999/xlink'}&lt;br /&gt;
&lt;br /&gt;
# number of defined tablevels, FIXME, could derive from template?&lt;br /&gt;
tabLevels = 4  &lt;br /&gt;
def clearId(x, what='id', NS={}):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;recursively clear @id on element x and descendants&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if what in x.keys():&lt;br /&gt;
        del x.attrib[what]&lt;br /&gt;
    for i in x.xpath('.//*[@%s]'%what, NS):&lt;br /&gt;
        del i.attrib[what]&lt;br /&gt;
    return x&lt;br /&gt;
def getDim(fn, Id, what):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return dimension of element in fn with @id Id, what is&lt;br /&gt;
    x, y, width, height&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cmd = ('inkscape', '--without-gui', '--query-id', Id, '--query-'+what, fn)&lt;br /&gt;
    proc = subprocess.Popen(cmd, stdout = subprocess.PIPE,&lt;br /&gt;
                            stderr = subprocess.PIPE)&lt;br /&gt;
    # make new pipe for stderr to supress chatter from inkscape&lt;br /&gt;
    proc.wait()&lt;br /&gt;
    return proc.stdout.read()&lt;br /&gt;
def textReader(text):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;iterate parts in input text&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not text: return&lt;br /&gt;
    for line in text.split('\n'):&lt;br /&gt;
        if line.startswith('%slide '):&lt;br /&gt;
            yield {'type':'TITLE', 's':line[7:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%pause'):&lt;br /&gt;
            yield {'type':'PAUSE'}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%id '):&lt;br /&gt;
            yield {'type':'ID', 's':line[4:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if not line.strip():&lt;br /&gt;
            yield {'type':'BLANK'}&lt;br /&gt;
            continue&lt;br /&gt;
        tabs = len(line) - len(line.lstrip('\t'))&lt;br /&gt;
        yield {'type':'TAB', 'tabs':tabs, 's':line.strip()}&lt;br /&gt;
&lt;br /&gt;
nid = 0&lt;br /&gt;
def nextId():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return an unsed Id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # FIXME, should inspect templat doc.&lt;br /&gt;
    global nid&lt;br /&gt;
    nid += 1&lt;br /&gt;
    return 'is'+str(nid)&lt;br /&gt;
&lt;br /&gt;
def instanceFromTemplate(template, instance, text, pauseOk = True):&lt;br /&gt;
&lt;br /&gt;
    slide = 0&lt;br /&gt;
&lt;br /&gt;
    hlist = []&lt;br /&gt;
    doc0 = ET.parse(template)&lt;br /&gt;
    slideId = None&lt;br /&gt;
    &lt;br /&gt;
    for t in textReader(text):&lt;br /&gt;
        &lt;br /&gt;
        if t['type'] == 'ID':&lt;br /&gt;
            slideId = t['s']&lt;br /&gt;
            continue&lt;br /&gt;
            &lt;br /&gt;
        if t['type'] == 'TITLE':&lt;br /&gt;
            print t['s']&lt;br /&gt;
            if hlist:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
                hlist = [t]&lt;br /&gt;
                doc0 = ET.parse(template)&lt;br /&gt;
                slideId = None&lt;br /&gt;
            else:&lt;br /&gt;
                hlist.append(t)&lt;br /&gt;
            dst = doc0.xpath('//s:g[@id=&amp;quot;gr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'PAUSE':&lt;br /&gt;
            if pauseOk:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        hlist.append(t)&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'BLANK':&lt;br /&gt;
            continue&lt;br /&gt;
        if t['type'] == 'TAB':&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_tab%d&amp;quot;]' % t['tabs'], NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            #break&lt;br /&gt;
            continue&lt;br /&gt;
           &lt;br /&gt;
    process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
def process(instance, doc0, hlist, slideId, slide):&lt;br /&gt;
&lt;br /&gt;
    instance = instance.replace('%s', '%04d'%slide)&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
&lt;br /&gt;
    bullDim = {}&lt;br /&gt;
    tabDim = {}&lt;br /&gt;
    for n in range(0,tabLevels):&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;bu_%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'width', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'bu_%d'%n, i))&lt;br /&gt;
            bullDim[n] = b&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;fr_tab%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'fr_tab%d'%n, i))&lt;br /&gt;
            tabDim[n] = b&lt;br /&gt;
&lt;br /&gt;
    delta = 0.&lt;br /&gt;
    firstVert = True&lt;br /&gt;
    for n, i in enumerate(hlist):&lt;br /&gt;
        if hlist[n]['type'] in ['TITLE']:&lt;br /&gt;
            continue  # already in right place, not a vertical element&lt;br /&gt;
&lt;br /&gt;
        hgt = 0&lt;br /&gt;
        if hlist[n-1]['type'] == 'BLANK':&lt;br /&gt;
            hgt = 30&lt;br /&gt;
        else:&lt;br /&gt;
            hgt = float(getDim(instance, hlist[n-1]['id'], 'height'))&lt;br /&gt;
&lt;br /&gt;
        hgt += 5&lt;br /&gt;
&lt;br /&gt;
        if firstVert:&lt;br /&gt;
            hgt = 0&lt;br /&gt;
            firstVert = False&lt;br /&gt;
&lt;br /&gt;
        delta += hgt&lt;br /&gt;
&lt;br /&gt;
        if hlist[n]['type'] == 'BLANK': continue&lt;br /&gt;
&lt;br /&gt;
        tabs = hlist[n]['tabs']&lt;br /&gt;
&lt;br /&gt;
        r = doc0.xpath('//s:flowRoot[@id=&amp;quot;%s&amp;quot;]' % hlist[n]['id'], NS)[0]&lt;br /&gt;
        clearId(r, what='transform')&lt;br /&gt;
        r = r.xpath('.//s:rect', NS)[0]&lt;br /&gt;
        r.set('x', str(tabDim[tabs]['x']))&lt;br /&gt;
        y = tabDim[tabs]['y'] + delta&lt;br /&gt;
        #print '%s -&amp;gt; %s' % (str(r.get('y')), str(y))&lt;br /&gt;
        #print tabDim[tabs]['y']&lt;br /&gt;
        r.set('y', str(y))&lt;br /&gt;
&lt;br /&gt;
        if (hlist[n]['type'] == 'TAB' and hlist[n]['tabs'] in bullDim&lt;br /&gt;
            and 'isDone' not in hlist[n]):&lt;br /&gt;
            hlist[n]['isDone'] = True&lt;br /&gt;
            dx = (tabDim[tabs]['x'] &lt;br /&gt;
                  - bullDim[tabs]['x']&lt;br /&gt;
                  - 1.5*bullDim[tabs]['width']&lt;br /&gt;
                  )&lt;br /&gt;
            dy = (y &lt;br /&gt;
                  + tabDim[tabs]['height'] / 2.&lt;br /&gt;
                  - bullDim[tabs]['y'] &lt;br /&gt;
                  - bullDim[tabs]['height'] / 2.&lt;br /&gt;
                  )&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            #print y, tabDim[tabs]['height'], bullDim[tabs]['y'], bullDim[tabs]['height'], dy&lt;br /&gt;
            clone = ET.Element('svg:use')&lt;br /&gt;
            clone.set('transform', 'translate(%f,%f)' % (dx,dy))&lt;br /&gt;
            clone.set('{%s}href'%NS['xlink'], '#bu_%d'%tabs)&lt;br /&gt;
            dst.append(clone)&lt;br /&gt;
            &lt;br /&gt;
            #path = ET.Element('svg:path')&lt;br /&gt;
            #path.set('d', ('M %f,%f' + ' L %f,%f'*5 + ' z') % (&lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y'], &lt;br /&gt;
            #    bullDim[tabs]['x']+20, bullDim[tabs]['y']+bullDim[tabs]['height']/2., &lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y']+bullDim[tabs]['height'], &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y'], &lt;br /&gt;
            #    tabDim[tabs]['x']+20, tabDim[tabs]['y']+tabDim[tabs]['height']/2., &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y']+tabDim[tabs]['height'], &lt;br /&gt;
            #    ))&lt;br /&gt;
            #dst.append(path)&lt;br /&gt;
            #break&lt;br /&gt;
&lt;br /&gt;
    # look for instance specific parts&lt;br /&gt;
    if slideId:&lt;br /&gt;
        f = instancePath(slideId)&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            comp = ET.parse(f)&lt;br /&gt;
            svg = doc0.xpath('//s:svg', NS)[0]&lt;br /&gt;
            g = &amp;quot;{%s}g&amp;quot;%NS['s']&lt;br /&gt;
            k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
            for n, i in enumerate(svg.getchildren()):&lt;br /&gt;
                if i.tag == g and i.get(k, '').startswith('Instance'):&lt;br /&gt;
                    x = comp.xpath('//s:svg//s:g[@i:label=&amp;quot;%s&amp;quot;]'%i.get(k), NS)&lt;br /&gt;
                    if x:&lt;br /&gt;
                        x = x[0]&lt;br /&gt;
                        cpy = clearId(copy.deepcopy(x))&lt;br /&gt;
                        svg[n] = cpy&lt;br /&gt;
            defs = comp.xpath('//s:svg/s:defs/*', NS)&lt;br /&gt;
            dst = doc0.xpath('//s:svg/s:defs', NS)[0]&lt;br /&gt;
            for d in defs:&lt;br /&gt;
                Id = d.get('id')&lt;br /&gt;
                current = doc0.xpath('//s:svg/s:defs/*[@id=&amp;quot;%s&amp;quot;]'%Id, NS)&lt;br /&gt;
                if not current:&lt;br /&gt;
                    cpy = copy.deepcopy(d)&lt;br /&gt;
                    dst.append(cpy)&lt;br /&gt;
&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
    &lt;br /&gt;
    return slideId&lt;br /&gt;
def instancePath(slideId, component = None):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return path to component for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # return os.path.join(slideId, component)&lt;br /&gt;
    return slideId+'.svg'&lt;br /&gt;
def writeComponents(inkscapeFile, slideId):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;write components from file for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not slideId: return&lt;br /&gt;
    doc0 = ET.parse(inkscapeFile)&lt;br /&gt;
    hasComponents = False&lt;br /&gt;
    for i in doc0.xpath('//s:svg//s:g', NS):&lt;br /&gt;
        k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
        # print i.keys(),k&lt;br /&gt;
        if (k in i.keys() and i.get(k).startswith('Instance')):&lt;br /&gt;
            if len(i) &amp;gt; 0:&lt;br /&gt;
                hasComponents = True&lt;br /&gt;
                break&lt;br /&gt;
                # ET.ElementTree(i).write(f)&lt;br /&gt;
                &lt;br /&gt;
    f = instancePath(slideId, 'components')&lt;br /&gt;
    &lt;br /&gt;
    if hasComponents:&lt;br /&gt;
        if (os.path.abspath(os.path.realpath(inkscapeFile)) != &lt;br /&gt;
            os.path.abspath(os.path.realpath(f))):&lt;br /&gt;
            file(f, 'w').write(file(inkscapeFile).read())&lt;br /&gt;
    else:&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            os.remove(f)&lt;br /&gt;
&lt;br /&gt;
if __name__ == '__main__':&lt;br /&gt;
    import sys&lt;br /&gt;
    instanceFromTemplate(sys.argv[1], 'slide%s.svg', file(sys.argv[2]).read())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16494</id>
		<title>InkSlide</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16494"/>
		<updated>2007-10-23T17:21:09Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: /* How it works */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== InkSlide - quick and easy presentations using Inkscape ==&lt;br /&gt;
&lt;br /&gt;
InkSlide produces slides like this:&lt;br /&gt;
&lt;br /&gt;
[[Image:slide0004.jpg]]&lt;br /&gt;
&lt;br /&gt;
from simple text input like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
InkSlide: Features&lt;br /&gt;
==================&lt;br /&gt;
&lt;br /&gt;
Features include wrapped top level text and&lt;br /&gt;
- mulitple&lt;br /&gt;
  - levels&lt;br /&gt;
    - of wrapped bulleted lists with bullets and font&lt;br /&gt;
      information taken from the template file.&lt;br /&gt;
&lt;br /&gt;
Slide specific content like this:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
which is updated when the template changes.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An Inkscape file is used as a template file to define the background, title position and font, fonts and positions for text at different levels of indentation, groups to be cloned and used as bullets, etc.&lt;br /&gt;
&lt;br /&gt;
Content specific to a particular slide can also be created in Inkscape, this content merged with the template and text input to make the final slide, so changes to the template after a particular slide is edited in Inkscape are included.&lt;br /&gt;
&lt;br /&gt;
=== Download ===&lt;br /&gt;
&lt;br /&gt;
Copy the [[InkSlide#Code|text below]] into a file called 'inkslide.py'.  (How do you attach a non-image file on this wiki?)&lt;br /&gt;
&lt;br /&gt;
Here is an [[Media:template0.svg|example template]].  Use Save Link As to stop your browser displaying the file instead of downloading it.&lt;br /&gt;
&lt;br /&gt;
=== How it works ===&lt;br /&gt;
&lt;br /&gt;
First InkSlide parses the text input, something like the example at the top of this page.  It looks for flowed text boxes in the template file with special @id attributes like ''fr_title'' for the title, and ''fr_tab2'' for the second level of bulleted lists.  Copies of these elements are made and placed in a layer called 'Texts'.  At this point, the text is all piled up at the top of the page.&lt;br /&gt;
&lt;br /&gt;
InkSlide saves the file, and uses Inkscape's command line ''--query-height'' and friends to find out how high the wrapped text pieces are.  At it then repositions them properly, adds clones of the bullet markers, etc.  Bullet markers have special @id attributes like ''bu_1'' for the first level of bulleted list.&lt;br /&gt;
&lt;br /&gt;
Finally, InkSlide checks to see if there's an id tag for the slide, and if so if there's a corresponding Inkscape SVG file with content for this particular slide. This content has to occur in layers whose name starts with ''Instance'', and these layers have to be present in the template file, but you can have more than one, so they can be above and below the regular content.  If such content is found, it's copied into the output slide.&lt;br /&gt;
&lt;br /&gt;
=== How it works ===&lt;br /&gt;
&lt;br /&gt;
First InkSlide parses the text input, something like the example at the top of this page.  It looks for flowed text boxes in the template file with special @id attributes like ''fr_title'' for the title, and ''fr_tab2'' for the second level of bulleted lists.  Copies of these elements are made and placed in a layer called 'Texts'.  At this point, the text is all piled up at the top of the page.&lt;br /&gt;
&lt;br /&gt;
InkSlide saves the file, and uses Inkscape's command line ''--query-height'' and friends to find out how high the wrapped text pieces are.  At it then repositions them properly, adds clones of the bullet markers, etc.  Bullet markers have special @id attributes like ''bu_1'' for the first level of bulleted list.&lt;br /&gt;
&lt;br /&gt;
Finally, InkSlide checks to see if there's an %id tag for the slide, and if so if there's a corresponding Inkscape SVG file with content for this particular slide. This content has to occur in layers whose name starts with ''Instance'', and these layers have to be present in the template file, but you can have more than one, so they can be above and below the regular content.  If such content is found, it's copied into the output slide.&lt;br /&gt;
&lt;br /&gt;
=== The Template File ===&lt;br /&gt;
&lt;br /&gt;
The template file is a regular Inkscape file.  If this list seems hard, just use the example template and modify it to suit your needs.&lt;br /&gt;
&lt;br /&gt;
Required contents:&lt;br /&gt;
&lt;br /&gt;
*A layer called ''Texts'', into which text will be placed.&lt;br /&gt;
*A flowed text frame with an @id of ''fr_title'' to define the font and position for the slide title.  This should contain text like ''Title goes here Sly'' because ''Sly'' includes letters which ascend and descend from the baseline to the greatest extent.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*A group with an @id of ''gr_title'' into which the title will be placed.  This makes it easier to do special effects on the title like shadows.&lt;br /&gt;
*Flowed text frames with an @id of ''fr_tabN'' for each N in 0, 1, 2, 3.  ''fr_tab0'' is used for top level non bulleted text.  These should also include ''Sly'' or similar.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*Objects or groups withan @id of ''bu_N'' for each N in 1, 2, 3, for the three levels of bulleted list.&lt;br /&gt;
&lt;br /&gt;
The fr_* elements define the font, font-size, color etc. for the different levels of text.  They also define the position and wrap width.  See the example template file for a possible arrangement.&lt;br /&gt;
&lt;br /&gt;
Optional contents:&lt;br /&gt;
&lt;br /&gt;
*A background&lt;br /&gt;
*Affiliation text and logos you want to appear on every slide&lt;br /&gt;
*A blurred clone of the gr_title element, offset and behind it.&lt;br /&gt;
*Layers whose name starts with ''Instance'' - these are the layers the user can edit directly in Inkscape for a particular slide.&lt;br /&gt;
&lt;br /&gt;
When you save the template file you should make sure that layers containing place holder text (the fr_* elements) and bullets are invisible, or at least hidden below the background layer, which should be visible.  Likewise the Texts layer should be visible, as well as any Instance layers.&lt;br /&gt;
&lt;br /&gt;
=== Text Input ===&lt;br /&gt;
&lt;br /&gt;
InkSlide uses [http://docutils.sourceforge.net/rst.html reStructuredText] for markup, although it renders only a subset of rst.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Inkslide: an example&lt;br /&gt;
====================&lt;br /&gt;
&lt;br /&gt;
This text will appear as a top level paragraph.&lt;br /&gt;
&lt;br /&gt;
- Dash prefix indicates this text will appear as a bulleted item.&lt;br /&gt;
&lt;br /&gt;
  - Indented dash prefix -&amp;gt; second level bulleted list.&lt;br /&gt;
&lt;br /&gt;
.. is:pause&lt;br /&gt;
&lt;br /&gt;
Two slides will be produced for this input, one with everything up to the&lt;br /&gt;
&amp;quot;.. is:pause&amp;quot;, and one with that plus the remaining content.&lt;br /&gt;
Incremental list display can be done this way.&lt;br /&gt;
&lt;br /&gt;
The is:id tag below indicates that a file with a name like&lt;br /&gt;
'extra/is_intro.svg' should be inspected for extra content&lt;br /&gt;
for this particular slide.&lt;br /&gt;
&lt;br /&gt;
.. is:id is_intro&lt;br /&gt;
&lt;br /&gt;
Inkslide: the next slide...&lt;br /&gt;
===========================&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== The GUI ===&lt;br /&gt;
&lt;br /&gt;
A GUI for InkSlide is available as a plug-in for the [http://webpages.charter.net/edreamleo/front.html Leo] outline editor.  This makes slide re-arranging easy, and adds some macro substitution capability.  It also adds buttons for editing slide specific content in Inkscape.&lt;br /&gt;
&lt;br /&gt;
FIXME: post plug-in on Leo wiki and link.&lt;br /&gt;
&lt;br /&gt;
=== To do / status ===&lt;br /&gt;
&lt;br /&gt;
InkSlide is very usable, but is missing some features.  Remember you can &lt;br /&gt;
use the slide specific editing feature to do any of these things and&lt;br /&gt;
anything else you can do in Inkscape already, these will just be more&lt;br /&gt;
convenient in the text input when they're done.&lt;br /&gt;
&lt;br /&gt;
*Inline text formatting (bold, color, etc.) for part of a piece of text&lt;br /&gt;
*Justification switching&lt;br /&gt;
*Verbatim code display&lt;br /&gt;
*&amp;lt;strike&amp;gt;Simple image inclusion&amp;lt;/strike&amp;gt;&lt;br /&gt;
*more helper tools to make PNG and PDF versions of slides&lt;br /&gt;
*configuring / using a directory for edited slides&lt;br /&gt;
*command line flags for generating specific slides&lt;br /&gt;
&lt;br /&gt;
Currently InkSlide is slow, because it must invoke Inkscape once for every single dimension it needs to retrieve.  I'll try and get a --query-all switch into 0.46.&lt;br /&gt;
&lt;br /&gt;
=== Bugs ===&lt;br /&gt;
&lt;br /&gt;
*There may be an issue with the way slide specific content is merged into the template producing conflicting @id attributes, but I haven't seen this happen yet.&lt;br /&gt;
&lt;br /&gt;
=== Author  / Credits ===&lt;br /&gt;
&lt;br /&gt;
InkSlide was written by Terry Brown, terry-n-brown@yahoo.com - but use underscore, not '-'.&lt;br /&gt;
&lt;br /&gt;
InkSlide was inspired by [http://member.wide.ad.jp/wg/mgp/ MagicPoint]&lt;br /&gt;
&lt;br /&gt;
Matt Harrison suggested using reStructured text as the markup language.&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# inkslide.py $Id$&lt;br /&gt;
# Author: Terry Brown&lt;br /&gt;
# Created: Thu Oct 11 2007&lt;br /&gt;
&lt;br /&gt;
import lxml.etree as ET&lt;br /&gt;
&lt;br /&gt;
import copy&lt;br /&gt;
import subprocess&lt;br /&gt;
import os.path&lt;br /&gt;
&lt;br /&gt;
NS = { 'i': 'http://www.inkscape.org/namespaces/inkscape',&lt;br /&gt;
       's': 'http://www.w3.org/2000/svg',&lt;br /&gt;
       'xlink' : 'http://www.w3.org/1999/xlink'}&lt;br /&gt;
&lt;br /&gt;
# number of defined tablevels, FIXME, could derive from template?&lt;br /&gt;
tabLevels = 4  &lt;br /&gt;
def clearId(x, what='id', NS={}):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;recursively clear @id on element x and descendants&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if what in x.keys():&lt;br /&gt;
        del x.attrib[what]&lt;br /&gt;
    for i in x.xpath('.//*[@%s]'%what, NS):&lt;br /&gt;
        del i.attrib[what]&lt;br /&gt;
    return x&lt;br /&gt;
def getDim(fn, Id, what):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return dimension of element in fn with @id Id, what is&lt;br /&gt;
    x, y, width, height&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cmd = ('inkscape', '--without-gui', '--query-id', Id, '--query-'+what, fn)&lt;br /&gt;
    proc = subprocess.Popen(cmd, stdout = subprocess.PIPE,&lt;br /&gt;
                            stderr = subprocess.PIPE)&lt;br /&gt;
    # make new pipe for stderr to supress chatter from inkscape&lt;br /&gt;
    proc.wait()&lt;br /&gt;
    return proc.stdout.read()&lt;br /&gt;
def textReader(text):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;iterate parts in input text&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not text: return&lt;br /&gt;
    for line in text.split('\n'):&lt;br /&gt;
        if line.startswith('%slide '):&lt;br /&gt;
            yield {'type':'TITLE', 's':line[7:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%pause'):&lt;br /&gt;
            yield {'type':'PAUSE'}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%id '):&lt;br /&gt;
            yield {'type':'ID', 's':line[4:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if not line.strip():&lt;br /&gt;
            yield {'type':'BLANK'}&lt;br /&gt;
            continue&lt;br /&gt;
        tabs = len(line) - len(line.lstrip('\t'))&lt;br /&gt;
        yield {'type':'TAB', 'tabs':tabs, 's':line.strip()}&lt;br /&gt;
&lt;br /&gt;
nid = 0&lt;br /&gt;
def nextId():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return an unsed Id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # FIXME, should inspect templat doc.&lt;br /&gt;
    global nid&lt;br /&gt;
    nid += 1&lt;br /&gt;
    return 'is'+str(nid)&lt;br /&gt;
&lt;br /&gt;
def instanceFromTemplate(template, instance, text, pauseOk = True):&lt;br /&gt;
&lt;br /&gt;
    slide = 0&lt;br /&gt;
&lt;br /&gt;
    hlist = []&lt;br /&gt;
    doc0 = ET.parse(template)&lt;br /&gt;
    slideId = None&lt;br /&gt;
    &lt;br /&gt;
    for t in textReader(text):&lt;br /&gt;
        &lt;br /&gt;
        if t['type'] == 'ID':&lt;br /&gt;
            slideId = t['s']&lt;br /&gt;
            continue&lt;br /&gt;
            &lt;br /&gt;
        if t['type'] == 'TITLE':&lt;br /&gt;
            print t['s']&lt;br /&gt;
            if hlist:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
                hlist = [t]&lt;br /&gt;
                doc0 = ET.parse(template)&lt;br /&gt;
                slideId = None&lt;br /&gt;
            else:&lt;br /&gt;
                hlist.append(t)&lt;br /&gt;
            dst = doc0.xpath('//s:g[@id=&amp;quot;gr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'PAUSE':&lt;br /&gt;
            if pauseOk:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        hlist.append(t)&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'BLANK':&lt;br /&gt;
            continue&lt;br /&gt;
        if t['type'] == 'TAB':&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_tab%d&amp;quot;]' % t['tabs'], NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            #break&lt;br /&gt;
            continue&lt;br /&gt;
           &lt;br /&gt;
    process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
def process(instance, doc0, hlist, slideId, slide):&lt;br /&gt;
&lt;br /&gt;
    instance = instance.replace('%s', '%04d'%slide)&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
&lt;br /&gt;
    bullDim = {}&lt;br /&gt;
    tabDim = {}&lt;br /&gt;
    for n in range(0,tabLevels):&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;bu_%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'width', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'bu_%d'%n, i))&lt;br /&gt;
            bullDim[n] = b&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;fr_tab%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'fr_tab%d'%n, i))&lt;br /&gt;
            tabDim[n] = b&lt;br /&gt;
&lt;br /&gt;
    delta = 0.&lt;br /&gt;
    firstVert = True&lt;br /&gt;
    for n, i in enumerate(hlist):&lt;br /&gt;
        if hlist[n]['type'] in ['TITLE']:&lt;br /&gt;
            continue  # already in right place, not a vertical element&lt;br /&gt;
&lt;br /&gt;
        hgt = 0&lt;br /&gt;
        if hlist[n-1]['type'] == 'BLANK':&lt;br /&gt;
            hgt = 30&lt;br /&gt;
        else:&lt;br /&gt;
            hgt = float(getDim(instance, hlist[n-1]['id'], 'height'))&lt;br /&gt;
&lt;br /&gt;
        hgt += 5&lt;br /&gt;
&lt;br /&gt;
        if firstVert:&lt;br /&gt;
            hgt = 0&lt;br /&gt;
            firstVert = False&lt;br /&gt;
&lt;br /&gt;
        delta += hgt&lt;br /&gt;
&lt;br /&gt;
        if hlist[n]['type'] == 'BLANK': continue&lt;br /&gt;
&lt;br /&gt;
        tabs = hlist[n]['tabs']&lt;br /&gt;
&lt;br /&gt;
        r = doc0.xpath('//s:flowRoot[@id=&amp;quot;%s&amp;quot;]' % hlist[n]['id'], NS)[0]&lt;br /&gt;
        clearId(r, what='transform')&lt;br /&gt;
        r = r.xpath('.//s:rect', NS)[0]&lt;br /&gt;
        r.set('x', str(tabDim[tabs]['x']))&lt;br /&gt;
        y = tabDim[tabs]['y'] + delta&lt;br /&gt;
        #print '%s -&amp;gt; %s' % (str(r.get('y')), str(y))&lt;br /&gt;
        #print tabDim[tabs]['y']&lt;br /&gt;
        r.set('y', str(y))&lt;br /&gt;
&lt;br /&gt;
        if (hlist[n]['type'] == 'TAB' and hlist[n]['tabs'] in bullDim&lt;br /&gt;
            and 'isDone' not in hlist[n]):&lt;br /&gt;
            hlist[n]['isDone'] = True&lt;br /&gt;
            dx = (tabDim[tabs]['x'] &lt;br /&gt;
                  - bullDim[tabs]['x']&lt;br /&gt;
                  - 1.5*bullDim[tabs]['width']&lt;br /&gt;
                  )&lt;br /&gt;
            dy = (y &lt;br /&gt;
                  + tabDim[tabs]['height'] / 2.&lt;br /&gt;
                  - bullDim[tabs]['y'] &lt;br /&gt;
                  - bullDim[tabs]['height'] / 2.&lt;br /&gt;
                  )&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            #print y, tabDim[tabs]['height'], bullDim[tabs]['y'], bullDim[tabs]['height'], dy&lt;br /&gt;
            clone = ET.Element('svg:use')&lt;br /&gt;
            clone.set('transform', 'translate(%f,%f)' % (dx,dy))&lt;br /&gt;
            clone.set('{%s}href'%NS['xlink'], '#bu_%d'%tabs)&lt;br /&gt;
            dst.append(clone)&lt;br /&gt;
            &lt;br /&gt;
            #path = ET.Element('svg:path')&lt;br /&gt;
            #path.set('d', ('M %f,%f' + ' L %f,%f'*5 + ' z') % (&lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y'], &lt;br /&gt;
            #    bullDim[tabs]['x']+20, bullDim[tabs]['y']+bullDim[tabs]['height']/2., &lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y']+bullDim[tabs]['height'], &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y'], &lt;br /&gt;
            #    tabDim[tabs]['x']+20, tabDim[tabs]['y']+tabDim[tabs]['height']/2., &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y']+tabDim[tabs]['height'], &lt;br /&gt;
            #    ))&lt;br /&gt;
            #dst.append(path)&lt;br /&gt;
            #break&lt;br /&gt;
&lt;br /&gt;
    # look for instance specific parts&lt;br /&gt;
    if slideId:&lt;br /&gt;
        f = instancePath(slideId)&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            comp = ET.parse(f)&lt;br /&gt;
            svg = doc0.xpath('//s:svg', NS)[0]&lt;br /&gt;
            g = &amp;quot;{%s}g&amp;quot;%NS['s']&lt;br /&gt;
            k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
            for n, i in enumerate(svg.getchildren()):&lt;br /&gt;
                if i.tag == g and i.get(k, '').startswith('Instance'):&lt;br /&gt;
                    x = comp.xpath('//s:svg//s:g[@i:label=&amp;quot;%s&amp;quot;]'%i.get(k), NS)&lt;br /&gt;
                    if x:&lt;br /&gt;
                        x = x[0]&lt;br /&gt;
                        cpy = clearId(copy.deepcopy(x))&lt;br /&gt;
                        svg[n] = cpy&lt;br /&gt;
            defs = comp.xpath('//s:svg/s:defs/*', NS)&lt;br /&gt;
            dst = doc0.xpath('//s:svg/s:defs', NS)[0]&lt;br /&gt;
            for d in defs:&lt;br /&gt;
                Id = d.get('id')&lt;br /&gt;
                current = doc0.xpath('//s:svg/s:defs/*[@id=&amp;quot;%s&amp;quot;]'%Id, NS)&lt;br /&gt;
                if not current:&lt;br /&gt;
                    cpy = copy.deepcopy(d)&lt;br /&gt;
                    dst.append(cpy)&lt;br /&gt;
&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
    &lt;br /&gt;
    return slideId&lt;br /&gt;
def instancePath(slideId, component = None):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return path to component for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # return os.path.join(slideId, component)&lt;br /&gt;
    return slideId+'.svg'&lt;br /&gt;
def writeComponents(inkscapeFile, slideId):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;write components from file for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not slideId: return&lt;br /&gt;
    doc0 = ET.parse(inkscapeFile)&lt;br /&gt;
    hasComponents = False&lt;br /&gt;
    for i in doc0.xpath('//s:svg//s:g', NS):&lt;br /&gt;
        k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
        # print i.keys(),k&lt;br /&gt;
        if (k in i.keys() and i.get(k).startswith('Instance')):&lt;br /&gt;
            if len(i) &amp;gt; 0:&lt;br /&gt;
                hasComponents = True&lt;br /&gt;
                break&lt;br /&gt;
                # ET.ElementTree(i).write(f)&lt;br /&gt;
                &lt;br /&gt;
    f = instancePath(slideId, 'components')&lt;br /&gt;
    &lt;br /&gt;
    if hasComponents:&lt;br /&gt;
        if (os.path.abspath(os.path.realpath(inkscapeFile)) != &lt;br /&gt;
            os.path.abspath(os.path.realpath(f))):&lt;br /&gt;
            file(f, 'w').write(file(inkscapeFile).read())&lt;br /&gt;
    else:&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            os.remove(f)&lt;br /&gt;
&lt;br /&gt;
if __name__ == '__main__':&lt;br /&gt;
    import sys&lt;br /&gt;
    instanceFromTemplate(sys.argv[1], 'slide%s.svg', file(sys.argv[2]).read())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16493</id>
		<title>InkSlide</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16493"/>
		<updated>2007-10-23T17:19:46Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: /* InkSlide - quick and easy presentations using Inkscape */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== InkSlide - quick and easy presentations using Inkscape ==&lt;br /&gt;
&lt;br /&gt;
InkSlide produces slides like this:&lt;br /&gt;
&lt;br /&gt;
[[Image:slide0004.jpg]]&lt;br /&gt;
&lt;br /&gt;
from simple text input like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
InkSlide: Features&lt;br /&gt;
==================&lt;br /&gt;
&lt;br /&gt;
Features include wrapped top level text and&lt;br /&gt;
- mulitple&lt;br /&gt;
  - levels&lt;br /&gt;
    - of wrapped bulleted lists with bullets and font&lt;br /&gt;
      information taken from the template file.&lt;br /&gt;
&lt;br /&gt;
Slide specific content like this:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
which is updated when the template changes.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An Inkscape file is used as a template file to define the background, title position and font, fonts and positions for text at different levels of indentation, groups to be cloned and used as bullets, etc.&lt;br /&gt;
&lt;br /&gt;
Content specific to a particular slide can also be created in Inkscape, this content merged with the template and text input to make the final slide, so changes to the template after a particular slide is edited in Inkscape are included.&lt;br /&gt;
&lt;br /&gt;
=== Download ===&lt;br /&gt;
&lt;br /&gt;
Copy the [[InkSlide#Code|text below]] into a file called 'inkslide.py'.  (How do you attach a non-image file on this wiki?)&lt;br /&gt;
&lt;br /&gt;
Here is an [[Media:template0.svg|example template]].  Use Save Link As to stop your browser displaying the file instead of downloading it.&lt;br /&gt;
&lt;br /&gt;
=== Use ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python inkslide.py myTemplate.svg mySlides.txt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This creates a slide00xx.svg for each slide in your presentation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python inkslide.py --writeInstance &amp;lt;instanceId&amp;gt; myTemplate.svg mySlides.txt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Creates / updates the Inkscape file you can edit to add special content to this particular slide.   '''NOT IMPLEMENTED YET''' - the code exists, but not the command line hook.&lt;br /&gt;
&lt;br /&gt;
=== How it works ===&lt;br /&gt;
&lt;br /&gt;
First InkSlide parses the text input, something like the example at the top of this page.  It looks for flowed text boxes in the template file with special @id attributes like ''fr_title'' for the title, and ''fr_tab2'' for the second level of bulleted lists.  Copies of these elements are made and placed in a layer called 'Texts'.  At this point, the text is all piled up at the top of the page.&lt;br /&gt;
&lt;br /&gt;
InkSlide saves the file, and uses Inkscape's command line ''--query-height'' and friends to find out how high the wrapped text pieces are.  At it then repositions them properly, adds clones of the bullet markers, etc.  Bullet markers have special @id attributes like ''bu_1'' for the first level of bulleted list.&lt;br /&gt;
&lt;br /&gt;
Finally, InkSlide checks to see if there's an %id tag for the slide, and if so if there's a corresponding Inkscape SVG file with content for this particular slide. This content has to occur in layers whose name starts with ''Instance'', and these layers have to be present in the template file, but you can have more than one, so they can be above and below the regular content.  If such content is found, it's copied into the output slide.&lt;br /&gt;
&lt;br /&gt;
=== The Template File ===&lt;br /&gt;
&lt;br /&gt;
The template file is a regular Inkscape file.  If this list seems hard, just use the example template and modify it to suit your needs.&lt;br /&gt;
&lt;br /&gt;
Required contents:&lt;br /&gt;
&lt;br /&gt;
*A layer called ''Texts'', into which text will be placed.&lt;br /&gt;
*A flowed text frame with an @id of ''fr_title'' to define the font and position for the slide title.  This should contain text like ''Title goes here Sly'' because ''Sly'' includes letters which ascend and descend from the baseline to the greatest extent.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*A group with an @id of ''gr_title'' into which the title will be placed.  This makes it easier to do special effects on the title like shadows.&lt;br /&gt;
*Flowed text frames with an @id of ''fr_tabN'' for each N in 0, 1, 2, 3.  ''fr_tab0'' is used for top level non bulleted text.  These should also include ''Sly'' or similar.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*Objects or groups withan @id of ''bu_N'' for each N in 1, 2, 3, for the three levels of bulleted list.&lt;br /&gt;
&lt;br /&gt;
The fr_* elements define the font, font-size, color etc. for the different levels of text.  They also define the position and wrap width.  See the example template file for a possible arrangement.&lt;br /&gt;
&lt;br /&gt;
Optional contents:&lt;br /&gt;
&lt;br /&gt;
*A background&lt;br /&gt;
*Affiliation text and logos you want to appear on every slide&lt;br /&gt;
*A blurred clone of the gr_title element, offset and behind it.&lt;br /&gt;
*Layers whose name starts with ''Instance'' - these are the layers the user can edit directly in Inkscape for a particular slide.&lt;br /&gt;
&lt;br /&gt;
When you save the template file you should make sure that layers containing place holder text (the fr_* elements) and bullets are invisible, or at least hidden below the background layer, which should be visible.  Likewise the Texts layer should be visible, as well as any Instance layers.&lt;br /&gt;
&lt;br /&gt;
=== Text Input ===&lt;br /&gt;
&lt;br /&gt;
InkSlide uses [http://docutils.sourceforge.net/rst.html reStructuredText] for markup, although it renders only a subset of rst.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Inkslide: an example&lt;br /&gt;
====================&lt;br /&gt;
&lt;br /&gt;
This text will appear as a top level paragraph.&lt;br /&gt;
&lt;br /&gt;
- Dash prefix indicates this text will appear as a bulleted item.&lt;br /&gt;
&lt;br /&gt;
  - Indented dash prefix -&amp;gt; second level bulleted list.&lt;br /&gt;
&lt;br /&gt;
.. is:pause&lt;br /&gt;
&lt;br /&gt;
Two slides will be produced for this input, one with everything up to the&lt;br /&gt;
&amp;quot;.. is:pause&amp;quot;, and one with that plus the remaining content.&lt;br /&gt;
Incremental list display can be done this way.&lt;br /&gt;
&lt;br /&gt;
The is:id tag below indicates that a file with a name like&lt;br /&gt;
'extra/is_intro.svg' should be inspected for extra content&lt;br /&gt;
for this particular slide.&lt;br /&gt;
&lt;br /&gt;
.. is:id is_intro&lt;br /&gt;
&lt;br /&gt;
Inkslide: the next slide...&lt;br /&gt;
===========================&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== The GUI ===&lt;br /&gt;
&lt;br /&gt;
A GUI for InkSlide is available as a plug-in for the [http://webpages.charter.net/edreamleo/front.html Leo] outline editor.  This makes slide re-arranging easy, and adds some macro substitution capability.  It also adds buttons for editing slide specific content in Inkscape.&lt;br /&gt;
&lt;br /&gt;
FIXME: post plug-in on Leo wiki and link.&lt;br /&gt;
&lt;br /&gt;
=== To do / status ===&lt;br /&gt;
&lt;br /&gt;
InkSlide is very usable, but is missing some features.  Remember you can &lt;br /&gt;
use the slide specific editing feature to do any of these things and&lt;br /&gt;
anything else you can do in Inkscape already, these will just be more&lt;br /&gt;
convenient in the text input when they're done.&lt;br /&gt;
&lt;br /&gt;
*Inline text formatting (bold, color, etc.) for part of a piece of text&lt;br /&gt;
*Justification switching&lt;br /&gt;
*Verbatim code display&lt;br /&gt;
*&amp;lt;strike&amp;gt;Simple image inclusion&amp;lt;/strike&amp;gt;&lt;br /&gt;
*more helper tools to make PNG and PDF versions of slides&lt;br /&gt;
*configuring / using a directory for edited slides&lt;br /&gt;
*command line flags for generating specific slides&lt;br /&gt;
&lt;br /&gt;
Currently InkSlide is slow, because it must invoke Inkscape once for every single dimension it needs to retrieve.  I'll try and get a --query-all switch into 0.46.&lt;br /&gt;
&lt;br /&gt;
=== Bugs ===&lt;br /&gt;
&lt;br /&gt;
*There may be an issue with the way slide specific content is merged into the template producing conflicting @id attributes, but I haven't seen this happen yet.&lt;br /&gt;
&lt;br /&gt;
=== Author  / Credits ===&lt;br /&gt;
&lt;br /&gt;
InkSlide was written by Terry Brown, terry-n-brown@yahoo.com - but use underscore, not '-'.&lt;br /&gt;
&lt;br /&gt;
InkSlide was inspired by [http://member.wide.ad.jp/wg/mgp/ MagicPoint]&lt;br /&gt;
&lt;br /&gt;
Matt Harrison suggested using reStructured text as the markup language.&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# inkslide.py $Id$&lt;br /&gt;
# Author: Terry Brown&lt;br /&gt;
# Created: Thu Oct 11 2007&lt;br /&gt;
&lt;br /&gt;
import lxml.etree as ET&lt;br /&gt;
&lt;br /&gt;
import copy&lt;br /&gt;
import subprocess&lt;br /&gt;
import os.path&lt;br /&gt;
&lt;br /&gt;
NS = { 'i': 'http://www.inkscape.org/namespaces/inkscape',&lt;br /&gt;
       's': 'http://www.w3.org/2000/svg',&lt;br /&gt;
       'xlink' : 'http://www.w3.org/1999/xlink'}&lt;br /&gt;
&lt;br /&gt;
# number of defined tablevels, FIXME, could derive from template?&lt;br /&gt;
tabLevels = 4  &lt;br /&gt;
def clearId(x, what='id', NS={}):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;recursively clear @id on element x and descendants&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if what in x.keys():&lt;br /&gt;
        del x.attrib[what]&lt;br /&gt;
    for i in x.xpath('.//*[@%s]'%what, NS):&lt;br /&gt;
        del i.attrib[what]&lt;br /&gt;
    return x&lt;br /&gt;
def getDim(fn, Id, what):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return dimension of element in fn with @id Id, what is&lt;br /&gt;
    x, y, width, height&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cmd = ('inkscape', '--without-gui', '--query-id', Id, '--query-'+what, fn)&lt;br /&gt;
    proc = subprocess.Popen(cmd, stdout = subprocess.PIPE,&lt;br /&gt;
                            stderr = subprocess.PIPE)&lt;br /&gt;
    # make new pipe for stderr to supress chatter from inkscape&lt;br /&gt;
    proc.wait()&lt;br /&gt;
    return proc.stdout.read()&lt;br /&gt;
def textReader(text):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;iterate parts in input text&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not text: return&lt;br /&gt;
    for line in text.split('\n'):&lt;br /&gt;
        if line.startswith('%slide '):&lt;br /&gt;
            yield {'type':'TITLE', 's':line[7:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%pause'):&lt;br /&gt;
            yield {'type':'PAUSE'}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%id '):&lt;br /&gt;
            yield {'type':'ID', 's':line[4:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if not line.strip():&lt;br /&gt;
            yield {'type':'BLANK'}&lt;br /&gt;
            continue&lt;br /&gt;
        tabs = len(line) - len(line.lstrip('\t'))&lt;br /&gt;
        yield {'type':'TAB', 'tabs':tabs, 's':line.strip()}&lt;br /&gt;
&lt;br /&gt;
nid = 0&lt;br /&gt;
def nextId():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return an unsed Id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # FIXME, should inspect templat doc.&lt;br /&gt;
    global nid&lt;br /&gt;
    nid += 1&lt;br /&gt;
    return 'is'+str(nid)&lt;br /&gt;
&lt;br /&gt;
def instanceFromTemplate(template, instance, text, pauseOk = True):&lt;br /&gt;
&lt;br /&gt;
    slide = 0&lt;br /&gt;
&lt;br /&gt;
    hlist = []&lt;br /&gt;
    doc0 = ET.parse(template)&lt;br /&gt;
    slideId = None&lt;br /&gt;
    &lt;br /&gt;
    for t in textReader(text):&lt;br /&gt;
        &lt;br /&gt;
        if t['type'] == 'ID':&lt;br /&gt;
            slideId = t['s']&lt;br /&gt;
            continue&lt;br /&gt;
            &lt;br /&gt;
        if t['type'] == 'TITLE':&lt;br /&gt;
            print t['s']&lt;br /&gt;
            if hlist:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
                hlist = [t]&lt;br /&gt;
                doc0 = ET.parse(template)&lt;br /&gt;
                slideId = None&lt;br /&gt;
            else:&lt;br /&gt;
                hlist.append(t)&lt;br /&gt;
            dst = doc0.xpath('//s:g[@id=&amp;quot;gr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'PAUSE':&lt;br /&gt;
            if pauseOk:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        hlist.append(t)&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'BLANK':&lt;br /&gt;
            continue&lt;br /&gt;
        if t['type'] == 'TAB':&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_tab%d&amp;quot;]' % t['tabs'], NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            #break&lt;br /&gt;
            continue&lt;br /&gt;
           &lt;br /&gt;
    process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
def process(instance, doc0, hlist, slideId, slide):&lt;br /&gt;
&lt;br /&gt;
    instance = instance.replace('%s', '%04d'%slide)&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
&lt;br /&gt;
    bullDim = {}&lt;br /&gt;
    tabDim = {}&lt;br /&gt;
    for n in range(0,tabLevels):&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;bu_%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'width', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'bu_%d'%n, i))&lt;br /&gt;
            bullDim[n] = b&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;fr_tab%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'fr_tab%d'%n, i))&lt;br /&gt;
            tabDim[n] = b&lt;br /&gt;
&lt;br /&gt;
    delta = 0.&lt;br /&gt;
    firstVert = True&lt;br /&gt;
    for n, i in enumerate(hlist):&lt;br /&gt;
        if hlist[n]['type'] in ['TITLE']:&lt;br /&gt;
            continue  # already in right place, not a vertical element&lt;br /&gt;
&lt;br /&gt;
        hgt = 0&lt;br /&gt;
        if hlist[n-1]['type'] == 'BLANK':&lt;br /&gt;
            hgt = 30&lt;br /&gt;
        else:&lt;br /&gt;
            hgt = float(getDim(instance, hlist[n-1]['id'], 'height'))&lt;br /&gt;
&lt;br /&gt;
        hgt += 5&lt;br /&gt;
&lt;br /&gt;
        if firstVert:&lt;br /&gt;
            hgt = 0&lt;br /&gt;
            firstVert = False&lt;br /&gt;
&lt;br /&gt;
        delta += hgt&lt;br /&gt;
&lt;br /&gt;
        if hlist[n]['type'] == 'BLANK': continue&lt;br /&gt;
&lt;br /&gt;
        tabs = hlist[n]['tabs']&lt;br /&gt;
&lt;br /&gt;
        r = doc0.xpath('//s:flowRoot[@id=&amp;quot;%s&amp;quot;]' % hlist[n]['id'], NS)[0]&lt;br /&gt;
        clearId(r, what='transform')&lt;br /&gt;
        r = r.xpath('.//s:rect', NS)[0]&lt;br /&gt;
        r.set('x', str(tabDim[tabs]['x']))&lt;br /&gt;
        y = tabDim[tabs]['y'] + delta&lt;br /&gt;
        #print '%s -&amp;gt; %s' % (str(r.get('y')), str(y))&lt;br /&gt;
        #print tabDim[tabs]['y']&lt;br /&gt;
        r.set('y', str(y))&lt;br /&gt;
&lt;br /&gt;
        if (hlist[n]['type'] == 'TAB' and hlist[n]['tabs'] in bullDim&lt;br /&gt;
            and 'isDone' not in hlist[n]):&lt;br /&gt;
            hlist[n]['isDone'] = True&lt;br /&gt;
            dx = (tabDim[tabs]['x'] &lt;br /&gt;
                  - bullDim[tabs]['x']&lt;br /&gt;
                  - 1.5*bullDim[tabs]['width']&lt;br /&gt;
                  )&lt;br /&gt;
            dy = (y &lt;br /&gt;
                  + tabDim[tabs]['height'] / 2.&lt;br /&gt;
                  - bullDim[tabs]['y'] &lt;br /&gt;
                  - bullDim[tabs]['height'] / 2.&lt;br /&gt;
                  )&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            #print y, tabDim[tabs]['height'], bullDim[tabs]['y'], bullDim[tabs]['height'], dy&lt;br /&gt;
            clone = ET.Element('svg:use')&lt;br /&gt;
            clone.set('transform', 'translate(%f,%f)' % (dx,dy))&lt;br /&gt;
            clone.set('{%s}href'%NS['xlink'], '#bu_%d'%tabs)&lt;br /&gt;
            dst.append(clone)&lt;br /&gt;
            &lt;br /&gt;
            #path = ET.Element('svg:path')&lt;br /&gt;
            #path.set('d', ('M %f,%f' + ' L %f,%f'*5 + ' z') % (&lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y'], &lt;br /&gt;
            #    bullDim[tabs]['x']+20, bullDim[tabs]['y']+bullDim[tabs]['height']/2., &lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y']+bullDim[tabs]['height'], &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y'], &lt;br /&gt;
            #    tabDim[tabs]['x']+20, tabDim[tabs]['y']+tabDim[tabs]['height']/2., &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y']+tabDim[tabs]['height'], &lt;br /&gt;
            #    ))&lt;br /&gt;
            #dst.append(path)&lt;br /&gt;
            #break&lt;br /&gt;
&lt;br /&gt;
    # look for instance specific parts&lt;br /&gt;
    if slideId:&lt;br /&gt;
        f = instancePath(slideId)&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            comp = ET.parse(f)&lt;br /&gt;
            svg = doc0.xpath('//s:svg', NS)[0]&lt;br /&gt;
            g = &amp;quot;{%s}g&amp;quot;%NS['s']&lt;br /&gt;
            k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
            for n, i in enumerate(svg.getchildren()):&lt;br /&gt;
                if i.tag == g and i.get(k, '').startswith('Instance'):&lt;br /&gt;
                    x = comp.xpath('//s:svg//s:g[@i:label=&amp;quot;%s&amp;quot;]'%i.get(k), NS)&lt;br /&gt;
                    if x:&lt;br /&gt;
                        x = x[0]&lt;br /&gt;
                        cpy = clearId(copy.deepcopy(x))&lt;br /&gt;
                        svg[n] = cpy&lt;br /&gt;
            defs = comp.xpath('//s:svg/s:defs/*', NS)&lt;br /&gt;
            dst = doc0.xpath('//s:svg/s:defs', NS)[0]&lt;br /&gt;
            for d in defs:&lt;br /&gt;
                Id = d.get('id')&lt;br /&gt;
                current = doc0.xpath('//s:svg/s:defs/*[@id=&amp;quot;%s&amp;quot;]'%Id, NS)&lt;br /&gt;
                if not current:&lt;br /&gt;
                    cpy = copy.deepcopy(d)&lt;br /&gt;
                    dst.append(cpy)&lt;br /&gt;
&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
    &lt;br /&gt;
    return slideId&lt;br /&gt;
def instancePath(slideId, component = None):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return path to component for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # return os.path.join(slideId, component)&lt;br /&gt;
    return slideId+'.svg'&lt;br /&gt;
def writeComponents(inkscapeFile, slideId):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;write components from file for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not slideId: return&lt;br /&gt;
    doc0 = ET.parse(inkscapeFile)&lt;br /&gt;
    hasComponents = False&lt;br /&gt;
    for i in doc0.xpath('//s:svg//s:g', NS):&lt;br /&gt;
        k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
        # print i.keys(),k&lt;br /&gt;
        if (k in i.keys() and i.get(k).startswith('Instance')):&lt;br /&gt;
            if len(i) &amp;gt; 0:&lt;br /&gt;
                hasComponents = True&lt;br /&gt;
                break&lt;br /&gt;
                # ET.ElementTree(i).write(f)&lt;br /&gt;
                &lt;br /&gt;
    f = instancePath(slideId, 'components')&lt;br /&gt;
    &lt;br /&gt;
    if hasComponents:&lt;br /&gt;
        if (os.path.abspath(os.path.realpath(inkscapeFile)) != &lt;br /&gt;
            os.path.abspath(os.path.realpath(f))):&lt;br /&gt;
            file(f, 'w').write(file(inkscapeFile).read())&lt;br /&gt;
    else:&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            os.remove(f)&lt;br /&gt;
&lt;br /&gt;
if __name__ == '__main__':&lt;br /&gt;
    import sys&lt;br /&gt;
    instanceFromTemplate(sys.argv[1], 'slide%s.svg', file(sys.argv[2]).read())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16492</id>
		<title>InkSlide</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16492"/>
		<updated>2007-10-23T17:18:47Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: /* Text Input */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== InkSlide - quick and easy presentations using Inkscape ==&lt;br /&gt;
&lt;br /&gt;
InkSlide produces slides like this:&lt;br /&gt;
&lt;br /&gt;
[[Image:slide0004.jpg]]&lt;br /&gt;
&lt;br /&gt;
from simple text input like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%slide InkSlide: Features&lt;br /&gt;
Features include wrapped top level text and&lt;br /&gt;
    mulitple&lt;br /&gt;
        levels&lt;br /&gt;
            of wrapped bulleted lists with bullets and font&lt;br /&gt;
information taken from the template file.&lt;br /&gt;
&lt;br /&gt;
Slide specific content like this:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
which is updated when the template changes.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An Inkscape file is used as a template file to define the background, title position and font, fonts and positions for text at different levels of indentation, groups to be cloned and used as bullets, etc.&lt;br /&gt;
&lt;br /&gt;
Content specific to a particular slide can also be created in Inkscape, this content merged with the template and text input to make the final slide, so changes to the template after a particular slide is edited in Inkscape are included.&lt;br /&gt;
&lt;br /&gt;
=== Download ===&lt;br /&gt;
&lt;br /&gt;
Copy the [[InkSlide#Code|text below]] into a file called 'inkslide.py'.  (How do you attach a non-image file on this wiki?)&lt;br /&gt;
&lt;br /&gt;
Here is an [[Media:template0.svg|example template]].  Use Save Link As to stop your browser displaying the file instead of downloading it.&lt;br /&gt;
&lt;br /&gt;
=== Use ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python inkslide.py myTemplate.svg mySlides.txt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This creates a slide00xx.svg for each slide in your presentation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python inkslide.py --writeInstance &amp;lt;instanceId&amp;gt; myTemplate.svg mySlides.txt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Creates / updates the Inkscape file you can edit to add special content to this particular slide.   '''NOT IMPLEMENTED YET''' - the code exists, but not the command line hook.&lt;br /&gt;
&lt;br /&gt;
=== How it works ===&lt;br /&gt;
&lt;br /&gt;
First InkSlide parses the text input, something like the example at the top of this page.  It looks for flowed text boxes in the template file with special @id attributes like ''fr_title'' for the title, and ''fr_tab2'' for the second level of bulleted lists.  Copies of these elements are made and placed in a layer called 'Texts'.  At this point, the text is all piled up at the top of the page.&lt;br /&gt;
&lt;br /&gt;
InkSlide saves the file, and uses Inkscape's command line ''--query-height'' and friends to find out how high the wrapped text pieces are.  At it then repositions them properly, adds clones of the bullet markers, etc.  Bullet markers have special @id attributes like ''bu_1'' for the first level of bulleted list.&lt;br /&gt;
&lt;br /&gt;
Finally, InkSlide checks to see if there's an %id tag for the slide, and if so if there's a corresponding Inkscape SVG file with content for this particular slide. This content has to occur in layers whose name starts with ''Instance'', and these layers have to be present in the template file, but you can have more than one, so they can be above and below the regular content.  If such content is found, it's copied into the output slide.&lt;br /&gt;
&lt;br /&gt;
=== The Template File ===&lt;br /&gt;
&lt;br /&gt;
The template file is a regular Inkscape file.  If this list seems hard, just use the example template and modify it to suit your needs.&lt;br /&gt;
&lt;br /&gt;
Required contents:&lt;br /&gt;
&lt;br /&gt;
*A layer called ''Texts'', into which text will be placed.&lt;br /&gt;
*A flowed text frame with an @id of ''fr_title'' to define the font and position for the slide title.  This should contain text like ''Title goes here Sly'' because ''Sly'' includes letters which ascend and descend from the baseline to the greatest extent.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*A group with an @id of ''gr_title'' into which the title will be placed.  This makes it easier to do special effects on the title like shadows.&lt;br /&gt;
*Flowed text frames with an @id of ''fr_tabN'' for each N in 0, 1, 2, 3.  ''fr_tab0'' is used for top level non bulleted text.  These should also include ''Sly'' or similar.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*Objects or groups withan @id of ''bu_N'' for each N in 1, 2, 3, for the three levels of bulleted list.&lt;br /&gt;
&lt;br /&gt;
The fr_* elements define the font, font-size, color etc. for the different levels of text.  They also define the position and wrap width.  See the example template file for a possible arrangement.&lt;br /&gt;
&lt;br /&gt;
Optional contents:&lt;br /&gt;
&lt;br /&gt;
*A background&lt;br /&gt;
*Affiliation text and logos you want to appear on every slide&lt;br /&gt;
*A blurred clone of the gr_title element, offset and behind it.&lt;br /&gt;
*Layers whose name starts with ''Instance'' - these are the layers the user can edit directly in Inkscape for a particular slide.&lt;br /&gt;
&lt;br /&gt;
When you save the template file you should make sure that layers containing place holder text (the fr_* elements) and bullets are invisible, or at least hidden below the background layer, which should be visible.  Likewise the Texts layer should be visible, as well as any Instance layers.&lt;br /&gt;
&lt;br /&gt;
=== Text Input ===&lt;br /&gt;
&lt;br /&gt;
InkSlide uses [http://docutils.sourceforge.net/rst.html reStructuredText] for markup, although it renders only a subset of rst.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Inkslide: an example&lt;br /&gt;
====================&lt;br /&gt;
&lt;br /&gt;
This text will appear as a top level paragraph.&lt;br /&gt;
&lt;br /&gt;
- Dash prefix indicates this text will appear as a bulleted item.&lt;br /&gt;
&lt;br /&gt;
  - Indented dash prefix -&amp;gt; second level bulleted list.&lt;br /&gt;
&lt;br /&gt;
.. is:pause&lt;br /&gt;
&lt;br /&gt;
Two slides will be produced for this input, one with everything up to the&lt;br /&gt;
&amp;quot;.. is:pause&amp;quot;, and one with that plus the remaining content.&lt;br /&gt;
Incremental list display can be done this way.&lt;br /&gt;
&lt;br /&gt;
The is:id tag below indicates that a file with a name like&lt;br /&gt;
'extra/is_intro.svg' should be inspected for extra content&lt;br /&gt;
for this particular slide.&lt;br /&gt;
&lt;br /&gt;
.. is:id is_intro&lt;br /&gt;
&lt;br /&gt;
Inkslide: the next slide...&lt;br /&gt;
===========================&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== The GUI ===&lt;br /&gt;
&lt;br /&gt;
A GUI for InkSlide is available as a plug-in for the [http://webpages.charter.net/edreamleo/front.html Leo] outline editor.  This makes slide re-arranging easy, and adds some macro substitution capability.  It also adds buttons for editing slide specific content in Inkscape.&lt;br /&gt;
&lt;br /&gt;
FIXME: post plug-in on Leo wiki and link.&lt;br /&gt;
&lt;br /&gt;
=== To do / status ===&lt;br /&gt;
&lt;br /&gt;
InkSlide is very usable, but is missing some features.  Remember you can &lt;br /&gt;
use the slide specific editing feature to do any of these things and&lt;br /&gt;
anything else you can do in Inkscape already, these will just be more&lt;br /&gt;
convenient in the text input when they're done.&lt;br /&gt;
&lt;br /&gt;
*Inline text formatting (bold, color, etc.) for part of a piece of text&lt;br /&gt;
*Justification switching&lt;br /&gt;
*Verbatim code display&lt;br /&gt;
*&amp;lt;strike&amp;gt;Simple image inclusion&amp;lt;/strike&amp;gt;&lt;br /&gt;
*more helper tools to make PNG and PDF versions of slides&lt;br /&gt;
*configuring / using a directory for edited slides&lt;br /&gt;
*command line flags for generating specific slides&lt;br /&gt;
&lt;br /&gt;
Currently InkSlide is slow, because it must invoke Inkscape once for every single dimension it needs to retrieve.  I'll try and get a --query-all switch into 0.46.&lt;br /&gt;
&lt;br /&gt;
=== Bugs ===&lt;br /&gt;
&lt;br /&gt;
*There may be an issue with the way slide specific content is merged into the template producing conflicting @id attributes, but I haven't seen this happen yet.&lt;br /&gt;
&lt;br /&gt;
=== Author  / Credits ===&lt;br /&gt;
&lt;br /&gt;
InkSlide was written by Terry Brown, terry-n-brown@yahoo.com - but use underscore, not '-'.&lt;br /&gt;
&lt;br /&gt;
InkSlide was inspired by [http://member.wide.ad.jp/wg/mgp/ MagicPoint]&lt;br /&gt;
&lt;br /&gt;
Matt Harrison suggested using reStructured text as the markup language.&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# inkslide.py $Id$&lt;br /&gt;
# Author: Terry Brown&lt;br /&gt;
# Created: Thu Oct 11 2007&lt;br /&gt;
&lt;br /&gt;
import lxml.etree as ET&lt;br /&gt;
&lt;br /&gt;
import copy&lt;br /&gt;
import subprocess&lt;br /&gt;
import os.path&lt;br /&gt;
&lt;br /&gt;
NS = { 'i': 'http://www.inkscape.org/namespaces/inkscape',&lt;br /&gt;
       's': 'http://www.w3.org/2000/svg',&lt;br /&gt;
       'xlink' : 'http://www.w3.org/1999/xlink'}&lt;br /&gt;
&lt;br /&gt;
# number of defined tablevels, FIXME, could derive from template?&lt;br /&gt;
tabLevels = 4  &lt;br /&gt;
def clearId(x, what='id', NS={}):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;recursively clear @id on element x and descendants&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if what in x.keys():&lt;br /&gt;
        del x.attrib[what]&lt;br /&gt;
    for i in x.xpath('.//*[@%s]'%what, NS):&lt;br /&gt;
        del i.attrib[what]&lt;br /&gt;
    return x&lt;br /&gt;
def getDim(fn, Id, what):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return dimension of element in fn with @id Id, what is&lt;br /&gt;
    x, y, width, height&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cmd = ('inkscape', '--without-gui', '--query-id', Id, '--query-'+what, fn)&lt;br /&gt;
    proc = subprocess.Popen(cmd, stdout = subprocess.PIPE,&lt;br /&gt;
                            stderr = subprocess.PIPE)&lt;br /&gt;
    # make new pipe for stderr to supress chatter from inkscape&lt;br /&gt;
    proc.wait()&lt;br /&gt;
    return proc.stdout.read()&lt;br /&gt;
def textReader(text):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;iterate parts in input text&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not text: return&lt;br /&gt;
    for line in text.split('\n'):&lt;br /&gt;
        if line.startswith('%slide '):&lt;br /&gt;
            yield {'type':'TITLE', 's':line[7:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%pause'):&lt;br /&gt;
            yield {'type':'PAUSE'}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%id '):&lt;br /&gt;
            yield {'type':'ID', 's':line[4:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if not line.strip():&lt;br /&gt;
            yield {'type':'BLANK'}&lt;br /&gt;
            continue&lt;br /&gt;
        tabs = len(line) - len(line.lstrip('\t'))&lt;br /&gt;
        yield {'type':'TAB', 'tabs':tabs, 's':line.strip()}&lt;br /&gt;
&lt;br /&gt;
nid = 0&lt;br /&gt;
def nextId():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return an unsed Id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # FIXME, should inspect templat doc.&lt;br /&gt;
    global nid&lt;br /&gt;
    nid += 1&lt;br /&gt;
    return 'is'+str(nid)&lt;br /&gt;
&lt;br /&gt;
def instanceFromTemplate(template, instance, text, pauseOk = True):&lt;br /&gt;
&lt;br /&gt;
    slide = 0&lt;br /&gt;
&lt;br /&gt;
    hlist = []&lt;br /&gt;
    doc0 = ET.parse(template)&lt;br /&gt;
    slideId = None&lt;br /&gt;
    &lt;br /&gt;
    for t in textReader(text):&lt;br /&gt;
        &lt;br /&gt;
        if t['type'] == 'ID':&lt;br /&gt;
            slideId = t['s']&lt;br /&gt;
            continue&lt;br /&gt;
            &lt;br /&gt;
        if t['type'] == 'TITLE':&lt;br /&gt;
            print t['s']&lt;br /&gt;
            if hlist:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
                hlist = [t]&lt;br /&gt;
                doc0 = ET.parse(template)&lt;br /&gt;
                slideId = None&lt;br /&gt;
            else:&lt;br /&gt;
                hlist.append(t)&lt;br /&gt;
            dst = doc0.xpath('//s:g[@id=&amp;quot;gr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'PAUSE':&lt;br /&gt;
            if pauseOk:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        hlist.append(t)&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'BLANK':&lt;br /&gt;
            continue&lt;br /&gt;
        if t['type'] == 'TAB':&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_tab%d&amp;quot;]' % t['tabs'], NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            #break&lt;br /&gt;
            continue&lt;br /&gt;
           &lt;br /&gt;
    process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
def process(instance, doc0, hlist, slideId, slide):&lt;br /&gt;
&lt;br /&gt;
    instance = instance.replace('%s', '%04d'%slide)&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
&lt;br /&gt;
    bullDim = {}&lt;br /&gt;
    tabDim = {}&lt;br /&gt;
    for n in range(0,tabLevels):&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;bu_%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'width', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'bu_%d'%n, i))&lt;br /&gt;
            bullDim[n] = b&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;fr_tab%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'fr_tab%d'%n, i))&lt;br /&gt;
            tabDim[n] = b&lt;br /&gt;
&lt;br /&gt;
    delta = 0.&lt;br /&gt;
    firstVert = True&lt;br /&gt;
    for n, i in enumerate(hlist):&lt;br /&gt;
        if hlist[n]['type'] in ['TITLE']:&lt;br /&gt;
            continue  # already in right place, not a vertical element&lt;br /&gt;
&lt;br /&gt;
        hgt = 0&lt;br /&gt;
        if hlist[n-1]['type'] == 'BLANK':&lt;br /&gt;
            hgt = 30&lt;br /&gt;
        else:&lt;br /&gt;
            hgt = float(getDim(instance, hlist[n-1]['id'], 'height'))&lt;br /&gt;
&lt;br /&gt;
        hgt += 5&lt;br /&gt;
&lt;br /&gt;
        if firstVert:&lt;br /&gt;
            hgt = 0&lt;br /&gt;
            firstVert = False&lt;br /&gt;
&lt;br /&gt;
        delta += hgt&lt;br /&gt;
&lt;br /&gt;
        if hlist[n]['type'] == 'BLANK': continue&lt;br /&gt;
&lt;br /&gt;
        tabs = hlist[n]['tabs']&lt;br /&gt;
&lt;br /&gt;
        r = doc0.xpath('//s:flowRoot[@id=&amp;quot;%s&amp;quot;]' % hlist[n]['id'], NS)[0]&lt;br /&gt;
        clearId(r, what='transform')&lt;br /&gt;
        r = r.xpath('.//s:rect', NS)[0]&lt;br /&gt;
        r.set('x', str(tabDim[tabs]['x']))&lt;br /&gt;
        y = tabDim[tabs]['y'] + delta&lt;br /&gt;
        #print '%s -&amp;gt; %s' % (str(r.get('y')), str(y))&lt;br /&gt;
        #print tabDim[tabs]['y']&lt;br /&gt;
        r.set('y', str(y))&lt;br /&gt;
&lt;br /&gt;
        if (hlist[n]['type'] == 'TAB' and hlist[n]['tabs'] in bullDim&lt;br /&gt;
            and 'isDone' not in hlist[n]):&lt;br /&gt;
            hlist[n]['isDone'] = True&lt;br /&gt;
            dx = (tabDim[tabs]['x'] &lt;br /&gt;
                  - bullDim[tabs]['x']&lt;br /&gt;
                  - 1.5*bullDim[tabs]['width']&lt;br /&gt;
                  )&lt;br /&gt;
            dy = (y &lt;br /&gt;
                  + tabDim[tabs]['height'] / 2.&lt;br /&gt;
                  - bullDim[tabs]['y'] &lt;br /&gt;
                  - bullDim[tabs]['height'] / 2.&lt;br /&gt;
                  )&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            #print y, tabDim[tabs]['height'], bullDim[tabs]['y'], bullDim[tabs]['height'], dy&lt;br /&gt;
            clone = ET.Element('svg:use')&lt;br /&gt;
            clone.set('transform', 'translate(%f,%f)' % (dx,dy))&lt;br /&gt;
            clone.set('{%s}href'%NS['xlink'], '#bu_%d'%tabs)&lt;br /&gt;
            dst.append(clone)&lt;br /&gt;
            &lt;br /&gt;
            #path = ET.Element('svg:path')&lt;br /&gt;
            #path.set('d', ('M %f,%f' + ' L %f,%f'*5 + ' z') % (&lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y'], &lt;br /&gt;
            #    bullDim[tabs]['x']+20, bullDim[tabs]['y']+bullDim[tabs]['height']/2., &lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y']+bullDim[tabs]['height'], &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y'], &lt;br /&gt;
            #    tabDim[tabs]['x']+20, tabDim[tabs]['y']+tabDim[tabs]['height']/2., &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y']+tabDim[tabs]['height'], &lt;br /&gt;
            #    ))&lt;br /&gt;
            #dst.append(path)&lt;br /&gt;
            #break&lt;br /&gt;
&lt;br /&gt;
    # look for instance specific parts&lt;br /&gt;
    if slideId:&lt;br /&gt;
        f = instancePath(slideId)&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            comp = ET.parse(f)&lt;br /&gt;
            svg = doc0.xpath('//s:svg', NS)[0]&lt;br /&gt;
            g = &amp;quot;{%s}g&amp;quot;%NS['s']&lt;br /&gt;
            k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
            for n, i in enumerate(svg.getchildren()):&lt;br /&gt;
                if i.tag == g and i.get(k, '').startswith('Instance'):&lt;br /&gt;
                    x = comp.xpath('//s:svg//s:g[@i:label=&amp;quot;%s&amp;quot;]'%i.get(k), NS)&lt;br /&gt;
                    if x:&lt;br /&gt;
                        x = x[0]&lt;br /&gt;
                        cpy = clearId(copy.deepcopy(x))&lt;br /&gt;
                        svg[n] = cpy&lt;br /&gt;
            defs = comp.xpath('//s:svg/s:defs/*', NS)&lt;br /&gt;
            dst = doc0.xpath('//s:svg/s:defs', NS)[0]&lt;br /&gt;
            for d in defs:&lt;br /&gt;
                Id = d.get('id')&lt;br /&gt;
                current = doc0.xpath('//s:svg/s:defs/*[@id=&amp;quot;%s&amp;quot;]'%Id, NS)&lt;br /&gt;
                if not current:&lt;br /&gt;
                    cpy = copy.deepcopy(d)&lt;br /&gt;
                    dst.append(cpy)&lt;br /&gt;
&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
    &lt;br /&gt;
    return slideId&lt;br /&gt;
def instancePath(slideId, component = None):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return path to component for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # return os.path.join(slideId, component)&lt;br /&gt;
    return slideId+'.svg'&lt;br /&gt;
def writeComponents(inkscapeFile, slideId):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;write components from file for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not slideId: return&lt;br /&gt;
    doc0 = ET.parse(inkscapeFile)&lt;br /&gt;
    hasComponents = False&lt;br /&gt;
    for i in doc0.xpath('//s:svg//s:g', NS):&lt;br /&gt;
        k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
        # print i.keys(),k&lt;br /&gt;
        if (k in i.keys() and i.get(k).startswith('Instance')):&lt;br /&gt;
            if len(i) &amp;gt; 0:&lt;br /&gt;
                hasComponents = True&lt;br /&gt;
                break&lt;br /&gt;
                # ET.ElementTree(i).write(f)&lt;br /&gt;
                &lt;br /&gt;
    f = instancePath(slideId, 'components')&lt;br /&gt;
    &lt;br /&gt;
    if hasComponents:&lt;br /&gt;
        if (os.path.abspath(os.path.realpath(inkscapeFile)) != &lt;br /&gt;
            os.path.abspath(os.path.realpath(f))):&lt;br /&gt;
            file(f, 'w').write(file(inkscapeFile).read())&lt;br /&gt;
    else:&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            os.remove(f)&lt;br /&gt;
&lt;br /&gt;
if __name__ == '__main__':&lt;br /&gt;
    import sys&lt;br /&gt;
    instanceFromTemplate(sys.argv[1], 'slide%s.svg', file(sys.argv[2]).read())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16491</id>
		<title>InkSlide</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16491"/>
		<updated>2007-10-23T17:13:38Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: /* Author  / Credits */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== InkSlide - quick and easy presentations using Inkscape ==&lt;br /&gt;
&lt;br /&gt;
InkSlide produces slides like this:&lt;br /&gt;
&lt;br /&gt;
[[Image:slide0004.jpg]]&lt;br /&gt;
&lt;br /&gt;
from simple text input like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%slide InkSlide: Features&lt;br /&gt;
Features include wrapped top level text and&lt;br /&gt;
    mulitple&lt;br /&gt;
        levels&lt;br /&gt;
            of wrapped bulleted lists with bullets and font&lt;br /&gt;
information taken from the template file.&lt;br /&gt;
&lt;br /&gt;
Slide specific content like this:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
which is updated when the template changes.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An Inkscape file is used as a template file to define the background, title position and font, fonts and positions for text at different levels of indentation, groups to be cloned and used as bullets, etc.&lt;br /&gt;
&lt;br /&gt;
Content specific to a particular slide can also be created in Inkscape, this content merged with the template and text input to make the final slide, so changes to the template after a particular slide is edited in Inkscape are included.&lt;br /&gt;
&lt;br /&gt;
=== Download ===&lt;br /&gt;
&lt;br /&gt;
Copy the [[InkSlide#Code|text below]] into a file called 'inkslide.py'.  (How do you attach a non-image file on this wiki?)&lt;br /&gt;
&lt;br /&gt;
Here is an [[Media:template0.svg|example template]].  Use Save Link As to stop your browser displaying the file instead of downloading it.&lt;br /&gt;
&lt;br /&gt;
=== Use ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python inkslide.py myTemplate.svg mySlides.txt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This creates a slide00xx.svg for each slide in your presentation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python inkslide.py --writeInstance &amp;lt;instanceId&amp;gt; myTemplate.svg mySlides.txt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Creates / updates the Inkscape file you can edit to add special content to this particular slide.   '''NOT IMPLEMENTED YET''' - the code exists, but not the command line hook.&lt;br /&gt;
&lt;br /&gt;
=== How it works ===&lt;br /&gt;
&lt;br /&gt;
First InkSlide parses the text input, something like the example at the top of this page.  It looks for flowed text boxes in the template file with special @id attributes like ''fr_title'' for the title, and ''fr_tab2'' for the second level of bulleted lists.  Copies of these elements are made and placed in a layer called 'Texts'.  At this point, the text is all piled up at the top of the page.&lt;br /&gt;
&lt;br /&gt;
InkSlide saves the file, and uses Inkscape's command line ''--query-height'' and friends to find out how high the wrapped text pieces are.  At it then repositions them properly, adds clones of the bullet markers, etc.  Bullet markers have special @id attributes like ''bu_1'' for the first level of bulleted list.&lt;br /&gt;
&lt;br /&gt;
Finally, InkSlide checks to see if there's an %id tag for the slide, and if so if there's a corresponding Inkscape SVG file with content for this particular slide. This content has to occur in layers whose name starts with ''Instance'', and these layers have to be present in the template file, but you can have more than one, so they can be above and below the regular content.  If such content is found, it's copied into the output slide.&lt;br /&gt;
&lt;br /&gt;
=== The Template File ===&lt;br /&gt;
&lt;br /&gt;
The template file is a regular Inkscape file.  If this list seems hard, just use the example template and modify it to suit your needs.&lt;br /&gt;
&lt;br /&gt;
Required contents:&lt;br /&gt;
&lt;br /&gt;
*A layer called ''Texts'', into which text will be placed.&lt;br /&gt;
*A flowed text frame with an @id of ''fr_title'' to define the font and position for the slide title.  This should contain text like ''Title goes here Sly'' because ''Sly'' includes letters which ascend and descend from the baseline to the greatest extent.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*A group with an @id of ''gr_title'' into which the title will be placed.  This makes it easier to do special effects on the title like shadows.&lt;br /&gt;
*Flowed text frames with an @id of ''fr_tabN'' for each N in 0, 1, 2, 3.  ''fr_tab0'' is used for top level non bulleted text.  These should also include ''Sly'' or similar.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*Objects or groups withan @id of ''bu_N'' for each N in 1, 2, 3, for the three levels of bulleted list.&lt;br /&gt;
&lt;br /&gt;
The fr_* elements define the font, font-size, color etc. for the different levels of text.  They also define the position and wrap width.  See the example template file for a possible arrangement.&lt;br /&gt;
&lt;br /&gt;
Optional contents:&lt;br /&gt;
&lt;br /&gt;
*A background&lt;br /&gt;
*Affiliation text and logos you want to appear on every slide&lt;br /&gt;
*A blurred clone of the gr_title element, offset and behind it.&lt;br /&gt;
*Layers whose name starts with ''Instance'' - these are the layers the user can edit directly in Inkscape for a particular slide.&lt;br /&gt;
&lt;br /&gt;
When you save the template file you should make sure that layers containing place holder text (the fr_* elements) and bullets are invisible, or at least hidden below the background layer, which should be visible.  Likewise the Texts layer should be visible, as well as any Instance layers.&lt;br /&gt;
&lt;br /&gt;
=== Text Input ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%title Inkslide: an example&lt;br /&gt;
This text will appear as a top level paragraph.&lt;br /&gt;
    Indented by a single tab, this text will appear as a bulleted item.&lt;br /&gt;
        Two tabs -&amp;gt; second level bulleted list.&lt;br /&gt;
&lt;br /&gt;
A gap will be left for the preceeding line.&lt;br /&gt;
%pause&lt;br /&gt;
Two slides will be produced for this input, one with everything up to the %pause above,&lt;br /&gt;
and one with that plus the remaining content.&lt;br /&gt;
Incremental list display can be done this way.&lt;br /&gt;
&lt;br /&gt;
The %id tag below indicates that a file with a name like 'extra/is_intro.svg' should&lt;br /&gt;
be inspected for extra content for this particular slide.&lt;br /&gt;
%id is_intro&lt;br /&gt;
%title Inkslide: the next slide...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== The GUI ===&lt;br /&gt;
&lt;br /&gt;
A GUI for InkSlide is available as a plug-in for the [http://webpages.charter.net/edreamleo/front.html Leo] outline editor.  This makes slide re-arranging easy, and adds some macro substitution capability.  It also adds buttons for editing slide specific content in Inkscape.&lt;br /&gt;
&lt;br /&gt;
FIXME: post plug-in on Leo wiki and link.&lt;br /&gt;
&lt;br /&gt;
=== To do / status ===&lt;br /&gt;
&lt;br /&gt;
InkSlide is very usable, but is missing some features.  Remember you can &lt;br /&gt;
use the slide specific editing feature to do any of these things and&lt;br /&gt;
anything else you can do in Inkscape already, these will just be more&lt;br /&gt;
convenient in the text input when they're done.&lt;br /&gt;
&lt;br /&gt;
*Inline text formatting (bold, color, etc.) for part of a piece of text&lt;br /&gt;
*Justification switching&lt;br /&gt;
*Verbatim code display&lt;br /&gt;
*&amp;lt;strike&amp;gt;Simple image inclusion&amp;lt;/strike&amp;gt;&lt;br /&gt;
*more helper tools to make PNG and PDF versions of slides&lt;br /&gt;
*configuring / using a directory for edited slides&lt;br /&gt;
*command line flags for generating specific slides&lt;br /&gt;
&lt;br /&gt;
Currently InkSlide is slow, because it must invoke Inkscape once for every single dimension it needs to retrieve.  I'll try and get a --query-all switch into 0.46.&lt;br /&gt;
&lt;br /&gt;
=== Bugs ===&lt;br /&gt;
&lt;br /&gt;
*There may be an issue with the way slide specific content is merged into the template producing conflicting @id attributes, but I haven't seen this happen yet.&lt;br /&gt;
&lt;br /&gt;
=== Author  / Credits ===&lt;br /&gt;
&lt;br /&gt;
InkSlide was written by Terry Brown, terry-n-brown@yahoo.com - but use underscore, not '-'.&lt;br /&gt;
&lt;br /&gt;
InkSlide was inspired by [http://member.wide.ad.jp/wg/mgp/ MagicPoint]&lt;br /&gt;
&lt;br /&gt;
Matt Harrison suggested using reStructured text as the markup language.&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# inkslide.py $Id$&lt;br /&gt;
# Author: Terry Brown&lt;br /&gt;
# Created: Thu Oct 11 2007&lt;br /&gt;
&lt;br /&gt;
import lxml.etree as ET&lt;br /&gt;
&lt;br /&gt;
import copy&lt;br /&gt;
import subprocess&lt;br /&gt;
import os.path&lt;br /&gt;
&lt;br /&gt;
NS = { 'i': 'http://www.inkscape.org/namespaces/inkscape',&lt;br /&gt;
       's': 'http://www.w3.org/2000/svg',&lt;br /&gt;
       'xlink' : 'http://www.w3.org/1999/xlink'}&lt;br /&gt;
&lt;br /&gt;
# number of defined tablevels, FIXME, could derive from template?&lt;br /&gt;
tabLevels = 4  &lt;br /&gt;
def clearId(x, what='id', NS={}):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;recursively clear @id on element x and descendants&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if what in x.keys():&lt;br /&gt;
        del x.attrib[what]&lt;br /&gt;
    for i in x.xpath('.//*[@%s]'%what, NS):&lt;br /&gt;
        del i.attrib[what]&lt;br /&gt;
    return x&lt;br /&gt;
def getDim(fn, Id, what):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return dimension of element in fn with @id Id, what is&lt;br /&gt;
    x, y, width, height&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cmd = ('inkscape', '--without-gui', '--query-id', Id, '--query-'+what, fn)&lt;br /&gt;
    proc = subprocess.Popen(cmd, stdout = subprocess.PIPE,&lt;br /&gt;
                            stderr = subprocess.PIPE)&lt;br /&gt;
    # make new pipe for stderr to supress chatter from inkscape&lt;br /&gt;
    proc.wait()&lt;br /&gt;
    return proc.stdout.read()&lt;br /&gt;
def textReader(text):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;iterate parts in input text&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not text: return&lt;br /&gt;
    for line in text.split('\n'):&lt;br /&gt;
        if line.startswith('%slide '):&lt;br /&gt;
            yield {'type':'TITLE', 's':line[7:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%pause'):&lt;br /&gt;
            yield {'type':'PAUSE'}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%id '):&lt;br /&gt;
            yield {'type':'ID', 's':line[4:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if not line.strip():&lt;br /&gt;
            yield {'type':'BLANK'}&lt;br /&gt;
            continue&lt;br /&gt;
        tabs = len(line) - len(line.lstrip('\t'))&lt;br /&gt;
        yield {'type':'TAB', 'tabs':tabs, 's':line.strip()}&lt;br /&gt;
&lt;br /&gt;
nid = 0&lt;br /&gt;
def nextId():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return an unsed Id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # FIXME, should inspect templat doc.&lt;br /&gt;
    global nid&lt;br /&gt;
    nid += 1&lt;br /&gt;
    return 'is'+str(nid)&lt;br /&gt;
&lt;br /&gt;
def instanceFromTemplate(template, instance, text, pauseOk = True):&lt;br /&gt;
&lt;br /&gt;
    slide = 0&lt;br /&gt;
&lt;br /&gt;
    hlist = []&lt;br /&gt;
    doc0 = ET.parse(template)&lt;br /&gt;
    slideId = None&lt;br /&gt;
    &lt;br /&gt;
    for t in textReader(text):&lt;br /&gt;
        &lt;br /&gt;
        if t['type'] == 'ID':&lt;br /&gt;
            slideId = t['s']&lt;br /&gt;
            continue&lt;br /&gt;
            &lt;br /&gt;
        if t['type'] == 'TITLE':&lt;br /&gt;
            print t['s']&lt;br /&gt;
            if hlist:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
                hlist = [t]&lt;br /&gt;
                doc0 = ET.parse(template)&lt;br /&gt;
                slideId = None&lt;br /&gt;
            else:&lt;br /&gt;
                hlist.append(t)&lt;br /&gt;
            dst = doc0.xpath('//s:g[@id=&amp;quot;gr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'PAUSE':&lt;br /&gt;
            if pauseOk:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        hlist.append(t)&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'BLANK':&lt;br /&gt;
            continue&lt;br /&gt;
        if t['type'] == 'TAB':&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_tab%d&amp;quot;]' % t['tabs'], NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            #break&lt;br /&gt;
            continue&lt;br /&gt;
           &lt;br /&gt;
    process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
def process(instance, doc0, hlist, slideId, slide):&lt;br /&gt;
&lt;br /&gt;
    instance = instance.replace('%s', '%04d'%slide)&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
&lt;br /&gt;
    bullDim = {}&lt;br /&gt;
    tabDim = {}&lt;br /&gt;
    for n in range(0,tabLevels):&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;bu_%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'width', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'bu_%d'%n, i))&lt;br /&gt;
            bullDim[n] = b&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;fr_tab%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'fr_tab%d'%n, i))&lt;br /&gt;
            tabDim[n] = b&lt;br /&gt;
&lt;br /&gt;
    delta = 0.&lt;br /&gt;
    firstVert = True&lt;br /&gt;
    for n, i in enumerate(hlist):&lt;br /&gt;
        if hlist[n]['type'] in ['TITLE']:&lt;br /&gt;
            continue  # already in right place, not a vertical element&lt;br /&gt;
&lt;br /&gt;
        hgt = 0&lt;br /&gt;
        if hlist[n-1]['type'] == 'BLANK':&lt;br /&gt;
            hgt = 30&lt;br /&gt;
        else:&lt;br /&gt;
            hgt = float(getDim(instance, hlist[n-1]['id'], 'height'))&lt;br /&gt;
&lt;br /&gt;
        hgt += 5&lt;br /&gt;
&lt;br /&gt;
        if firstVert:&lt;br /&gt;
            hgt = 0&lt;br /&gt;
            firstVert = False&lt;br /&gt;
&lt;br /&gt;
        delta += hgt&lt;br /&gt;
&lt;br /&gt;
        if hlist[n]['type'] == 'BLANK': continue&lt;br /&gt;
&lt;br /&gt;
        tabs = hlist[n]['tabs']&lt;br /&gt;
&lt;br /&gt;
        r = doc0.xpath('//s:flowRoot[@id=&amp;quot;%s&amp;quot;]' % hlist[n]['id'], NS)[0]&lt;br /&gt;
        clearId(r, what='transform')&lt;br /&gt;
        r = r.xpath('.//s:rect', NS)[0]&lt;br /&gt;
        r.set('x', str(tabDim[tabs]['x']))&lt;br /&gt;
        y = tabDim[tabs]['y'] + delta&lt;br /&gt;
        #print '%s -&amp;gt; %s' % (str(r.get('y')), str(y))&lt;br /&gt;
        #print tabDim[tabs]['y']&lt;br /&gt;
        r.set('y', str(y))&lt;br /&gt;
&lt;br /&gt;
        if (hlist[n]['type'] == 'TAB' and hlist[n]['tabs'] in bullDim&lt;br /&gt;
            and 'isDone' not in hlist[n]):&lt;br /&gt;
            hlist[n]['isDone'] = True&lt;br /&gt;
            dx = (tabDim[tabs]['x'] &lt;br /&gt;
                  - bullDim[tabs]['x']&lt;br /&gt;
                  - 1.5*bullDim[tabs]['width']&lt;br /&gt;
                  )&lt;br /&gt;
            dy = (y &lt;br /&gt;
                  + tabDim[tabs]['height'] / 2.&lt;br /&gt;
                  - bullDim[tabs]['y'] &lt;br /&gt;
                  - bullDim[tabs]['height'] / 2.&lt;br /&gt;
                  )&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            #print y, tabDim[tabs]['height'], bullDim[tabs]['y'], bullDim[tabs]['height'], dy&lt;br /&gt;
            clone = ET.Element('svg:use')&lt;br /&gt;
            clone.set('transform', 'translate(%f,%f)' % (dx,dy))&lt;br /&gt;
            clone.set('{%s}href'%NS['xlink'], '#bu_%d'%tabs)&lt;br /&gt;
            dst.append(clone)&lt;br /&gt;
            &lt;br /&gt;
            #path = ET.Element('svg:path')&lt;br /&gt;
            #path.set('d', ('M %f,%f' + ' L %f,%f'*5 + ' z') % (&lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y'], &lt;br /&gt;
            #    bullDim[tabs]['x']+20, bullDim[tabs]['y']+bullDim[tabs]['height']/2., &lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y']+bullDim[tabs]['height'], &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y'], &lt;br /&gt;
            #    tabDim[tabs]['x']+20, tabDim[tabs]['y']+tabDim[tabs]['height']/2., &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y']+tabDim[tabs]['height'], &lt;br /&gt;
            #    ))&lt;br /&gt;
            #dst.append(path)&lt;br /&gt;
            #break&lt;br /&gt;
&lt;br /&gt;
    # look for instance specific parts&lt;br /&gt;
    if slideId:&lt;br /&gt;
        f = instancePath(slideId)&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            comp = ET.parse(f)&lt;br /&gt;
            svg = doc0.xpath('//s:svg', NS)[0]&lt;br /&gt;
            g = &amp;quot;{%s}g&amp;quot;%NS['s']&lt;br /&gt;
            k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
            for n, i in enumerate(svg.getchildren()):&lt;br /&gt;
                if i.tag == g and i.get(k, '').startswith('Instance'):&lt;br /&gt;
                    x = comp.xpath('//s:svg//s:g[@i:label=&amp;quot;%s&amp;quot;]'%i.get(k), NS)&lt;br /&gt;
                    if x:&lt;br /&gt;
                        x = x[0]&lt;br /&gt;
                        cpy = clearId(copy.deepcopy(x))&lt;br /&gt;
                        svg[n] = cpy&lt;br /&gt;
            defs = comp.xpath('//s:svg/s:defs/*', NS)&lt;br /&gt;
            dst = doc0.xpath('//s:svg/s:defs', NS)[0]&lt;br /&gt;
            for d in defs:&lt;br /&gt;
                Id = d.get('id')&lt;br /&gt;
                current = doc0.xpath('//s:svg/s:defs/*[@id=&amp;quot;%s&amp;quot;]'%Id, NS)&lt;br /&gt;
                if not current:&lt;br /&gt;
                    cpy = copy.deepcopy(d)&lt;br /&gt;
                    dst.append(cpy)&lt;br /&gt;
&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
    &lt;br /&gt;
    return slideId&lt;br /&gt;
def instancePath(slideId, component = None):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return path to component for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # return os.path.join(slideId, component)&lt;br /&gt;
    return slideId+'.svg'&lt;br /&gt;
def writeComponents(inkscapeFile, slideId):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;write components from file for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not slideId: return&lt;br /&gt;
    doc0 = ET.parse(inkscapeFile)&lt;br /&gt;
    hasComponents = False&lt;br /&gt;
    for i in doc0.xpath('//s:svg//s:g', NS):&lt;br /&gt;
        k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
        # print i.keys(),k&lt;br /&gt;
        if (k in i.keys() and i.get(k).startswith('Instance')):&lt;br /&gt;
            if len(i) &amp;gt; 0:&lt;br /&gt;
                hasComponents = True&lt;br /&gt;
                break&lt;br /&gt;
                # ET.ElementTree(i).write(f)&lt;br /&gt;
                &lt;br /&gt;
    f = instancePath(slideId, 'components')&lt;br /&gt;
    &lt;br /&gt;
    if hasComponents:&lt;br /&gt;
        if (os.path.abspath(os.path.realpath(inkscapeFile)) != &lt;br /&gt;
            os.path.abspath(os.path.realpath(f))):&lt;br /&gt;
            file(f, 'w').write(file(inkscapeFile).read())&lt;br /&gt;
    else:&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            os.remove(f)&lt;br /&gt;
&lt;br /&gt;
if __name__ == '__main__':&lt;br /&gt;
    import sys&lt;br /&gt;
    instanceFromTemplate(sys.argv[1], 'slide%s.svg', file(sys.argv[2]).read())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16490</id>
		<title>InkSlide</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16490"/>
		<updated>2007-10-23T17:11:54Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: /* To do / status */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== InkSlide - quick and easy presentations using Inkscape ==&lt;br /&gt;
&lt;br /&gt;
InkSlide produces slides like this:&lt;br /&gt;
&lt;br /&gt;
[[Image:slide0004.jpg]]&lt;br /&gt;
&lt;br /&gt;
from simple text input like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%slide InkSlide: Features&lt;br /&gt;
Features include wrapped top level text and&lt;br /&gt;
    mulitple&lt;br /&gt;
        levels&lt;br /&gt;
            of wrapped bulleted lists with bullets and font&lt;br /&gt;
information taken from the template file.&lt;br /&gt;
&lt;br /&gt;
Slide specific content like this:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
which is updated when the template changes.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An Inkscape file is used as a template file to define the background, title position and font, fonts and positions for text at different levels of indentation, groups to be cloned and used as bullets, etc.&lt;br /&gt;
&lt;br /&gt;
Content specific to a particular slide can also be created in Inkscape, this content merged with the template and text input to make the final slide, so changes to the template after a particular slide is edited in Inkscape are included.&lt;br /&gt;
&lt;br /&gt;
=== Download ===&lt;br /&gt;
&lt;br /&gt;
Copy the [[InkSlide#Code|text below]] into a file called 'inkslide.py'.  (How do you attach a non-image file on this wiki?)&lt;br /&gt;
&lt;br /&gt;
Here is an [[Media:template0.svg|example template]].  Use Save Link As to stop your browser displaying the file instead of downloading it.&lt;br /&gt;
&lt;br /&gt;
=== Use ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python inkslide.py myTemplate.svg mySlides.txt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This creates a slide00xx.svg for each slide in your presentation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python inkslide.py --writeInstance &amp;lt;instanceId&amp;gt; myTemplate.svg mySlides.txt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Creates / updates the Inkscape file you can edit to add special content to this particular slide.   '''NOT IMPLEMENTED YET''' - the code exists, but not the command line hook.&lt;br /&gt;
&lt;br /&gt;
=== How it works ===&lt;br /&gt;
&lt;br /&gt;
First InkSlide parses the text input, something like the example at the top of this page.  It looks for flowed text boxes in the template file with special @id attributes like ''fr_title'' for the title, and ''fr_tab2'' for the second level of bulleted lists.  Copies of these elements are made and placed in a layer called 'Texts'.  At this point, the text is all piled up at the top of the page.&lt;br /&gt;
&lt;br /&gt;
InkSlide saves the file, and uses Inkscape's command line ''--query-height'' and friends to find out how high the wrapped text pieces are.  At it then repositions them properly, adds clones of the bullet markers, etc.  Bullet markers have special @id attributes like ''bu_1'' for the first level of bulleted list.&lt;br /&gt;
&lt;br /&gt;
Finally, InkSlide checks to see if there's an %id tag for the slide, and if so if there's a corresponding Inkscape SVG file with content for this particular slide. This content has to occur in layers whose name starts with ''Instance'', and these layers have to be present in the template file, but you can have more than one, so they can be above and below the regular content.  If such content is found, it's copied into the output slide.&lt;br /&gt;
&lt;br /&gt;
=== The Template File ===&lt;br /&gt;
&lt;br /&gt;
The template file is a regular Inkscape file.  If this list seems hard, just use the example template and modify it to suit your needs.&lt;br /&gt;
&lt;br /&gt;
Required contents:&lt;br /&gt;
&lt;br /&gt;
*A layer called ''Texts'', into which text will be placed.&lt;br /&gt;
*A flowed text frame with an @id of ''fr_title'' to define the font and position for the slide title.  This should contain text like ''Title goes here Sly'' because ''Sly'' includes letters which ascend and descend from the baseline to the greatest extent.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*A group with an @id of ''gr_title'' into which the title will be placed.  This makes it easier to do special effects on the title like shadows.&lt;br /&gt;
*Flowed text frames with an @id of ''fr_tabN'' for each N in 0, 1, 2, 3.  ''fr_tab0'' is used for top level non bulleted text.  These should also include ''Sly'' or similar.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*Objects or groups withan @id of ''bu_N'' for each N in 1, 2, 3, for the three levels of bulleted list.&lt;br /&gt;
&lt;br /&gt;
The fr_* elements define the font, font-size, color etc. for the different levels of text.  They also define the position and wrap width.  See the example template file for a possible arrangement.&lt;br /&gt;
&lt;br /&gt;
Optional contents:&lt;br /&gt;
&lt;br /&gt;
*A background&lt;br /&gt;
*Affiliation text and logos you want to appear on every slide&lt;br /&gt;
*A blurred clone of the gr_title element, offset and behind it.&lt;br /&gt;
*Layers whose name starts with ''Instance'' - these are the layers the user can edit directly in Inkscape for a particular slide.&lt;br /&gt;
&lt;br /&gt;
When you save the template file you should make sure that layers containing place holder text (the fr_* elements) and bullets are invisible, or at least hidden below the background layer, which should be visible.  Likewise the Texts layer should be visible, as well as any Instance layers.&lt;br /&gt;
&lt;br /&gt;
=== Text Input ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%title Inkslide: an example&lt;br /&gt;
This text will appear as a top level paragraph.&lt;br /&gt;
    Indented by a single tab, this text will appear as a bulleted item.&lt;br /&gt;
        Two tabs -&amp;gt; second level bulleted list.&lt;br /&gt;
&lt;br /&gt;
A gap will be left for the preceeding line.&lt;br /&gt;
%pause&lt;br /&gt;
Two slides will be produced for this input, one with everything up to the %pause above,&lt;br /&gt;
and one with that plus the remaining content.&lt;br /&gt;
Incremental list display can be done this way.&lt;br /&gt;
&lt;br /&gt;
The %id tag below indicates that a file with a name like 'extra/is_intro.svg' should&lt;br /&gt;
be inspected for extra content for this particular slide.&lt;br /&gt;
%id is_intro&lt;br /&gt;
%title Inkslide: the next slide...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== The GUI ===&lt;br /&gt;
&lt;br /&gt;
A GUI for InkSlide is available as a plug-in for the [http://webpages.charter.net/edreamleo/front.html Leo] outline editor.  This makes slide re-arranging easy, and adds some macro substitution capability.  It also adds buttons for editing slide specific content in Inkscape.&lt;br /&gt;
&lt;br /&gt;
FIXME: post plug-in on Leo wiki and link.&lt;br /&gt;
&lt;br /&gt;
=== To do / status ===&lt;br /&gt;
&lt;br /&gt;
InkSlide is very usable, but is missing some features.  Remember you can &lt;br /&gt;
use the slide specific editing feature to do any of these things and&lt;br /&gt;
anything else you can do in Inkscape already, these will just be more&lt;br /&gt;
convenient in the text input when they're done.&lt;br /&gt;
&lt;br /&gt;
*Inline text formatting (bold, color, etc.) for part of a piece of text&lt;br /&gt;
*Justification switching&lt;br /&gt;
*Verbatim code display&lt;br /&gt;
*&amp;lt;strike&amp;gt;Simple image inclusion&amp;lt;/strike&amp;gt;&lt;br /&gt;
*more helper tools to make PNG and PDF versions of slides&lt;br /&gt;
*configuring / using a directory for edited slides&lt;br /&gt;
*command line flags for generating specific slides&lt;br /&gt;
&lt;br /&gt;
Currently InkSlide is slow, because it must invoke Inkscape once for every single dimension it needs to retrieve.  I'll try and get a --query-all switch into 0.46.&lt;br /&gt;
&lt;br /&gt;
=== Bugs ===&lt;br /&gt;
&lt;br /&gt;
*There may be an issue with the way slide specific content is merged into the template producing conflicting @id attributes, but I haven't seen this happen yet.&lt;br /&gt;
&lt;br /&gt;
=== Author  / Credits ===&lt;br /&gt;
&lt;br /&gt;
InkSlide was written by Terry Brown, terry-n-brown@yahoo.com - but use underscore, not '-'.&lt;br /&gt;
&lt;br /&gt;
InkSlide was inspired by [http://member.wide.ad.jp/wg/mgp/ MagicPoint]&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# inkslide.py $Id$&lt;br /&gt;
# Author: Terry Brown&lt;br /&gt;
# Created: Thu Oct 11 2007&lt;br /&gt;
&lt;br /&gt;
import lxml.etree as ET&lt;br /&gt;
&lt;br /&gt;
import copy&lt;br /&gt;
import subprocess&lt;br /&gt;
import os.path&lt;br /&gt;
&lt;br /&gt;
NS = { 'i': 'http://www.inkscape.org/namespaces/inkscape',&lt;br /&gt;
       's': 'http://www.w3.org/2000/svg',&lt;br /&gt;
       'xlink' : 'http://www.w3.org/1999/xlink'}&lt;br /&gt;
&lt;br /&gt;
# number of defined tablevels, FIXME, could derive from template?&lt;br /&gt;
tabLevels = 4  &lt;br /&gt;
def clearId(x, what='id', NS={}):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;recursively clear @id on element x and descendants&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if what in x.keys():&lt;br /&gt;
        del x.attrib[what]&lt;br /&gt;
    for i in x.xpath('.//*[@%s]'%what, NS):&lt;br /&gt;
        del i.attrib[what]&lt;br /&gt;
    return x&lt;br /&gt;
def getDim(fn, Id, what):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return dimension of element in fn with @id Id, what is&lt;br /&gt;
    x, y, width, height&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cmd = ('inkscape', '--without-gui', '--query-id', Id, '--query-'+what, fn)&lt;br /&gt;
    proc = subprocess.Popen(cmd, stdout = subprocess.PIPE,&lt;br /&gt;
                            stderr = subprocess.PIPE)&lt;br /&gt;
    # make new pipe for stderr to supress chatter from inkscape&lt;br /&gt;
    proc.wait()&lt;br /&gt;
    return proc.stdout.read()&lt;br /&gt;
def textReader(text):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;iterate parts in input text&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not text: return&lt;br /&gt;
    for line in text.split('\n'):&lt;br /&gt;
        if line.startswith('%slide '):&lt;br /&gt;
            yield {'type':'TITLE', 's':line[7:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%pause'):&lt;br /&gt;
            yield {'type':'PAUSE'}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%id '):&lt;br /&gt;
            yield {'type':'ID', 's':line[4:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if not line.strip():&lt;br /&gt;
            yield {'type':'BLANK'}&lt;br /&gt;
            continue&lt;br /&gt;
        tabs = len(line) - len(line.lstrip('\t'))&lt;br /&gt;
        yield {'type':'TAB', 'tabs':tabs, 's':line.strip()}&lt;br /&gt;
&lt;br /&gt;
nid = 0&lt;br /&gt;
def nextId():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return an unsed Id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # FIXME, should inspect templat doc.&lt;br /&gt;
    global nid&lt;br /&gt;
    nid += 1&lt;br /&gt;
    return 'is'+str(nid)&lt;br /&gt;
&lt;br /&gt;
def instanceFromTemplate(template, instance, text, pauseOk = True):&lt;br /&gt;
&lt;br /&gt;
    slide = 0&lt;br /&gt;
&lt;br /&gt;
    hlist = []&lt;br /&gt;
    doc0 = ET.parse(template)&lt;br /&gt;
    slideId = None&lt;br /&gt;
    &lt;br /&gt;
    for t in textReader(text):&lt;br /&gt;
        &lt;br /&gt;
        if t['type'] == 'ID':&lt;br /&gt;
            slideId = t['s']&lt;br /&gt;
            continue&lt;br /&gt;
            &lt;br /&gt;
        if t['type'] == 'TITLE':&lt;br /&gt;
            print t['s']&lt;br /&gt;
            if hlist:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
                hlist = [t]&lt;br /&gt;
                doc0 = ET.parse(template)&lt;br /&gt;
                slideId = None&lt;br /&gt;
            else:&lt;br /&gt;
                hlist.append(t)&lt;br /&gt;
            dst = doc0.xpath('//s:g[@id=&amp;quot;gr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'PAUSE':&lt;br /&gt;
            if pauseOk:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        hlist.append(t)&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'BLANK':&lt;br /&gt;
            continue&lt;br /&gt;
        if t['type'] == 'TAB':&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_tab%d&amp;quot;]' % t['tabs'], NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            #break&lt;br /&gt;
            continue&lt;br /&gt;
           &lt;br /&gt;
    process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
def process(instance, doc0, hlist, slideId, slide):&lt;br /&gt;
&lt;br /&gt;
    instance = instance.replace('%s', '%04d'%slide)&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
&lt;br /&gt;
    bullDim = {}&lt;br /&gt;
    tabDim = {}&lt;br /&gt;
    for n in range(0,tabLevels):&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;bu_%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'width', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'bu_%d'%n, i))&lt;br /&gt;
            bullDim[n] = b&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;fr_tab%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'fr_tab%d'%n, i))&lt;br /&gt;
            tabDim[n] = b&lt;br /&gt;
&lt;br /&gt;
    delta = 0.&lt;br /&gt;
    firstVert = True&lt;br /&gt;
    for n, i in enumerate(hlist):&lt;br /&gt;
        if hlist[n]['type'] in ['TITLE']:&lt;br /&gt;
            continue  # already in right place, not a vertical element&lt;br /&gt;
&lt;br /&gt;
        hgt = 0&lt;br /&gt;
        if hlist[n-1]['type'] == 'BLANK':&lt;br /&gt;
            hgt = 30&lt;br /&gt;
        else:&lt;br /&gt;
            hgt = float(getDim(instance, hlist[n-1]['id'], 'height'))&lt;br /&gt;
&lt;br /&gt;
        hgt += 5&lt;br /&gt;
&lt;br /&gt;
        if firstVert:&lt;br /&gt;
            hgt = 0&lt;br /&gt;
            firstVert = False&lt;br /&gt;
&lt;br /&gt;
        delta += hgt&lt;br /&gt;
&lt;br /&gt;
        if hlist[n]['type'] == 'BLANK': continue&lt;br /&gt;
&lt;br /&gt;
        tabs = hlist[n]['tabs']&lt;br /&gt;
&lt;br /&gt;
        r = doc0.xpath('//s:flowRoot[@id=&amp;quot;%s&amp;quot;]' % hlist[n]['id'], NS)[0]&lt;br /&gt;
        clearId(r, what='transform')&lt;br /&gt;
        r = r.xpath('.//s:rect', NS)[0]&lt;br /&gt;
        r.set('x', str(tabDim[tabs]['x']))&lt;br /&gt;
        y = tabDim[tabs]['y'] + delta&lt;br /&gt;
        #print '%s -&amp;gt; %s' % (str(r.get('y')), str(y))&lt;br /&gt;
        #print tabDim[tabs]['y']&lt;br /&gt;
        r.set('y', str(y))&lt;br /&gt;
&lt;br /&gt;
        if (hlist[n]['type'] == 'TAB' and hlist[n]['tabs'] in bullDim&lt;br /&gt;
            and 'isDone' not in hlist[n]):&lt;br /&gt;
            hlist[n]['isDone'] = True&lt;br /&gt;
            dx = (tabDim[tabs]['x'] &lt;br /&gt;
                  - bullDim[tabs]['x']&lt;br /&gt;
                  - 1.5*bullDim[tabs]['width']&lt;br /&gt;
                  )&lt;br /&gt;
            dy = (y &lt;br /&gt;
                  + tabDim[tabs]['height'] / 2.&lt;br /&gt;
                  - bullDim[tabs]['y'] &lt;br /&gt;
                  - bullDim[tabs]['height'] / 2.&lt;br /&gt;
                  )&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            #print y, tabDim[tabs]['height'], bullDim[tabs]['y'], bullDim[tabs]['height'], dy&lt;br /&gt;
            clone = ET.Element('svg:use')&lt;br /&gt;
            clone.set('transform', 'translate(%f,%f)' % (dx,dy))&lt;br /&gt;
            clone.set('{%s}href'%NS['xlink'], '#bu_%d'%tabs)&lt;br /&gt;
            dst.append(clone)&lt;br /&gt;
            &lt;br /&gt;
            #path = ET.Element('svg:path')&lt;br /&gt;
            #path.set('d', ('M %f,%f' + ' L %f,%f'*5 + ' z') % (&lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y'], &lt;br /&gt;
            #    bullDim[tabs]['x']+20, bullDim[tabs]['y']+bullDim[tabs]['height']/2., &lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y']+bullDim[tabs]['height'], &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y'], &lt;br /&gt;
            #    tabDim[tabs]['x']+20, tabDim[tabs]['y']+tabDim[tabs]['height']/2., &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y']+tabDim[tabs]['height'], &lt;br /&gt;
            #    ))&lt;br /&gt;
            #dst.append(path)&lt;br /&gt;
            #break&lt;br /&gt;
&lt;br /&gt;
    # look for instance specific parts&lt;br /&gt;
    if slideId:&lt;br /&gt;
        f = instancePath(slideId)&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            comp = ET.parse(f)&lt;br /&gt;
            svg = doc0.xpath('//s:svg', NS)[0]&lt;br /&gt;
            g = &amp;quot;{%s}g&amp;quot;%NS['s']&lt;br /&gt;
            k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
            for n, i in enumerate(svg.getchildren()):&lt;br /&gt;
                if i.tag == g and i.get(k, '').startswith('Instance'):&lt;br /&gt;
                    x = comp.xpath('//s:svg//s:g[@i:label=&amp;quot;%s&amp;quot;]'%i.get(k), NS)&lt;br /&gt;
                    if x:&lt;br /&gt;
                        x = x[0]&lt;br /&gt;
                        cpy = clearId(copy.deepcopy(x))&lt;br /&gt;
                        svg[n] = cpy&lt;br /&gt;
            defs = comp.xpath('//s:svg/s:defs/*', NS)&lt;br /&gt;
            dst = doc0.xpath('//s:svg/s:defs', NS)[0]&lt;br /&gt;
            for d in defs:&lt;br /&gt;
                Id = d.get('id')&lt;br /&gt;
                current = doc0.xpath('//s:svg/s:defs/*[@id=&amp;quot;%s&amp;quot;]'%Id, NS)&lt;br /&gt;
                if not current:&lt;br /&gt;
                    cpy = copy.deepcopy(d)&lt;br /&gt;
                    dst.append(cpy)&lt;br /&gt;
&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
    &lt;br /&gt;
    return slideId&lt;br /&gt;
def instancePath(slideId, component = None):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return path to component for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # return os.path.join(slideId, component)&lt;br /&gt;
    return slideId+'.svg'&lt;br /&gt;
def writeComponents(inkscapeFile, slideId):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;write components from file for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not slideId: return&lt;br /&gt;
    doc0 = ET.parse(inkscapeFile)&lt;br /&gt;
    hasComponents = False&lt;br /&gt;
    for i in doc0.xpath('//s:svg//s:g', NS):&lt;br /&gt;
        k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
        # print i.keys(),k&lt;br /&gt;
        if (k in i.keys() and i.get(k).startswith('Instance')):&lt;br /&gt;
            if len(i) &amp;gt; 0:&lt;br /&gt;
                hasComponents = True&lt;br /&gt;
                break&lt;br /&gt;
                # ET.ElementTree(i).write(f)&lt;br /&gt;
                &lt;br /&gt;
    f = instancePath(slideId, 'components')&lt;br /&gt;
    &lt;br /&gt;
    if hasComponents:&lt;br /&gt;
        if (os.path.abspath(os.path.realpath(inkscapeFile)) != &lt;br /&gt;
            os.path.abspath(os.path.realpath(f))):&lt;br /&gt;
            file(f, 'w').write(file(inkscapeFile).read())&lt;br /&gt;
    else:&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            os.remove(f)&lt;br /&gt;
&lt;br /&gt;
if __name__ == '__main__':&lt;br /&gt;
    import sys&lt;br /&gt;
    instanceFromTemplate(sys.argv[1], 'slide%s.svg', file(sys.argv[2]).read())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16471</id>
		<title>InkSlide</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16471"/>
		<updated>2007-10-17T19:52:48Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: /* To do / status */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== InkSlide - quick and easy presentations using Inkscape ==&lt;br /&gt;
&lt;br /&gt;
InkSlide produces slides like this:&lt;br /&gt;
&lt;br /&gt;
[[Image:slide0004.jpg]]&lt;br /&gt;
&lt;br /&gt;
from simple text input like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%slide InkSlide: Features&lt;br /&gt;
Features include wrapped top level text and&lt;br /&gt;
    mulitple&lt;br /&gt;
        levels&lt;br /&gt;
            of wrapped bulleted lists with bullets and font&lt;br /&gt;
information taken from the template file.&lt;br /&gt;
&lt;br /&gt;
Slide specific content like this:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
which is updated when the template changes.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An Inkscape file is used as a template file to define the background, title position and font, fonts and positions for text at different levels of indentation, groups to be cloned and used as bullets, etc.&lt;br /&gt;
&lt;br /&gt;
Content specific to a particular slide can also be created in Inkscape, this content merged with the template and text input to make the final slide, so changes to the template after a particular slide is edited in Inkscape are included.&lt;br /&gt;
&lt;br /&gt;
=== Download ===&lt;br /&gt;
&lt;br /&gt;
Copy the [[InkSlide#Code|text below]] into a file called 'inkslide.py'.  (How do you attach a non-image file on this wiki?)&lt;br /&gt;
&lt;br /&gt;
Here is an [[Media:template0.svg|example template]].  Use Save Link As to stop your browser displaying the file instead of downloading it.&lt;br /&gt;
&lt;br /&gt;
=== Use ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python inkslide.py myTemplate.svg mySlides.txt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This creates a slide00xx.svg for each slide in your presentation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python inkslide.py --writeInstance &amp;lt;instanceId&amp;gt; myTemplate.svg mySlides.txt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Creates / updates the Inkscape file you can edit to add special content to this particular slide.   '''NOT IMPLEMENTED YET''' - the code exists, but not the command line hook.&lt;br /&gt;
&lt;br /&gt;
=== How it works ===&lt;br /&gt;
&lt;br /&gt;
First InkSlide parses the text input, something like the example at the top of this page.  It looks for flowed text boxes in the template file with special @id attributes like ''fr_title'' for the title, and ''fr_tab2'' for the second level of bulleted lists.  Copies of these elements are made and placed in a layer called 'Texts'.  At this point, the text is all piled up at the top of the page.&lt;br /&gt;
&lt;br /&gt;
InkSlide saves the file, and uses Inkscape's command line ''--query-height'' and friends to find out how high the wrapped text pieces are.  At it then repositions them properly, adds clones of the bullet markers, etc.  Bullet markers have special @id attributes like ''bu_1'' for the first level of bulleted list.&lt;br /&gt;
&lt;br /&gt;
Finally, InkSlide checks to see if there's an %id tag for the slide, and if so if there's a corresponding Inkscape SVG file with content for this particular slide. This content has to occur in layers whose name starts with ''Instance'', and these layers have to be present in the template file, but you can have more than one, so they can be above and below the regular content.  If such content is found, it's copied into the output slide.&lt;br /&gt;
&lt;br /&gt;
=== The Template File ===&lt;br /&gt;
&lt;br /&gt;
The template file is a regular Inkscape file.  If this list seems hard, just use the example template and modify it to suit your needs.&lt;br /&gt;
&lt;br /&gt;
Required contents:&lt;br /&gt;
&lt;br /&gt;
*A layer called ''Texts'', into which text will be placed.&lt;br /&gt;
*A flowed text frame with an @id of ''fr_title'' to define the font and position for the slide title.  This should contain text like ''Title goes here Sly'' because ''Sly'' includes letters which ascend and descend from the baseline to the greatest extent.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*A group with an @id of ''gr_title'' into which the title will be placed.  This makes it easier to do special effects on the title like shadows.&lt;br /&gt;
*Flowed text frames with an @id of ''fr_tabN'' for each N in 0, 1, 2, 3.  ''fr_tab0'' is used for top level non bulleted text.  These should also include ''Sly'' or similar.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*Objects or groups withan @id of ''bu_N'' for each N in 1, 2, 3, for the three levels of bulleted list.&lt;br /&gt;
&lt;br /&gt;
The fr_* elements define the font, font-size, color etc. for the different levels of text.  They also define the position and wrap width.  See the example template file for a possible arrangement.&lt;br /&gt;
&lt;br /&gt;
Optional contents:&lt;br /&gt;
&lt;br /&gt;
*A background&lt;br /&gt;
*Affiliation text and logos you want to appear on every slide&lt;br /&gt;
*A blurred clone of the gr_title element, offset and behind it.&lt;br /&gt;
*Layers whose name starts with ''Instance'' - these are the layers the user can edit directly in Inkscape for a particular slide.&lt;br /&gt;
&lt;br /&gt;
When you save the template file you should make sure that layers containing place holder text (the fr_* elements) and bullets are invisible, or at least hidden below the background layer, which should be visible.  Likewise the Texts layer should be visible, as well as any Instance layers.&lt;br /&gt;
&lt;br /&gt;
=== Text Input ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%title Inkslide: an example&lt;br /&gt;
This text will appear as a top level paragraph.&lt;br /&gt;
    Indented by a single tab, this text will appear as a bulleted item.&lt;br /&gt;
        Two tabs -&amp;gt; second level bulleted list.&lt;br /&gt;
&lt;br /&gt;
A gap will be left for the preceeding line.&lt;br /&gt;
%pause&lt;br /&gt;
Two slides will be produced for this input, one with everything up to the %pause above,&lt;br /&gt;
and one with that plus the remaining content.&lt;br /&gt;
Incremental list display can be done this way.&lt;br /&gt;
&lt;br /&gt;
The %id tag below indicates that a file with a name like 'extra/is_intro.svg' should&lt;br /&gt;
be inspected for extra content for this particular slide.&lt;br /&gt;
%id is_intro&lt;br /&gt;
%title Inkslide: the next slide...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== The GUI ===&lt;br /&gt;
&lt;br /&gt;
A GUI for InkSlide is available as a plug-in for the [http://webpages.charter.net/edreamleo/front.html Leo] outline editor.  This makes slide re-arranging easy, and adds some macro substitution capability.  It also adds buttons for editing slide specific content in Inkscape.&lt;br /&gt;
&lt;br /&gt;
FIXME: post plug-in on Leo wiki and link.&lt;br /&gt;
&lt;br /&gt;
=== To do / status ===&lt;br /&gt;
&lt;br /&gt;
InkSlide is very usable, but is missing some features.  Remember you can &lt;br /&gt;
use the slide specific editing feature to do any of these things and&lt;br /&gt;
anything else you can do in Inkscape already, these will just be more&lt;br /&gt;
convenient in the text input when they're done.&lt;br /&gt;
&lt;br /&gt;
*Inline text formatting (bold, color, etc.) for part of a piece of text&lt;br /&gt;
*Justification switching&lt;br /&gt;
*Verbatim code display&lt;br /&gt;
*Simple image inclusion&lt;br /&gt;
**currently you can do this using the Inkscape editing features&lt;br /&gt;
*more helper tool to make PNG and PDF versions of slides&lt;br /&gt;
*configuring / using a directory for edited slides&lt;br /&gt;
*command line flags for generating specific slides&lt;br /&gt;
&lt;br /&gt;
Currently InkSlide is slow, because it must invoke Inkscape once for every single dimension it needs to retrieve.  I'll try and get a --query-all switch into 0.46.&lt;br /&gt;
&lt;br /&gt;
=== Bugs ===&lt;br /&gt;
&lt;br /&gt;
*There may be an issue with the way slide specific content is merged into the template producing conflicting @id attributes, but I haven't seen this happen yet.&lt;br /&gt;
&lt;br /&gt;
=== Author  / Credits ===&lt;br /&gt;
&lt;br /&gt;
InkSlide was written by Terry Brown, terry-n-brown@yahoo.com - but use underscore, not '-'.&lt;br /&gt;
&lt;br /&gt;
InkSlide was inspired by [http://member.wide.ad.jp/wg/mgp/ MagicPoint]&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# inkslide.py $Id$&lt;br /&gt;
# Author: Terry Brown&lt;br /&gt;
# Created: Thu Oct 11 2007&lt;br /&gt;
&lt;br /&gt;
import lxml.etree as ET&lt;br /&gt;
&lt;br /&gt;
import copy&lt;br /&gt;
import subprocess&lt;br /&gt;
import os.path&lt;br /&gt;
&lt;br /&gt;
NS = { 'i': 'http://www.inkscape.org/namespaces/inkscape',&lt;br /&gt;
       's': 'http://www.w3.org/2000/svg',&lt;br /&gt;
       'xlink' : 'http://www.w3.org/1999/xlink'}&lt;br /&gt;
&lt;br /&gt;
# number of defined tablevels, FIXME, could derive from template?&lt;br /&gt;
tabLevels = 4  &lt;br /&gt;
def clearId(x, what='id', NS={}):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;recursively clear @id on element x and descendants&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if what in x.keys():&lt;br /&gt;
        del x.attrib[what]&lt;br /&gt;
    for i in x.xpath('.//*[@%s]'%what, NS):&lt;br /&gt;
        del i.attrib[what]&lt;br /&gt;
    return x&lt;br /&gt;
def getDim(fn, Id, what):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return dimension of element in fn with @id Id, what is&lt;br /&gt;
    x, y, width, height&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cmd = ('inkscape', '--without-gui', '--query-id', Id, '--query-'+what, fn)&lt;br /&gt;
    proc = subprocess.Popen(cmd, stdout = subprocess.PIPE,&lt;br /&gt;
                            stderr = subprocess.PIPE)&lt;br /&gt;
    # make new pipe for stderr to supress chatter from inkscape&lt;br /&gt;
    proc.wait()&lt;br /&gt;
    return proc.stdout.read()&lt;br /&gt;
def textReader(text):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;iterate parts in input text&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not text: return&lt;br /&gt;
    for line in text.split('\n'):&lt;br /&gt;
        if line.startswith('%slide '):&lt;br /&gt;
            yield {'type':'TITLE', 's':line[7:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%pause'):&lt;br /&gt;
            yield {'type':'PAUSE'}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%id '):&lt;br /&gt;
            yield {'type':'ID', 's':line[4:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if not line.strip():&lt;br /&gt;
            yield {'type':'BLANK'}&lt;br /&gt;
            continue&lt;br /&gt;
        tabs = len(line) - len(line.lstrip('\t'))&lt;br /&gt;
        yield {'type':'TAB', 'tabs':tabs, 's':line.strip()}&lt;br /&gt;
&lt;br /&gt;
nid = 0&lt;br /&gt;
def nextId():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return an unsed Id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # FIXME, should inspect templat doc.&lt;br /&gt;
    global nid&lt;br /&gt;
    nid += 1&lt;br /&gt;
    return 'is'+str(nid)&lt;br /&gt;
&lt;br /&gt;
def instanceFromTemplate(template, instance, text, pauseOk = True):&lt;br /&gt;
&lt;br /&gt;
    slide = 0&lt;br /&gt;
&lt;br /&gt;
    hlist = []&lt;br /&gt;
    doc0 = ET.parse(template)&lt;br /&gt;
    slideId = None&lt;br /&gt;
    &lt;br /&gt;
    for t in textReader(text):&lt;br /&gt;
        &lt;br /&gt;
        if t['type'] == 'ID':&lt;br /&gt;
            slideId = t['s']&lt;br /&gt;
            continue&lt;br /&gt;
            &lt;br /&gt;
        if t['type'] == 'TITLE':&lt;br /&gt;
            print t['s']&lt;br /&gt;
            if hlist:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
                hlist = [t]&lt;br /&gt;
                doc0 = ET.parse(template)&lt;br /&gt;
                slideId = None&lt;br /&gt;
            else:&lt;br /&gt;
                hlist.append(t)&lt;br /&gt;
            dst = doc0.xpath('//s:g[@id=&amp;quot;gr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'PAUSE':&lt;br /&gt;
            if pauseOk:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        hlist.append(t)&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'BLANK':&lt;br /&gt;
            continue&lt;br /&gt;
        if t['type'] == 'TAB':&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_tab%d&amp;quot;]' % t['tabs'], NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            #break&lt;br /&gt;
            continue&lt;br /&gt;
           &lt;br /&gt;
    process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
def process(instance, doc0, hlist, slideId, slide):&lt;br /&gt;
&lt;br /&gt;
    instance = instance.replace('%s', '%04d'%slide)&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
&lt;br /&gt;
    bullDim = {}&lt;br /&gt;
    tabDim = {}&lt;br /&gt;
    for n in range(0,tabLevels):&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;bu_%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'width', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'bu_%d'%n, i))&lt;br /&gt;
            bullDim[n] = b&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;fr_tab%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'fr_tab%d'%n, i))&lt;br /&gt;
            tabDim[n] = b&lt;br /&gt;
&lt;br /&gt;
    delta = 0.&lt;br /&gt;
    firstVert = True&lt;br /&gt;
    for n, i in enumerate(hlist):&lt;br /&gt;
        if hlist[n]['type'] in ['TITLE']:&lt;br /&gt;
            continue  # already in right place, not a vertical element&lt;br /&gt;
&lt;br /&gt;
        hgt = 0&lt;br /&gt;
        if hlist[n-1]['type'] == 'BLANK':&lt;br /&gt;
            hgt = 30&lt;br /&gt;
        else:&lt;br /&gt;
            hgt = float(getDim(instance, hlist[n-1]['id'], 'height'))&lt;br /&gt;
&lt;br /&gt;
        hgt += 5&lt;br /&gt;
&lt;br /&gt;
        if firstVert:&lt;br /&gt;
            hgt = 0&lt;br /&gt;
            firstVert = False&lt;br /&gt;
&lt;br /&gt;
        delta += hgt&lt;br /&gt;
&lt;br /&gt;
        if hlist[n]['type'] == 'BLANK': continue&lt;br /&gt;
&lt;br /&gt;
        tabs = hlist[n]['tabs']&lt;br /&gt;
&lt;br /&gt;
        r = doc0.xpath('//s:flowRoot[@id=&amp;quot;%s&amp;quot;]' % hlist[n]['id'], NS)[0]&lt;br /&gt;
        clearId(r, what='transform')&lt;br /&gt;
        r = r.xpath('.//s:rect', NS)[0]&lt;br /&gt;
        r.set('x', str(tabDim[tabs]['x']))&lt;br /&gt;
        y = tabDim[tabs]['y'] + delta&lt;br /&gt;
        #print '%s -&amp;gt; %s' % (str(r.get('y')), str(y))&lt;br /&gt;
        #print tabDim[tabs]['y']&lt;br /&gt;
        r.set('y', str(y))&lt;br /&gt;
&lt;br /&gt;
        if (hlist[n]['type'] == 'TAB' and hlist[n]['tabs'] in bullDim&lt;br /&gt;
            and 'isDone' not in hlist[n]):&lt;br /&gt;
            hlist[n]['isDone'] = True&lt;br /&gt;
            dx = (tabDim[tabs]['x'] &lt;br /&gt;
                  - bullDim[tabs]['x']&lt;br /&gt;
                  - 1.5*bullDim[tabs]['width']&lt;br /&gt;
                  )&lt;br /&gt;
            dy = (y &lt;br /&gt;
                  + tabDim[tabs]['height'] / 2.&lt;br /&gt;
                  - bullDim[tabs]['y'] &lt;br /&gt;
                  - bullDim[tabs]['height'] / 2.&lt;br /&gt;
                  )&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            #print y, tabDim[tabs]['height'], bullDim[tabs]['y'], bullDim[tabs]['height'], dy&lt;br /&gt;
            clone = ET.Element('svg:use')&lt;br /&gt;
            clone.set('transform', 'translate(%f,%f)' % (dx,dy))&lt;br /&gt;
            clone.set('{%s}href'%NS['xlink'], '#bu_%d'%tabs)&lt;br /&gt;
            dst.append(clone)&lt;br /&gt;
            &lt;br /&gt;
            #path = ET.Element('svg:path')&lt;br /&gt;
            #path.set('d', ('M %f,%f' + ' L %f,%f'*5 + ' z') % (&lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y'], &lt;br /&gt;
            #    bullDim[tabs]['x']+20, bullDim[tabs]['y']+bullDim[tabs]['height']/2., &lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y']+bullDim[tabs]['height'], &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y'], &lt;br /&gt;
            #    tabDim[tabs]['x']+20, tabDim[tabs]['y']+tabDim[tabs]['height']/2., &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y']+tabDim[tabs]['height'], &lt;br /&gt;
            #    ))&lt;br /&gt;
            #dst.append(path)&lt;br /&gt;
            #break&lt;br /&gt;
&lt;br /&gt;
    # look for instance specific parts&lt;br /&gt;
    if slideId:&lt;br /&gt;
        f = instancePath(slideId)&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            comp = ET.parse(f)&lt;br /&gt;
            svg = doc0.xpath('//s:svg', NS)[0]&lt;br /&gt;
            g = &amp;quot;{%s}g&amp;quot;%NS['s']&lt;br /&gt;
            k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
            for n, i in enumerate(svg.getchildren()):&lt;br /&gt;
                if i.tag == g and i.get(k, '').startswith('Instance'):&lt;br /&gt;
                    x = comp.xpath('//s:svg//s:g[@i:label=&amp;quot;%s&amp;quot;]'%i.get(k), NS)&lt;br /&gt;
                    if x:&lt;br /&gt;
                        x = x[0]&lt;br /&gt;
                        cpy = clearId(copy.deepcopy(x))&lt;br /&gt;
                        svg[n] = cpy&lt;br /&gt;
            defs = comp.xpath('//s:svg/s:defs/*', NS)&lt;br /&gt;
            dst = doc0.xpath('//s:svg/s:defs', NS)[0]&lt;br /&gt;
            for d in defs:&lt;br /&gt;
                Id = d.get('id')&lt;br /&gt;
                current = doc0.xpath('//s:svg/s:defs/*[@id=&amp;quot;%s&amp;quot;]'%Id, NS)&lt;br /&gt;
                if not current:&lt;br /&gt;
                    cpy = copy.deepcopy(d)&lt;br /&gt;
                    dst.append(cpy)&lt;br /&gt;
&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
    &lt;br /&gt;
    return slideId&lt;br /&gt;
def instancePath(slideId, component = None):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return path to component for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # return os.path.join(slideId, component)&lt;br /&gt;
    return slideId+'.svg'&lt;br /&gt;
def writeComponents(inkscapeFile, slideId):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;write components from file for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not slideId: return&lt;br /&gt;
    doc0 = ET.parse(inkscapeFile)&lt;br /&gt;
    hasComponents = False&lt;br /&gt;
    for i in doc0.xpath('//s:svg//s:g', NS):&lt;br /&gt;
        k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
        # print i.keys(),k&lt;br /&gt;
        if (k in i.keys() and i.get(k).startswith('Instance')):&lt;br /&gt;
            if len(i) &amp;gt; 0:&lt;br /&gt;
                hasComponents = True&lt;br /&gt;
                break&lt;br /&gt;
                # ET.ElementTree(i).write(f)&lt;br /&gt;
                &lt;br /&gt;
    f = instancePath(slideId, 'components')&lt;br /&gt;
    &lt;br /&gt;
    if hasComponents:&lt;br /&gt;
        if (os.path.abspath(os.path.realpath(inkscapeFile)) != &lt;br /&gt;
            os.path.abspath(os.path.realpath(f))):&lt;br /&gt;
            file(f, 'w').write(file(inkscapeFile).read())&lt;br /&gt;
    else:&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            os.remove(f)&lt;br /&gt;
&lt;br /&gt;
if __name__ == '__main__':&lt;br /&gt;
    import sys&lt;br /&gt;
    instanceFromTemplate(sys.argv[1], 'slide%s.svg', file(sys.argv[2]).read())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16470</id>
		<title>InkSlide</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16470"/>
		<updated>2007-10-17T19:50:27Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: /* Text Input */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== InkSlide - quick and easy presentations using Inkscape ==&lt;br /&gt;
&lt;br /&gt;
InkSlide produces slides like this:&lt;br /&gt;
&lt;br /&gt;
[[Image:slide0004.jpg]]&lt;br /&gt;
&lt;br /&gt;
from simple text input like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%slide InkSlide: Features&lt;br /&gt;
Features include wrapped top level text and&lt;br /&gt;
    mulitple&lt;br /&gt;
        levels&lt;br /&gt;
            of wrapped bulleted lists with bullets and font&lt;br /&gt;
information taken from the template file.&lt;br /&gt;
&lt;br /&gt;
Slide specific content like this:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
which is updated when the template changes.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An Inkscape file is used as a template file to define the background, title position and font, fonts and positions for text at different levels of indentation, groups to be cloned and used as bullets, etc.&lt;br /&gt;
&lt;br /&gt;
Content specific to a particular slide can also be created in Inkscape, this content merged with the template and text input to make the final slide, so changes to the template after a particular slide is edited in Inkscape are included.&lt;br /&gt;
&lt;br /&gt;
=== Download ===&lt;br /&gt;
&lt;br /&gt;
Copy the [[InkSlide#Code|text below]] into a file called 'inkslide.py'.  (How do you attach a non-image file on this wiki?)&lt;br /&gt;
&lt;br /&gt;
Here is an [[Media:template0.svg|example template]].  Use Save Link As to stop your browser displaying the file instead of downloading it.&lt;br /&gt;
&lt;br /&gt;
=== Use ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python inkslide.py myTemplate.svg mySlides.txt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This creates a slide00xx.svg for each slide in your presentation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python inkslide.py --writeInstance &amp;lt;instanceId&amp;gt; myTemplate.svg mySlides.txt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Creates / updates the Inkscape file you can edit to add special content to this particular slide.   '''NOT IMPLEMENTED YET''' - the code exists, but not the command line hook.&lt;br /&gt;
&lt;br /&gt;
=== How it works ===&lt;br /&gt;
&lt;br /&gt;
First InkSlide parses the text input, something like the example at the top of this page.  It looks for flowed text boxes in the template file with special @id attributes like ''fr_title'' for the title, and ''fr_tab2'' for the second level of bulleted lists.  Copies of these elements are made and placed in a layer called 'Texts'.  At this point, the text is all piled up at the top of the page.&lt;br /&gt;
&lt;br /&gt;
InkSlide saves the file, and uses Inkscape's command line ''--query-height'' and friends to find out how high the wrapped text pieces are.  At it then repositions them properly, adds clones of the bullet markers, etc.  Bullet markers have special @id attributes like ''bu_1'' for the first level of bulleted list.&lt;br /&gt;
&lt;br /&gt;
Finally, InkSlide checks to see if there's an %id tag for the slide, and if so if there's a corresponding Inkscape SVG file with content for this particular slide. This content has to occur in layers whose name starts with ''Instance'', and these layers have to be present in the template file, but you can have more than one, so they can be above and below the regular content.  If such content is found, it's copied into the output slide.&lt;br /&gt;
&lt;br /&gt;
=== The Template File ===&lt;br /&gt;
&lt;br /&gt;
The template file is a regular Inkscape file.  If this list seems hard, just use the example template and modify it to suit your needs.&lt;br /&gt;
&lt;br /&gt;
Required contents:&lt;br /&gt;
&lt;br /&gt;
*A layer called ''Texts'', into which text will be placed.&lt;br /&gt;
*A flowed text frame with an @id of ''fr_title'' to define the font and position for the slide title.  This should contain text like ''Title goes here Sly'' because ''Sly'' includes letters which ascend and descend from the baseline to the greatest extent.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*A group with an @id of ''gr_title'' into which the title will be placed.  This makes it easier to do special effects on the title like shadows.&lt;br /&gt;
*Flowed text frames with an @id of ''fr_tabN'' for each N in 0, 1, 2, 3.  ''fr_tab0'' is used for top level non bulleted text.  These should also include ''Sly'' or similar.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*Objects or groups withan @id of ''bu_N'' for each N in 1, 2, 3, for the three levels of bulleted list.&lt;br /&gt;
&lt;br /&gt;
The fr_* elements define the font, font-size, color etc. for the different levels of text.  They also define the position and wrap width.  See the example template file for a possible arrangement.&lt;br /&gt;
&lt;br /&gt;
Optional contents:&lt;br /&gt;
&lt;br /&gt;
*A background&lt;br /&gt;
*Affiliation text and logos you want to appear on every slide&lt;br /&gt;
*A blurred clone of the gr_title element, offset and behind it.&lt;br /&gt;
*Layers whose name starts with ''Instance'' - these are the layers the user can edit directly in Inkscape for a particular slide.&lt;br /&gt;
&lt;br /&gt;
When you save the template file you should make sure that layers containing place holder text (the fr_* elements) and bullets are invisible, or at least hidden below the background layer, which should be visible.  Likewise the Texts layer should be visible, as well as any Instance layers.&lt;br /&gt;
&lt;br /&gt;
=== Text Input ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%title Inkslide: an example&lt;br /&gt;
This text will appear as a top level paragraph.&lt;br /&gt;
    Indented by a single tab, this text will appear as a bulleted item.&lt;br /&gt;
        Two tabs -&amp;gt; second level bulleted list.&lt;br /&gt;
&lt;br /&gt;
A gap will be left for the preceeding line.&lt;br /&gt;
%pause&lt;br /&gt;
Two slides will be produced for this input, one with everything up to the %pause above,&lt;br /&gt;
and one with that plus the remaining content.&lt;br /&gt;
Incremental list display can be done this way.&lt;br /&gt;
&lt;br /&gt;
The %id tag below indicates that a file with a name like 'extra/is_intro.svg' should&lt;br /&gt;
be inspected for extra content for this particular slide.&lt;br /&gt;
%id is_intro&lt;br /&gt;
%title Inkslide: the next slide...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== The GUI ===&lt;br /&gt;
&lt;br /&gt;
A GUI for InkSlide is available as a plug-in for the [http://webpages.charter.net/edreamleo/front.html Leo] outline editor.  This makes slide re-arranging easy, and adds some macro substitution capability.  It also adds buttons for editing slide specific content in Inkscape.&lt;br /&gt;
&lt;br /&gt;
FIXME: post plug-in on Leo wiki and link.&lt;br /&gt;
&lt;br /&gt;
=== To do / status ===&lt;br /&gt;
&lt;br /&gt;
InkSlide is very usable, but is missing some features:&lt;br /&gt;
&lt;br /&gt;
*Inline text formatting (bold, color, etc.) for part of a piece of text&lt;br /&gt;
*Justification switching&lt;br /&gt;
*Verbatim code display&lt;br /&gt;
*Simple image inclusion&lt;br /&gt;
**currently you can do this using the Inkscape editing features&lt;br /&gt;
*more helper tool to make PNG and PDF versions of slides&lt;br /&gt;
*configuring / using a directory for edited slides&lt;br /&gt;
*command line flags for generating specific slides&lt;br /&gt;
&lt;br /&gt;
Currently InkSlide is slow, because it must invoke Inkscape once for every single dimension it needs to retrieve.  I'll try and get a --query-all switch into 0.46.&lt;br /&gt;
&lt;br /&gt;
=== Bugs ===&lt;br /&gt;
&lt;br /&gt;
*There may be an issue with the way slide specific content is merged into the template producing conflicting @id attributes, but I haven't seen this happen yet.&lt;br /&gt;
&lt;br /&gt;
=== Author  / Credits ===&lt;br /&gt;
&lt;br /&gt;
InkSlide was written by Terry Brown, terry-n-brown@yahoo.com - but use underscore, not '-'.&lt;br /&gt;
&lt;br /&gt;
InkSlide was inspired by [http://member.wide.ad.jp/wg/mgp/ MagicPoint]&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# inkslide.py $Id$&lt;br /&gt;
# Author: Terry Brown&lt;br /&gt;
# Created: Thu Oct 11 2007&lt;br /&gt;
&lt;br /&gt;
import lxml.etree as ET&lt;br /&gt;
&lt;br /&gt;
import copy&lt;br /&gt;
import subprocess&lt;br /&gt;
import os.path&lt;br /&gt;
&lt;br /&gt;
NS = { 'i': 'http://www.inkscape.org/namespaces/inkscape',&lt;br /&gt;
       's': 'http://www.w3.org/2000/svg',&lt;br /&gt;
       'xlink' : 'http://www.w3.org/1999/xlink'}&lt;br /&gt;
&lt;br /&gt;
# number of defined tablevels, FIXME, could derive from template?&lt;br /&gt;
tabLevels = 4  &lt;br /&gt;
def clearId(x, what='id', NS={}):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;recursively clear @id on element x and descendants&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if what in x.keys():&lt;br /&gt;
        del x.attrib[what]&lt;br /&gt;
    for i in x.xpath('.//*[@%s]'%what, NS):&lt;br /&gt;
        del i.attrib[what]&lt;br /&gt;
    return x&lt;br /&gt;
def getDim(fn, Id, what):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return dimension of element in fn with @id Id, what is&lt;br /&gt;
    x, y, width, height&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cmd = ('inkscape', '--without-gui', '--query-id', Id, '--query-'+what, fn)&lt;br /&gt;
    proc = subprocess.Popen(cmd, stdout = subprocess.PIPE,&lt;br /&gt;
                            stderr = subprocess.PIPE)&lt;br /&gt;
    # make new pipe for stderr to supress chatter from inkscape&lt;br /&gt;
    proc.wait()&lt;br /&gt;
    return proc.stdout.read()&lt;br /&gt;
def textReader(text):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;iterate parts in input text&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not text: return&lt;br /&gt;
    for line in text.split('\n'):&lt;br /&gt;
        if line.startswith('%slide '):&lt;br /&gt;
            yield {'type':'TITLE', 's':line[7:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%pause'):&lt;br /&gt;
            yield {'type':'PAUSE'}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%id '):&lt;br /&gt;
            yield {'type':'ID', 's':line[4:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if not line.strip():&lt;br /&gt;
            yield {'type':'BLANK'}&lt;br /&gt;
            continue&lt;br /&gt;
        tabs = len(line) - len(line.lstrip('\t'))&lt;br /&gt;
        yield {'type':'TAB', 'tabs':tabs, 's':line.strip()}&lt;br /&gt;
&lt;br /&gt;
nid = 0&lt;br /&gt;
def nextId():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return an unsed Id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # FIXME, should inspect templat doc.&lt;br /&gt;
    global nid&lt;br /&gt;
    nid += 1&lt;br /&gt;
    return 'is'+str(nid)&lt;br /&gt;
&lt;br /&gt;
def instanceFromTemplate(template, instance, text, pauseOk = True):&lt;br /&gt;
&lt;br /&gt;
    slide = 0&lt;br /&gt;
&lt;br /&gt;
    hlist = []&lt;br /&gt;
    doc0 = ET.parse(template)&lt;br /&gt;
    slideId = None&lt;br /&gt;
    &lt;br /&gt;
    for t in textReader(text):&lt;br /&gt;
        &lt;br /&gt;
        if t['type'] == 'ID':&lt;br /&gt;
            slideId = t['s']&lt;br /&gt;
            continue&lt;br /&gt;
            &lt;br /&gt;
        if t['type'] == 'TITLE':&lt;br /&gt;
            print t['s']&lt;br /&gt;
            if hlist:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
                hlist = [t]&lt;br /&gt;
                doc0 = ET.parse(template)&lt;br /&gt;
                slideId = None&lt;br /&gt;
            else:&lt;br /&gt;
                hlist.append(t)&lt;br /&gt;
            dst = doc0.xpath('//s:g[@id=&amp;quot;gr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'PAUSE':&lt;br /&gt;
            if pauseOk:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        hlist.append(t)&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'BLANK':&lt;br /&gt;
            continue&lt;br /&gt;
        if t['type'] == 'TAB':&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_tab%d&amp;quot;]' % t['tabs'], NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            #break&lt;br /&gt;
            continue&lt;br /&gt;
           &lt;br /&gt;
    process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
def process(instance, doc0, hlist, slideId, slide):&lt;br /&gt;
&lt;br /&gt;
    instance = instance.replace('%s', '%04d'%slide)&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
&lt;br /&gt;
    bullDim = {}&lt;br /&gt;
    tabDim = {}&lt;br /&gt;
    for n in range(0,tabLevels):&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;bu_%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'width', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'bu_%d'%n, i))&lt;br /&gt;
            bullDim[n] = b&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;fr_tab%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'fr_tab%d'%n, i))&lt;br /&gt;
            tabDim[n] = b&lt;br /&gt;
&lt;br /&gt;
    delta = 0.&lt;br /&gt;
    firstVert = True&lt;br /&gt;
    for n, i in enumerate(hlist):&lt;br /&gt;
        if hlist[n]['type'] in ['TITLE']:&lt;br /&gt;
            continue  # already in right place, not a vertical element&lt;br /&gt;
&lt;br /&gt;
        hgt = 0&lt;br /&gt;
        if hlist[n-1]['type'] == 'BLANK':&lt;br /&gt;
            hgt = 30&lt;br /&gt;
        else:&lt;br /&gt;
            hgt = float(getDim(instance, hlist[n-1]['id'], 'height'))&lt;br /&gt;
&lt;br /&gt;
        hgt += 5&lt;br /&gt;
&lt;br /&gt;
        if firstVert:&lt;br /&gt;
            hgt = 0&lt;br /&gt;
            firstVert = False&lt;br /&gt;
&lt;br /&gt;
        delta += hgt&lt;br /&gt;
&lt;br /&gt;
        if hlist[n]['type'] == 'BLANK': continue&lt;br /&gt;
&lt;br /&gt;
        tabs = hlist[n]['tabs']&lt;br /&gt;
&lt;br /&gt;
        r = doc0.xpath('//s:flowRoot[@id=&amp;quot;%s&amp;quot;]' % hlist[n]['id'], NS)[0]&lt;br /&gt;
        clearId(r, what='transform')&lt;br /&gt;
        r = r.xpath('.//s:rect', NS)[0]&lt;br /&gt;
        r.set('x', str(tabDim[tabs]['x']))&lt;br /&gt;
        y = tabDim[tabs]['y'] + delta&lt;br /&gt;
        #print '%s -&amp;gt; %s' % (str(r.get('y')), str(y))&lt;br /&gt;
        #print tabDim[tabs]['y']&lt;br /&gt;
        r.set('y', str(y))&lt;br /&gt;
&lt;br /&gt;
        if (hlist[n]['type'] == 'TAB' and hlist[n]['tabs'] in bullDim&lt;br /&gt;
            and 'isDone' not in hlist[n]):&lt;br /&gt;
            hlist[n]['isDone'] = True&lt;br /&gt;
            dx = (tabDim[tabs]['x'] &lt;br /&gt;
                  - bullDim[tabs]['x']&lt;br /&gt;
                  - 1.5*bullDim[tabs]['width']&lt;br /&gt;
                  )&lt;br /&gt;
            dy = (y &lt;br /&gt;
                  + tabDim[tabs]['height'] / 2.&lt;br /&gt;
                  - bullDim[tabs]['y'] &lt;br /&gt;
                  - bullDim[tabs]['height'] / 2.&lt;br /&gt;
                  )&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            #print y, tabDim[tabs]['height'], bullDim[tabs]['y'], bullDim[tabs]['height'], dy&lt;br /&gt;
            clone = ET.Element('svg:use')&lt;br /&gt;
            clone.set('transform', 'translate(%f,%f)' % (dx,dy))&lt;br /&gt;
            clone.set('{%s}href'%NS['xlink'], '#bu_%d'%tabs)&lt;br /&gt;
            dst.append(clone)&lt;br /&gt;
            &lt;br /&gt;
            #path = ET.Element('svg:path')&lt;br /&gt;
            #path.set('d', ('M %f,%f' + ' L %f,%f'*5 + ' z') % (&lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y'], &lt;br /&gt;
            #    bullDim[tabs]['x']+20, bullDim[tabs]['y']+bullDim[tabs]['height']/2., &lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y']+bullDim[tabs]['height'], &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y'], &lt;br /&gt;
            #    tabDim[tabs]['x']+20, tabDim[tabs]['y']+tabDim[tabs]['height']/2., &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y']+tabDim[tabs]['height'], &lt;br /&gt;
            #    ))&lt;br /&gt;
            #dst.append(path)&lt;br /&gt;
            #break&lt;br /&gt;
&lt;br /&gt;
    # look for instance specific parts&lt;br /&gt;
    if slideId:&lt;br /&gt;
        f = instancePath(slideId)&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            comp = ET.parse(f)&lt;br /&gt;
            svg = doc0.xpath('//s:svg', NS)[0]&lt;br /&gt;
            g = &amp;quot;{%s}g&amp;quot;%NS['s']&lt;br /&gt;
            k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
            for n, i in enumerate(svg.getchildren()):&lt;br /&gt;
                if i.tag == g and i.get(k, '').startswith('Instance'):&lt;br /&gt;
                    x = comp.xpath('//s:svg//s:g[@i:label=&amp;quot;%s&amp;quot;]'%i.get(k), NS)&lt;br /&gt;
                    if x:&lt;br /&gt;
                        x = x[0]&lt;br /&gt;
                        cpy = clearId(copy.deepcopy(x))&lt;br /&gt;
                        svg[n] = cpy&lt;br /&gt;
            defs = comp.xpath('//s:svg/s:defs/*', NS)&lt;br /&gt;
            dst = doc0.xpath('//s:svg/s:defs', NS)[0]&lt;br /&gt;
            for d in defs:&lt;br /&gt;
                Id = d.get('id')&lt;br /&gt;
                current = doc0.xpath('//s:svg/s:defs/*[@id=&amp;quot;%s&amp;quot;]'%Id, NS)&lt;br /&gt;
                if not current:&lt;br /&gt;
                    cpy = copy.deepcopy(d)&lt;br /&gt;
                    dst.append(cpy)&lt;br /&gt;
&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
    &lt;br /&gt;
    return slideId&lt;br /&gt;
def instancePath(slideId, component = None):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return path to component for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # return os.path.join(slideId, component)&lt;br /&gt;
    return slideId+'.svg'&lt;br /&gt;
def writeComponents(inkscapeFile, slideId):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;write components from file for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not slideId: return&lt;br /&gt;
    doc0 = ET.parse(inkscapeFile)&lt;br /&gt;
    hasComponents = False&lt;br /&gt;
    for i in doc0.xpath('//s:svg//s:g', NS):&lt;br /&gt;
        k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
        # print i.keys(),k&lt;br /&gt;
        if (k in i.keys() and i.get(k).startswith('Instance')):&lt;br /&gt;
            if len(i) &amp;gt; 0:&lt;br /&gt;
                hasComponents = True&lt;br /&gt;
                break&lt;br /&gt;
                # ET.ElementTree(i).write(f)&lt;br /&gt;
                &lt;br /&gt;
    f = instancePath(slideId, 'components')&lt;br /&gt;
    &lt;br /&gt;
    if hasComponents:&lt;br /&gt;
        if (os.path.abspath(os.path.realpath(inkscapeFile)) != &lt;br /&gt;
            os.path.abspath(os.path.realpath(f))):&lt;br /&gt;
            file(f, 'w').write(file(inkscapeFile).read())&lt;br /&gt;
    else:&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            os.remove(f)&lt;br /&gt;
&lt;br /&gt;
if __name__ == '__main__':&lt;br /&gt;
    import sys&lt;br /&gt;
    instanceFromTemplate(sys.argv[1], 'slide%s.svg', file(sys.argv[2]).read())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16469</id>
		<title>InkSlide</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16469"/>
		<updated>2007-10-17T19:50:09Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: /* Text Input */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== InkSlide - quick and easy presentations using Inkscape ==&lt;br /&gt;
&lt;br /&gt;
InkSlide produces slides like this:&lt;br /&gt;
&lt;br /&gt;
[[Image:slide0004.jpg]]&lt;br /&gt;
&lt;br /&gt;
from simple text input like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%slide InkSlide: Features&lt;br /&gt;
Features include wrapped top level text and&lt;br /&gt;
    mulitple&lt;br /&gt;
        levels&lt;br /&gt;
            of wrapped bulleted lists with bullets and font&lt;br /&gt;
information taken from the template file.&lt;br /&gt;
&lt;br /&gt;
Slide specific content like this:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
which is updated when the template changes.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An Inkscape file is used as a template file to define the background, title position and font, fonts and positions for text at different levels of indentation, groups to be cloned and used as bullets, etc.&lt;br /&gt;
&lt;br /&gt;
Content specific to a particular slide can also be created in Inkscape, this content merged with the template and text input to make the final slide, so changes to the template after a particular slide is edited in Inkscape are included.&lt;br /&gt;
&lt;br /&gt;
=== Download ===&lt;br /&gt;
&lt;br /&gt;
Copy the [[InkSlide#Code|text below]] into a file called 'inkslide.py'.  (How do you attach a non-image file on this wiki?)&lt;br /&gt;
&lt;br /&gt;
Here is an [[Media:template0.svg|example template]].  Use Save Link As to stop your browser displaying the file instead of downloading it.&lt;br /&gt;
&lt;br /&gt;
=== Use ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python inkslide.py myTemplate.svg mySlides.txt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This creates a slide00xx.svg for each slide in your presentation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python inkslide.py --writeInstance &amp;lt;instanceId&amp;gt; myTemplate.svg mySlides.txt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Creates / updates the Inkscape file you can edit to add special content to this particular slide.   '''NOT IMPLEMENTED YET''' - the code exists, but not the command line hook.&lt;br /&gt;
&lt;br /&gt;
=== How it works ===&lt;br /&gt;
&lt;br /&gt;
First InkSlide parses the text input, something like the example at the top of this page.  It looks for flowed text boxes in the template file with special @id attributes like ''fr_title'' for the title, and ''fr_tab2'' for the second level of bulleted lists.  Copies of these elements are made and placed in a layer called 'Texts'.  At this point, the text is all piled up at the top of the page.&lt;br /&gt;
&lt;br /&gt;
InkSlide saves the file, and uses Inkscape's command line ''--query-height'' and friends to find out how high the wrapped text pieces are.  At it then repositions them properly, adds clones of the bullet markers, etc.  Bullet markers have special @id attributes like ''bu_1'' for the first level of bulleted list.&lt;br /&gt;
&lt;br /&gt;
Finally, InkSlide checks to see if there's an %id tag for the slide, and if so if there's a corresponding Inkscape SVG file with content for this particular slide. This content has to occur in layers whose name starts with ''Instance'', and these layers have to be present in the template file, but you can have more than one, so they can be above and below the regular content.  If such content is found, it's copied into the output slide.&lt;br /&gt;
&lt;br /&gt;
=== The Template File ===&lt;br /&gt;
&lt;br /&gt;
The template file is a regular Inkscape file.  If this list seems hard, just use the example template and modify it to suit your needs.&lt;br /&gt;
&lt;br /&gt;
Required contents:&lt;br /&gt;
&lt;br /&gt;
*A layer called ''Texts'', into which text will be placed.&lt;br /&gt;
*A flowed text frame with an @id of ''fr_title'' to define the font and position for the slide title.  This should contain text like ''Title goes here Sly'' because ''Sly'' includes letters which ascend and descend from the baseline to the greatest extent.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*A group with an @id of ''gr_title'' into which the title will be placed.  This makes it easier to do special effects on the title like shadows.&lt;br /&gt;
*Flowed text frames with an @id of ''fr_tabN'' for each N in 0, 1, 2, 3.  ''fr_tab0'' is used for top level non bulleted text.  These should also include ''Sly'' or similar.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*Objects or groups withan @id of ''bu_N'' for each N in 1, 2, 3, for the three levels of bulleted list.&lt;br /&gt;
&lt;br /&gt;
The fr_* elements define the font, font-size, color etc. for the different levels of text.  They also define the position and wrap width.  See the example template file for a possible arrangement.&lt;br /&gt;
&lt;br /&gt;
Optional contents:&lt;br /&gt;
&lt;br /&gt;
*A background&lt;br /&gt;
*Affiliation text and logos you want to appear on every slide&lt;br /&gt;
*A blurred clone of the gr_title element, offset and behind it.&lt;br /&gt;
*Layers whose name starts with ''Instance'' - these are the layers the user can edit directly in Inkscape for a particular slide.&lt;br /&gt;
&lt;br /&gt;
When you save the template file you should make sure that layers containing place holder text (the fr_* elements) and bullets are invisible, or at least hidden below the background layer, which should be visible.  Likewise the Texts layer should be visible, as well as any Instance layers.&lt;br /&gt;
&lt;br /&gt;
=== Text Input ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%title Inkslide: an example&lt;br /&gt;
This text will appear as a top level paragraph.&lt;br /&gt;
    Indented by a single tab, this text will appear as a bulleted item.&lt;br /&gt;
        Two tabs -&amp;gt; second level bulleted list.&lt;br /&gt;
&lt;br /&gt;
A gap will be left for the preceeding line.&lt;br /&gt;
%pause&lt;br /&gt;
Two slides will be produced for this input, one with everything up to the %pause above,&lt;br /&gt;
and one with that plus the remaining content.  Incremental list display can be done this way.&lt;br /&gt;
&lt;br /&gt;
The %id tag below indicates that a file with a name like 'extra/is_intro.svg' should&lt;br /&gt;
be inspected for extra content for this particular slide.&lt;br /&gt;
%id is_intro&lt;br /&gt;
%title Inkslide: the next slide...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== The GUI ===&lt;br /&gt;
&lt;br /&gt;
A GUI for InkSlide is available as a plug-in for the [http://webpages.charter.net/edreamleo/front.html Leo] outline editor.  This makes slide re-arranging easy, and adds some macro substitution capability.  It also adds buttons for editing slide specific content in Inkscape.&lt;br /&gt;
&lt;br /&gt;
FIXME: post plug-in on Leo wiki and link.&lt;br /&gt;
&lt;br /&gt;
=== To do / status ===&lt;br /&gt;
&lt;br /&gt;
InkSlide is very usable, but is missing some features:&lt;br /&gt;
&lt;br /&gt;
*Inline text formatting (bold, color, etc.) for part of a piece of text&lt;br /&gt;
*Justification switching&lt;br /&gt;
*Verbatim code display&lt;br /&gt;
*Simple image inclusion&lt;br /&gt;
**currently you can do this using the Inkscape editing features&lt;br /&gt;
*more helper tool to make PNG and PDF versions of slides&lt;br /&gt;
*configuring / using a directory for edited slides&lt;br /&gt;
*command line flags for generating specific slides&lt;br /&gt;
&lt;br /&gt;
Currently InkSlide is slow, because it must invoke Inkscape once for every single dimension it needs to retrieve.  I'll try and get a --query-all switch into 0.46.&lt;br /&gt;
&lt;br /&gt;
=== Bugs ===&lt;br /&gt;
&lt;br /&gt;
*There may be an issue with the way slide specific content is merged into the template producing conflicting @id attributes, but I haven't seen this happen yet.&lt;br /&gt;
&lt;br /&gt;
=== Author  / Credits ===&lt;br /&gt;
&lt;br /&gt;
InkSlide was written by Terry Brown, terry-n-brown@yahoo.com - but use underscore, not '-'.&lt;br /&gt;
&lt;br /&gt;
InkSlide was inspired by [http://member.wide.ad.jp/wg/mgp/ MagicPoint]&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# inkslide.py $Id$&lt;br /&gt;
# Author: Terry Brown&lt;br /&gt;
# Created: Thu Oct 11 2007&lt;br /&gt;
&lt;br /&gt;
import lxml.etree as ET&lt;br /&gt;
&lt;br /&gt;
import copy&lt;br /&gt;
import subprocess&lt;br /&gt;
import os.path&lt;br /&gt;
&lt;br /&gt;
NS = { 'i': 'http://www.inkscape.org/namespaces/inkscape',&lt;br /&gt;
       's': 'http://www.w3.org/2000/svg',&lt;br /&gt;
       'xlink' : 'http://www.w3.org/1999/xlink'}&lt;br /&gt;
&lt;br /&gt;
# number of defined tablevels, FIXME, could derive from template?&lt;br /&gt;
tabLevels = 4  &lt;br /&gt;
def clearId(x, what='id', NS={}):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;recursively clear @id on element x and descendants&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if what in x.keys():&lt;br /&gt;
        del x.attrib[what]&lt;br /&gt;
    for i in x.xpath('.//*[@%s]'%what, NS):&lt;br /&gt;
        del i.attrib[what]&lt;br /&gt;
    return x&lt;br /&gt;
def getDim(fn, Id, what):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return dimension of element in fn with @id Id, what is&lt;br /&gt;
    x, y, width, height&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cmd = ('inkscape', '--without-gui', '--query-id', Id, '--query-'+what, fn)&lt;br /&gt;
    proc = subprocess.Popen(cmd, stdout = subprocess.PIPE,&lt;br /&gt;
                            stderr = subprocess.PIPE)&lt;br /&gt;
    # make new pipe for stderr to supress chatter from inkscape&lt;br /&gt;
    proc.wait()&lt;br /&gt;
    return proc.stdout.read()&lt;br /&gt;
def textReader(text):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;iterate parts in input text&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not text: return&lt;br /&gt;
    for line in text.split('\n'):&lt;br /&gt;
        if line.startswith('%slide '):&lt;br /&gt;
            yield {'type':'TITLE', 's':line[7:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%pause'):&lt;br /&gt;
            yield {'type':'PAUSE'}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%id '):&lt;br /&gt;
            yield {'type':'ID', 's':line[4:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if not line.strip():&lt;br /&gt;
            yield {'type':'BLANK'}&lt;br /&gt;
            continue&lt;br /&gt;
        tabs = len(line) - len(line.lstrip('\t'))&lt;br /&gt;
        yield {'type':'TAB', 'tabs':tabs, 's':line.strip()}&lt;br /&gt;
&lt;br /&gt;
nid = 0&lt;br /&gt;
def nextId():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return an unsed Id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # FIXME, should inspect templat doc.&lt;br /&gt;
    global nid&lt;br /&gt;
    nid += 1&lt;br /&gt;
    return 'is'+str(nid)&lt;br /&gt;
&lt;br /&gt;
def instanceFromTemplate(template, instance, text, pauseOk = True):&lt;br /&gt;
&lt;br /&gt;
    slide = 0&lt;br /&gt;
&lt;br /&gt;
    hlist = []&lt;br /&gt;
    doc0 = ET.parse(template)&lt;br /&gt;
    slideId = None&lt;br /&gt;
    &lt;br /&gt;
    for t in textReader(text):&lt;br /&gt;
        &lt;br /&gt;
        if t['type'] == 'ID':&lt;br /&gt;
            slideId = t['s']&lt;br /&gt;
            continue&lt;br /&gt;
            &lt;br /&gt;
        if t['type'] == 'TITLE':&lt;br /&gt;
            print t['s']&lt;br /&gt;
            if hlist:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
                hlist = [t]&lt;br /&gt;
                doc0 = ET.parse(template)&lt;br /&gt;
                slideId = None&lt;br /&gt;
            else:&lt;br /&gt;
                hlist.append(t)&lt;br /&gt;
            dst = doc0.xpath('//s:g[@id=&amp;quot;gr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'PAUSE':&lt;br /&gt;
            if pauseOk:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        hlist.append(t)&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'BLANK':&lt;br /&gt;
            continue&lt;br /&gt;
        if t['type'] == 'TAB':&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_tab%d&amp;quot;]' % t['tabs'], NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            #break&lt;br /&gt;
            continue&lt;br /&gt;
           &lt;br /&gt;
    process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
def process(instance, doc0, hlist, slideId, slide):&lt;br /&gt;
&lt;br /&gt;
    instance = instance.replace('%s', '%04d'%slide)&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
&lt;br /&gt;
    bullDim = {}&lt;br /&gt;
    tabDim = {}&lt;br /&gt;
    for n in range(0,tabLevels):&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;bu_%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'width', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'bu_%d'%n, i))&lt;br /&gt;
            bullDim[n] = b&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;fr_tab%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'fr_tab%d'%n, i))&lt;br /&gt;
            tabDim[n] = b&lt;br /&gt;
&lt;br /&gt;
    delta = 0.&lt;br /&gt;
    firstVert = True&lt;br /&gt;
    for n, i in enumerate(hlist):&lt;br /&gt;
        if hlist[n]['type'] in ['TITLE']:&lt;br /&gt;
            continue  # already in right place, not a vertical element&lt;br /&gt;
&lt;br /&gt;
        hgt = 0&lt;br /&gt;
        if hlist[n-1]['type'] == 'BLANK':&lt;br /&gt;
            hgt = 30&lt;br /&gt;
        else:&lt;br /&gt;
            hgt = float(getDim(instance, hlist[n-1]['id'], 'height'))&lt;br /&gt;
&lt;br /&gt;
        hgt += 5&lt;br /&gt;
&lt;br /&gt;
        if firstVert:&lt;br /&gt;
            hgt = 0&lt;br /&gt;
            firstVert = False&lt;br /&gt;
&lt;br /&gt;
        delta += hgt&lt;br /&gt;
&lt;br /&gt;
        if hlist[n]['type'] == 'BLANK': continue&lt;br /&gt;
&lt;br /&gt;
        tabs = hlist[n]['tabs']&lt;br /&gt;
&lt;br /&gt;
        r = doc0.xpath('//s:flowRoot[@id=&amp;quot;%s&amp;quot;]' % hlist[n]['id'], NS)[0]&lt;br /&gt;
        clearId(r, what='transform')&lt;br /&gt;
        r = r.xpath('.//s:rect', NS)[0]&lt;br /&gt;
        r.set('x', str(tabDim[tabs]['x']))&lt;br /&gt;
        y = tabDim[tabs]['y'] + delta&lt;br /&gt;
        #print '%s -&amp;gt; %s' % (str(r.get('y')), str(y))&lt;br /&gt;
        #print tabDim[tabs]['y']&lt;br /&gt;
        r.set('y', str(y))&lt;br /&gt;
&lt;br /&gt;
        if (hlist[n]['type'] == 'TAB' and hlist[n]['tabs'] in bullDim&lt;br /&gt;
            and 'isDone' not in hlist[n]):&lt;br /&gt;
            hlist[n]['isDone'] = True&lt;br /&gt;
            dx = (tabDim[tabs]['x'] &lt;br /&gt;
                  - bullDim[tabs]['x']&lt;br /&gt;
                  - 1.5*bullDim[tabs]['width']&lt;br /&gt;
                  )&lt;br /&gt;
            dy = (y &lt;br /&gt;
                  + tabDim[tabs]['height'] / 2.&lt;br /&gt;
                  - bullDim[tabs]['y'] &lt;br /&gt;
                  - bullDim[tabs]['height'] / 2.&lt;br /&gt;
                  )&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            #print y, tabDim[tabs]['height'], bullDim[tabs]['y'], bullDim[tabs]['height'], dy&lt;br /&gt;
            clone = ET.Element('svg:use')&lt;br /&gt;
            clone.set('transform', 'translate(%f,%f)' % (dx,dy))&lt;br /&gt;
            clone.set('{%s}href'%NS['xlink'], '#bu_%d'%tabs)&lt;br /&gt;
            dst.append(clone)&lt;br /&gt;
            &lt;br /&gt;
            #path = ET.Element('svg:path')&lt;br /&gt;
            #path.set('d', ('M %f,%f' + ' L %f,%f'*5 + ' z') % (&lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y'], &lt;br /&gt;
            #    bullDim[tabs]['x']+20, bullDim[tabs]['y']+bullDim[tabs]['height']/2., &lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y']+bullDim[tabs]['height'], &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y'], &lt;br /&gt;
            #    tabDim[tabs]['x']+20, tabDim[tabs]['y']+tabDim[tabs]['height']/2., &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y']+tabDim[tabs]['height'], &lt;br /&gt;
            #    ))&lt;br /&gt;
            #dst.append(path)&lt;br /&gt;
            #break&lt;br /&gt;
&lt;br /&gt;
    # look for instance specific parts&lt;br /&gt;
    if slideId:&lt;br /&gt;
        f = instancePath(slideId)&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            comp = ET.parse(f)&lt;br /&gt;
            svg = doc0.xpath('//s:svg', NS)[0]&lt;br /&gt;
            g = &amp;quot;{%s}g&amp;quot;%NS['s']&lt;br /&gt;
            k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
            for n, i in enumerate(svg.getchildren()):&lt;br /&gt;
                if i.tag == g and i.get(k, '').startswith('Instance'):&lt;br /&gt;
                    x = comp.xpath('//s:svg//s:g[@i:label=&amp;quot;%s&amp;quot;]'%i.get(k), NS)&lt;br /&gt;
                    if x:&lt;br /&gt;
                        x = x[0]&lt;br /&gt;
                        cpy = clearId(copy.deepcopy(x))&lt;br /&gt;
                        svg[n] = cpy&lt;br /&gt;
            defs = comp.xpath('//s:svg/s:defs/*', NS)&lt;br /&gt;
            dst = doc0.xpath('//s:svg/s:defs', NS)[0]&lt;br /&gt;
            for d in defs:&lt;br /&gt;
                Id = d.get('id')&lt;br /&gt;
                current = doc0.xpath('//s:svg/s:defs/*[@id=&amp;quot;%s&amp;quot;]'%Id, NS)&lt;br /&gt;
                if not current:&lt;br /&gt;
                    cpy = copy.deepcopy(d)&lt;br /&gt;
                    dst.append(cpy)&lt;br /&gt;
&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
    &lt;br /&gt;
    return slideId&lt;br /&gt;
def instancePath(slideId, component = None):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return path to component for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # return os.path.join(slideId, component)&lt;br /&gt;
    return slideId+'.svg'&lt;br /&gt;
def writeComponents(inkscapeFile, slideId):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;write components from file for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not slideId: return&lt;br /&gt;
    doc0 = ET.parse(inkscapeFile)&lt;br /&gt;
    hasComponents = False&lt;br /&gt;
    for i in doc0.xpath('//s:svg//s:g', NS):&lt;br /&gt;
        k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
        # print i.keys(),k&lt;br /&gt;
        if (k in i.keys() and i.get(k).startswith('Instance')):&lt;br /&gt;
            if len(i) &amp;gt; 0:&lt;br /&gt;
                hasComponents = True&lt;br /&gt;
                break&lt;br /&gt;
                # ET.ElementTree(i).write(f)&lt;br /&gt;
                &lt;br /&gt;
    f = instancePath(slideId, 'components')&lt;br /&gt;
    &lt;br /&gt;
    if hasComponents:&lt;br /&gt;
        if (os.path.abspath(os.path.realpath(inkscapeFile)) != &lt;br /&gt;
            os.path.abspath(os.path.realpath(f))):&lt;br /&gt;
            file(f, 'w').write(file(inkscapeFile).read())&lt;br /&gt;
    else:&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            os.remove(f)&lt;br /&gt;
&lt;br /&gt;
if __name__ == '__main__':&lt;br /&gt;
    import sys&lt;br /&gt;
    instanceFromTemplate(sys.argv[1], 'slide%s.svg', file(sys.argv[2]).read())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16468</id>
		<title>InkSlide</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16468"/>
		<updated>2007-10-17T17:03:36Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: /* To do / status */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== InkSlide - quick and easy presentations using Inkscape ==&lt;br /&gt;
&lt;br /&gt;
InkSlide produces slides like this:&lt;br /&gt;
&lt;br /&gt;
[[Image:slide0004.jpg]]&lt;br /&gt;
&lt;br /&gt;
from simple text input like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%slide InkSlide: Features&lt;br /&gt;
Features include wrapped top level text and&lt;br /&gt;
    mulitple&lt;br /&gt;
        levels&lt;br /&gt;
            of wrapped bulleted lists with bullets and font&lt;br /&gt;
information taken from the template file.&lt;br /&gt;
&lt;br /&gt;
Slide specific content like this:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
which is updated when the template changes.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An Inkscape file is used as a template file to define the background, title position and font, fonts and positions for text at different levels of indentation, groups to be cloned and used as bullets, etc.&lt;br /&gt;
&lt;br /&gt;
Content specific to a particular slide can also be created in Inkscape, this content merged with the template and text input to make the final slide, so changes to the template after a particular slide is edited in Inkscape are included.&lt;br /&gt;
&lt;br /&gt;
=== Download ===&lt;br /&gt;
&lt;br /&gt;
Copy the [[InkSlide#Code|text below]] into a file called 'inkslide.py'.  (How do you attach a non-image file on this wiki?)&lt;br /&gt;
&lt;br /&gt;
Here is an [[Media:template0.svg|example template]].  Use Save Link As to stop your browser displaying the file instead of downloading it.&lt;br /&gt;
&lt;br /&gt;
=== Use ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python inkslide.py myTemplate.svg mySlides.txt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This creates a slide00xx.svg for each slide in your presentation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python inkslide.py --writeInstance &amp;lt;instanceId&amp;gt; myTemplate.svg mySlides.txt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Creates / updates the Inkscape file you can edit to add special content to this particular slide.   '''NOT IMPLEMENTED YET''' - the code exists, but not the command line hook.&lt;br /&gt;
&lt;br /&gt;
=== How it works ===&lt;br /&gt;
&lt;br /&gt;
First InkSlide parses the text input, something like the example at the top of this page.  It looks for flowed text boxes in the template file with special @id attributes like ''fr_title'' for the title, and ''fr_tab2'' for the second level of bulleted lists.  Copies of these elements are made and placed in a layer called 'Texts'.  At this point, the text is all piled up at the top of the page.&lt;br /&gt;
&lt;br /&gt;
InkSlide saves the file, and uses Inkscape's command line ''--query-height'' and friends to find out how high the wrapped text pieces are.  At it then repositions them properly, adds clones of the bullet markers, etc.  Bullet markers have special @id attributes like ''bu_1'' for the first level of bulleted list.&lt;br /&gt;
&lt;br /&gt;
Finally, InkSlide checks to see if there's an %id tag for the slide, and if so if there's a corresponding Inkscape SVG file with content for this particular slide. This content has to occur in layers whose name starts with ''Instance'', and these layers have to be present in the template file, but you can have more than one, so they can be above and below the regular content.  If such content is found, it's copied into the output slide.&lt;br /&gt;
&lt;br /&gt;
=== The Template File ===&lt;br /&gt;
&lt;br /&gt;
The template file is a regular Inkscape file.  If this list seems hard, just use the example template and modify it to suit your needs.&lt;br /&gt;
&lt;br /&gt;
Required contents:&lt;br /&gt;
&lt;br /&gt;
*A layer called ''Texts'', into which text will be placed.&lt;br /&gt;
*A flowed text frame with an @id of ''fr_title'' to define the font and position for the slide title.  This should contain text like ''Title goes here Sly'' because ''Sly'' includes letters which ascend and descend from the baseline to the greatest extent.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*A group with an @id of ''gr_title'' into which the title will be placed.  This makes it easier to do special effects on the title like shadows.&lt;br /&gt;
*Flowed text frames with an @id of ''fr_tabN'' for each N in 0, 1, 2, 3.  ''fr_tab0'' is used for top level non bulleted text.  These should also include ''Sly'' or similar.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*Objects or groups withan @id of ''bu_N'' for each N in 1, 2, 3, for the three levels of bulleted list.&lt;br /&gt;
&lt;br /&gt;
The fr_* elements define the font, font-size, color etc. for the different levels of text.  They also define the position and wrap width.  See the example template file for a possible arrangement.&lt;br /&gt;
&lt;br /&gt;
Optional contents:&lt;br /&gt;
&lt;br /&gt;
*A background&lt;br /&gt;
*Affiliation text and logos you want to appear on every slide&lt;br /&gt;
*A blurred clone of the gr_title element, offset and behind it.&lt;br /&gt;
*Layers whose name starts with ''Instance'' - these are the layers the user can edit directly in Inkscape for a particular slide.&lt;br /&gt;
&lt;br /&gt;
When you save the template file you should make sure that layers containing place holder text (the fr_* elements) and bullets are invisible, or at least hidden below the background layer, which should be visible.  Likewise the Texts layer should be visible, as well as any Instance layers.&lt;br /&gt;
&lt;br /&gt;
=== Text Input ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%title Inkslide: an example&lt;br /&gt;
This text will appear as a top level paragraph.&lt;br /&gt;
    Indented by a single tab, this text will appear as a bulleted item.&lt;br /&gt;
        Two tabs -&amp;gt; second level bulleted list.&lt;br /&gt;
&lt;br /&gt;
A gap will be left for the preceeding line.&lt;br /&gt;
%pause&lt;br /&gt;
Two slides will be produced for this input, one with everything up to the %pause above,&lt;br /&gt;
and one with the remaining content.  Incremental list display can be done this way.&lt;br /&gt;
&lt;br /&gt;
The %id tag below indicates that a file with a name like 'extra/is_intro.svg' should&lt;br /&gt;
be inspected for extra content for this particular slide.&lt;br /&gt;
%id is_intro&lt;br /&gt;
%title Inkslide: the next slide...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== The GUI ===&lt;br /&gt;
&lt;br /&gt;
A GUI for InkSlide is available as a plug-in for the [http://webpages.charter.net/edreamleo/front.html Leo] outline editor.  This makes slide re-arranging easy, and adds some macro substitution capability.  It also adds buttons for editing slide specific content in Inkscape.&lt;br /&gt;
&lt;br /&gt;
FIXME: post plug-in on Leo wiki and link.&lt;br /&gt;
&lt;br /&gt;
=== To do / status ===&lt;br /&gt;
&lt;br /&gt;
InkSlide is very usable, but is missing some features:&lt;br /&gt;
&lt;br /&gt;
*Inline text formatting (bold, color, etc.) for part of a piece of text&lt;br /&gt;
*Justification switching&lt;br /&gt;
*Verbatim code display&lt;br /&gt;
*Simple image inclusion&lt;br /&gt;
**currently you can do this using the Inkscape editing features&lt;br /&gt;
*more helper tool to make PNG and PDF versions of slides&lt;br /&gt;
*configuring / using a directory for edited slides&lt;br /&gt;
*command line flags for generating specific slides&lt;br /&gt;
&lt;br /&gt;
Currently InkSlide is slow, because it must invoke Inkscape once for every single dimension it needs to retrieve.  I'll try and get a --query-all switch into 0.46.&lt;br /&gt;
&lt;br /&gt;
=== Bugs ===&lt;br /&gt;
&lt;br /&gt;
*There may be an issue with the way slide specific content is merged into the template producing conflicting @id attributes, but I haven't seen this happen yet.&lt;br /&gt;
&lt;br /&gt;
=== Author  / Credits ===&lt;br /&gt;
&lt;br /&gt;
InkSlide was written by Terry Brown, terry-n-brown@yahoo.com - but use underscore, not '-'.&lt;br /&gt;
&lt;br /&gt;
InkSlide was inspired by [http://member.wide.ad.jp/wg/mgp/ MagicPoint]&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# inkslide.py $Id$&lt;br /&gt;
# Author: Terry Brown&lt;br /&gt;
# Created: Thu Oct 11 2007&lt;br /&gt;
&lt;br /&gt;
import lxml.etree as ET&lt;br /&gt;
&lt;br /&gt;
import copy&lt;br /&gt;
import subprocess&lt;br /&gt;
import os.path&lt;br /&gt;
&lt;br /&gt;
NS = { 'i': 'http://www.inkscape.org/namespaces/inkscape',&lt;br /&gt;
       's': 'http://www.w3.org/2000/svg',&lt;br /&gt;
       'xlink' : 'http://www.w3.org/1999/xlink'}&lt;br /&gt;
&lt;br /&gt;
# number of defined tablevels, FIXME, could derive from template?&lt;br /&gt;
tabLevels = 4  &lt;br /&gt;
def clearId(x, what='id', NS={}):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;recursively clear @id on element x and descendants&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if what in x.keys():&lt;br /&gt;
        del x.attrib[what]&lt;br /&gt;
    for i in x.xpath('.//*[@%s]'%what, NS):&lt;br /&gt;
        del i.attrib[what]&lt;br /&gt;
    return x&lt;br /&gt;
def getDim(fn, Id, what):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return dimension of element in fn with @id Id, what is&lt;br /&gt;
    x, y, width, height&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cmd = ('inkscape', '--without-gui', '--query-id', Id, '--query-'+what, fn)&lt;br /&gt;
    proc = subprocess.Popen(cmd, stdout = subprocess.PIPE,&lt;br /&gt;
                            stderr = subprocess.PIPE)&lt;br /&gt;
    # make new pipe for stderr to supress chatter from inkscape&lt;br /&gt;
    proc.wait()&lt;br /&gt;
    return proc.stdout.read()&lt;br /&gt;
def textReader(text):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;iterate parts in input text&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not text: return&lt;br /&gt;
    for line in text.split('\n'):&lt;br /&gt;
        if line.startswith('%slide '):&lt;br /&gt;
            yield {'type':'TITLE', 's':line[7:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%pause'):&lt;br /&gt;
            yield {'type':'PAUSE'}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%id '):&lt;br /&gt;
            yield {'type':'ID', 's':line[4:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if not line.strip():&lt;br /&gt;
            yield {'type':'BLANK'}&lt;br /&gt;
            continue&lt;br /&gt;
        tabs = len(line) - len(line.lstrip('\t'))&lt;br /&gt;
        yield {'type':'TAB', 'tabs':tabs, 's':line.strip()}&lt;br /&gt;
&lt;br /&gt;
nid = 0&lt;br /&gt;
def nextId():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return an unsed Id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # FIXME, should inspect templat doc.&lt;br /&gt;
    global nid&lt;br /&gt;
    nid += 1&lt;br /&gt;
    return 'is'+str(nid)&lt;br /&gt;
&lt;br /&gt;
def instanceFromTemplate(template, instance, text, pauseOk = True):&lt;br /&gt;
&lt;br /&gt;
    slide = 0&lt;br /&gt;
&lt;br /&gt;
    hlist = []&lt;br /&gt;
    doc0 = ET.parse(template)&lt;br /&gt;
    slideId = None&lt;br /&gt;
    &lt;br /&gt;
    for t in textReader(text):&lt;br /&gt;
        &lt;br /&gt;
        if t['type'] == 'ID':&lt;br /&gt;
            slideId = t['s']&lt;br /&gt;
            continue&lt;br /&gt;
            &lt;br /&gt;
        if t['type'] == 'TITLE':&lt;br /&gt;
            print t['s']&lt;br /&gt;
            if hlist:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
                hlist = [t]&lt;br /&gt;
                doc0 = ET.parse(template)&lt;br /&gt;
                slideId = None&lt;br /&gt;
            else:&lt;br /&gt;
                hlist.append(t)&lt;br /&gt;
            dst = doc0.xpath('//s:g[@id=&amp;quot;gr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'PAUSE':&lt;br /&gt;
            if pauseOk:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        hlist.append(t)&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'BLANK':&lt;br /&gt;
            continue&lt;br /&gt;
        if t['type'] == 'TAB':&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_tab%d&amp;quot;]' % t['tabs'], NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            #break&lt;br /&gt;
            continue&lt;br /&gt;
           &lt;br /&gt;
    process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
def process(instance, doc0, hlist, slideId, slide):&lt;br /&gt;
&lt;br /&gt;
    instance = instance.replace('%s', '%04d'%slide)&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
&lt;br /&gt;
    bullDim = {}&lt;br /&gt;
    tabDim = {}&lt;br /&gt;
    for n in range(0,tabLevels):&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;bu_%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'width', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'bu_%d'%n, i))&lt;br /&gt;
            bullDim[n] = b&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;fr_tab%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'fr_tab%d'%n, i))&lt;br /&gt;
            tabDim[n] = b&lt;br /&gt;
&lt;br /&gt;
    delta = 0.&lt;br /&gt;
    firstVert = True&lt;br /&gt;
    for n, i in enumerate(hlist):&lt;br /&gt;
        if hlist[n]['type'] in ['TITLE']:&lt;br /&gt;
            continue  # already in right place, not a vertical element&lt;br /&gt;
&lt;br /&gt;
        hgt = 0&lt;br /&gt;
        if hlist[n-1]['type'] == 'BLANK':&lt;br /&gt;
            hgt = 30&lt;br /&gt;
        else:&lt;br /&gt;
            hgt = float(getDim(instance, hlist[n-1]['id'], 'height'))&lt;br /&gt;
&lt;br /&gt;
        hgt += 5&lt;br /&gt;
&lt;br /&gt;
        if firstVert:&lt;br /&gt;
            hgt = 0&lt;br /&gt;
            firstVert = False&lt;br /&gt;
&lt;br /&gt;
        delta += hgt&lt;br /&gt;
&lt;br /&gt;
        if hlist[n]['type'] == 'BLANK': continue&lt;br /&gt;
&lt;br /&gt;
        tabs = hlist[n]['tabs']&lt;br /&gt;
&lt;br /&gt;
        r = doc0.xpath('//s:flowRoot[@id=&amp;quot;%s&amp;quot;]' % hlist[n]['id'], NS)[0]&lt;br /&gt;
        clearId(r, what='transform')&lt;br /&gt;
        r = r.xpath('.//s:rect', NS)[0]&lt;br /&gt;
        r.set('x', str(tabDim[tabs]['x']))&lt;br /&gt;
        y = tabDim[tabs]['y'] + delta&lt;br /&gt;
        #print '%s -&amp;gt; %s' % (str(r.get('y')), str(y))&lt;br /&gt;
        #print tabDim[tabs]['y']&lt;br /&gt;
        r.set('y', str(y))&lt;br /&gt;
&lt;br /&gt;
        if (hlist[n]['type'] == 'TAB' and hlist[n]['tabs'] in bullDim&lt;br /&gt;
            and 'isDone' not in hlist[n]):&lt;br /&gt;
            hlist[n]['isDone'] = True&lt;br /&gt;
            dx = (tabDim[tabs]['x'] &lt;br /&gt;
                  - bullDim[tabs]['x']&lt;br /&gt;
                  - 1.5*bullDim[tabs]['width']&lt;br /&gt;
                  )&lt;br /&gt;
            dy = (y &lt;br /&gt;
                  + tabDim[tabs]['height'] / 2.&lt;br /&gt;
                  - bullDim[tabs]['y'] &lt;br /&gt;
                  - bullDim[tabs]['height'] / 2.&lt;br /&gt;
                  )&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            #print y, tabDim[tabs]['height'], bullDim[tabs]['y'], bullDim[tabs]['height'], dy&lt;br /&gt;
            clone = ET.Element('svg:use')&lt;br /&gt;
            clone.set('transform', 'translate(%f,%f)' % (dx,dy))&lt;br /&gt;
            clone.set('{%s}href'%NS['xlink'], '#bu_%d'%tabs)&lt;br /&gt;
            dst.append(clone)&lt;br /&gt;
            &lt;br /&gt;
            #path = ET.Element('svg:path')&lt;br /&gt;
            #path.set('d', ('M %f,%f' + ' L %f,%f'*5 + ' z') % (&lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y'], &lt;br /&gt;
            #    bullDim[tabs]['x']+20, bullDim[tabs]['y']+bullDim[tabs]['height']/2., &lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y']+bullDim[tabs]['height'], &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y'], &lt;br /&gt;
            #    tabDim[tabs]['x']+20, tabDim[tabs]['y']+tabDim[tabs]['height']/2., &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y']+tabDim[tabs]['height'], &lt;br /&gt;
            #    ))&lt;br /&gt;
            #dst.append(path)&lt;br /&gt;
            #break&lt;br /&gt;
&lt;br /&gt;
    # look for instance specific parts&lt;br /&gt;
    if slideId:&lt;br /&gt;
        f = instancePath(slideId)&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            comp = ET.parse(f)&lt;br /&gt;
            svg = doc0.xpath('//s:svg', NS)[0]&lt;br /&gt;
            g = &amp;quot;{%s}g&amp;quot;%NS['s']&lt;br /&gt;
            k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
            for n, i in enumerate(svg.getchildren()):&lt;br /&gt;
                if i.tag == g and i.get(k, '').startswith('Instance'):&lt;br /&gt;
                    x = comp.xpath('//s:svg//s:g[@i:label=&amp;quot;%s&amp;quot;]'%i.get(k), NS)&lt;br /&gt;
                    if x:&lt;br /&gt;
                        x = x[0]&lt;br /&gt;
                        cpy = clearId(copy.deepcopy(x))&lt;br /&gt;
                        svg[n] = cpy&lt;br /&gt;
            defs = comp.xpath('//s:svg/s:defs/*', NS)&lt;br /&gt;
            dst = doc0.xpath('//s:svg/s:defs', NS)[0]&lt;br /&gt;
            for d in defs:&lt;br /&gt;
                Id = d.get('id')&lt;br /&gt;
                current = doc0.xpath('//s:svg/s:defs/*[@id=&amp;quot;%s&amp;quot;]'%Id, NS)&lt;br /&gt;
                if not current:&lt;br /&gt;
                    cpy = copy.deepcopy(d)&lt;br /&gt;
                    dst.append(cpy)&lt;br /&gt;
&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
    &lt;br /&gt;
    return slideId&lt;br /&gt;
def instancePath(slideId, component = None):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return path to component for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # return os.path.join(slideId, component)&lt;br /&gt;
    return slideId+'.svg'&lt;br /&gt;
def writeComponents(inkscapeFile, slideId):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;write components from file for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not slideId: return&lt;br /&gt;
    doc0 = ET.parse(inkscapeFile)&lt;br /&gt;
    hasComponents = False&lt;br /&gt;
    for i in doc0.xpath('//s:svg//s:g', NS):&lt;br /&gt;
        k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
        # print i.keys(),k&lt;br /&gt;
        if (k in i.keys() and i.get(k).startswith('Instance')):&lt;br /&gt;
            if len(i) &amp;gt; 0:&lt;br /&gt;
                hasComponents = True&lt;br /&gt;
                break&lt;br /&gt;
                # ET.ElementTree(i).write(f)&lt;br /&gt;
                &lt;br /&gt;
    f = instancePath(slideId, 'components')&lt;br /&gt;
    &lt;br /&gt;
    if hasComponents:&lt;br /&gt;
        if (os.path.abspath(os.path.realpath(inkscapeFile)) != &lt;br /&gt;
            os.path.abspath(os.path.realpath(f))):&lt;br /&gt;
            file(f, 'w').write(file(inkscapeFile).read())&lt;br /&gt;
    else:&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            os.remove(f)&lt;br /&gt;
&lt;br /&gt;
if __name__ == '__main__':&lt;br /&gt;
    import sys&lt;br /&gt;
    instanceFromTemplate(sys.argv[1], 'slide%s.svg', file(sys.argv[2]).read())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16467</id>
		<title>InkSlide</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16467"/>
		<updated>2007-10-17T17:00:34Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: /* Download */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== InkSlide - quick and easy presentations using Inkscape ==&lt;br /&gt;
&lt;br /&gt;
InkSlide produces slides like this:&lt;br /&gt;
&lt;br /&gt;
[[Image:slide0004.jpg]]&lt;br /&gt;
&lt;br /&gt;
from simple text input like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%slide InkSlide: Features&lt;br /&gt;
Features include wrapped top level text and&lt;br /&gt;
    mulitple&lt;br /&gt;
        levels&lt;br /&gt;
            of wrapped bulleted lists with bullets and font&lt;br /&gt;
information taken from the template file.&lt;br /&gt;
&lt;br /&gt;
Slide specific content like this:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
which is updated when the template changes.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An Inkscape file is used as a template file to define the background, title position and font, fonts and positions for text at different levels of indentation, groups to be cloned and used as bullets, etc.&lt;br /&gt;
&lt;br /&gt;
Content specific to a particular slide can also be created in Inkscape, this content merged with the template and text input to make the final slide, so changes to the template after a particular slide is edited in Inkscape are included.&lt;br /&gt;
&lt;br /&gt;
=== Download ===&lt;br /&gt;
&lt;br /&gt;
Copy the [[InkSlide#Code|text below]] into a file called 'inkslide.py'.  (How do you attach a non-image file on this wiki?)&lt;br /&gt;
&lt;br /&gt;
Here is an [[Media:template0.svg|example template]].  Use Save Link As to stop your browser displaying the file instead of downloading it.&lt;br /&gt;
&lt;br /&gt;
=== Use ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python inkslide.py myTemplate.svg mySlides.txt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This creates a slide00xx.svg for each slide in your presentation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python inkslide.py --writeInstance &amp;lt;instanceId&amp;gt; myTemplate.svg mySlides.txt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Creates / updates the Inkscape file you can edit to add special content to this particular slide.   '''NOT IMPLEMENTED YET''' - the code exists, but not the command line hook.&lt;br /&gt;
&lt;br /&gt;
=== How it works ===&lt;br /&gt;
&lt;br /&gt;
First InkSlide parses the text input, something like the example at the top of this page.  It looks for flowed text boxes in the template file with special @id attributes like ''fr_title'' for the title, and ''fr_tab2'' for the second level of bulleted lists.  Copies of these elements are made and placed in a layer called 'Texts'.  At this point, the text is all piled up at the top of the page.&lt;br /&gt;
&lt;br /&gt;
InkSlide saves the file, and uses Inkscape's command line ''--query-height'' and friends to find out how high the wrapped text pieces are.  At it then repositions them properly, adds clones of the bullet markers, etc.  Bullet markers have special @id attributes like ''bu_1'' for the first level of bulleted list.&lt;br /&gt;
&lt;br /&gt;
Finally, InkSlide checks to see if there's an %id tag for the slide, and if so if there's a corresponding Inkscape SVG file with content for this particular slide. This content has to occur in layers whose name starts with ''Instance'', and these layers have to be present in the template file, but you can have more than one, so they can be above and below the regular content.  If such content is found, it's copied into the output slide.&lt;br /&gt;
&lt;br /&gt;
=== The Template File ===&lt;br /&gt;
&lt;br /&gt;
The template file is a regular Inkscape file.  If this list seems hard, just use the example template and modify it to suit your needs.&lt;br /&gt;
&lt;br /&gt;
Required contents:&lt;br /&gt;
&lt;br /&gt;
*A layer called ''Texts'', into which text will be placed.&lt;br /&gt;
*A flowed text frame with an @id of ''fr_title'' to define the font and position for the slide title.  This should contain text like ''Title goes here Sly'' because ''Sly'' includes letters which ascend and descend from the baseline to the greatest extent.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*A group with an @id of ''gr_title'' into which the title will be placed.  This makes it easier to do special effects on the title like shadows.&lt;br /&gt;
*Flowed text frames with an @id of ''fr_tabN'' for each N in 0, 1, 2, 3.  ''fr_tab0'' is used for top level non bulleted text.  These should also include ''Sly'' or similar.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*Objects or groups withan @id of ''bu_N'' for each N in 1, 2, 3, for the three levels of bulleted list.&lt;br /&gt;
&lt;br /&gt;
The fr_* elements define the font, font-size, color etc. for the different levels of text.  They also define the position and wrap width.  See the example template file for a possible arrangement.&lt;br /&gt;
&lt;br /&gt;
Optional contents:&lt;br /&gt;
&lt;br /&gt;
*A background&lt;br /&gt;
*Affiliation text and logos you want to appear on every slide&lt;br /&gt;
*A blurred clone of the gr_title element, offset and behind it.&lt;br /&gt;
*Layers whose name starts with ''Instance'' - these are the layers the user can edit directly in Inkscape for a particular slide.&lt;br /&gt;
&lt;br /&gt;
When you save the template file you should make sure that layers containing place holder text (the fr_* elements) and bullets are invisible, or at least hidden below the background layer, which should be visible.  Likewise the Texts layer should be visible, as well as any Instance layers.&lt;br /&gt;
&lt;br /&gt;
=== Text Input ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%title Inkslide: an example&lt;br /&gt;
This text will appear as a top level paragraph.&lt;br /&gt;
    Indented by a single tab, this text will appear as a bulleted item.&lt;br /&gt;
        Two tabs -&amp;gt; second level bulleted list.&lt;br /&gt;
&lt;br /&gt;
A gap will be left for the preceeding line.&lt;br /&gt;
%pause&lt;br /&gt;
Two slides will be produced for this input, one with everything up to the %pause above,&lt;br /&gt;
and one with the remaining content.  Incremental list display can be done this way.&lt;br /&gt;
&lt;br /&gt;
The %id tag below indicates that a file with a name like 'extra/is_intro.svg' should&lt;br /&gt;
be inspected for extra content for this particular slide.&lt;br /&gt;
%id is_intro&lt;br /&gt;
%title Inkslide: the next slide...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== The GUI ===&lt;br /&gt;
&lt;br /&gt;
A GUI for InkSlide is available as a plug-in for the [http://webpages.charter.net/edreamleo/front.html Leo] outline editor.  This makes slide re-arranging easy, and adds some macro substitution capability.  It also adds buttons for editing slide specific content in Inkscape.&lt;br /&gt;
&lt;br /&gt;
FIXME: post plug-in on Leo wiki and link.&lt;br /&gt;
&lt;br /&gt;
=== To do / status ===&lt;br /&gt;
&lt;br /&gt;
InkSlide is very usable, but is missing some features:&lt;br /&gt;
&lt;br /&gt;
*Inline text formatting (bold, color, etc.) for part of a piece of text&lt;br /&gt;
*Justification switching&lt;br /&gt;
*Verbatim code display&lt;br /&gt;
*Simple image inclusion&lt;br /&gt;
**currently you can do this using the Inkscape editing features&lt;br /&gt;
*more helper tool to make PNG and PDF versions of slides&lt;br /&gt;
*configuring / using a directory for edited slides&lt;br /&gt;
*command line flags for generating specific slides&lt;br /&gt;
&lt;br /&gt;
=== Bugs ===&lt;br /&gt;
&lt;br /&gt;
*There may be an issue with the way slide specific content is merged into the template producing conflicting @id attributes, but I haven't seen this happen yet.&lt;br /&gt;
&lt;br /&gt;
=== Author  / Credits ===&lt;br /&gt;
&lt;br /&gt;
InkSlide was written by Terry Brown, terry-n-brown@yahoo.com - but use underscore, not '-'.&lt;br /&gt;
&lt;br /&gt;
InkSlide was inspired by [http://member.wide.ad.jp/wg/mgp/ MagicPoint]&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# inkslide.py $Id$&lt;br /&gt;
# Author: Terry Brown&lt;br /&gt;
# Created: Thu Oct 11 2007&lt;br /&gt;
&lt;br /&gt;
import lxml.etree as ET&lt;br /&gt;
&lt;br /&gt;
import copy&lt;br /&gt;
import subprocess&lt;br /&gt;
import os.path&lt;br /&gt;
&lt;br /&gt;
NS = { 'i': 'http://www.inkscape.org/namespaces/inkscape',&lt;br /&gt;
       's': 'http://www.w3.org/2000/svg',&lt;br /&gt;
       'xlink' : 'http://www.w3.org/1999/xlink'}&lt;br /&gt;
&lt;br /&gt;
# number of defined tablevels, FIXME, could derive from template?&lt;br /&gt;
tabLevels = 4  &lt;br /&gt;
def clearId(x, what='id', NS={}):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;recursively clear @id on element x and descendants&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if what in x.keys():&lt;br /&gt;
        del x.attrib[what]&lt;br /&gt;
    for i in x.xpath('.//*[@%s]'%what, NS):&lt;br /&gt;
        del i.attrib[what]&lt;br /&gt;
    return x&lt;br /&gt;
def getDim(fn, Id, what):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return dimension of element in fn with @id Id, what is&lt;br /&gt;
    x, y, width, height&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cmd = ('inkscape', '--without-gui', '--query-id', Id, '--query-'+what, fn)&lt;br /&gt;
    proc = subprocess.Popen(cmd, stdout = subprocess.PIPE,&lt;br /&gt;
                            stderr = subprocess.PIPE)&lt;br /&gt;
    # make new pipe for stderr to supress chatter from inkscape&lt;br /&gt;
    proc.wait()&lt;br /&gt;
    return proc.stdout.read()&lt;br /&gt;
def textReader(text):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;iterate parts in input text&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not text: return&lt;br /&gt;
    for line in text.split('\n'):&lt;br /&gt;
        if line.startswith('%slide '):&lt;br /&gt;
            yield {'type':'TITLE', 's':line[7:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%pause'):&lt;br /&gt;
            yield {'type':'PAUSE'}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%id '):&lt;br /&gt;
            yield {'type':'ID', 's':line[4:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if not line.strip():&lt;br /&gt;
            yield {'type':'BLANK'}&lt;br /&gt;
            continue&lt;br /&gt;
        tabs = len(line) - len(line.lstrip('\t'))&lt;br /&gt;
        yield {'type':'TAB', 'tabs':tabs, 's':line.strip()}&lt;br /&gt;
&lt;br /&gt;
nid = 0&lt;br /&gt;
def nextId():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return an unsed Id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # FIXME, should inspect templat doc.&lt;br /&gt;
    global nid&lt;br /&gt;
    nid += 1&lt;br /&gt;
    return 'is'+str(nid)&lt;br /&gt;
&lt;br /&gt;
def instanceFromTemplate(template, instance, text, pauseOk = True):&lt;br /&gt;
&lt;br /&gt;
    slide = 0&lt;br /&gt;
&lt;br /&gt;
    hlist = []&lt;br /&gt;
    doc0 = ET.parse(template)&lt;br /&gt;
    slideId = None&lt;br /&gt;
    &lt;br /&gt;
    for t in textReader(text):&lt;br /&gt;
        &lt;br /&gt;
        if t['type'] == 'ID':&lt;br /&gt;
            slideId = t['s']&lt;br /&gt;
            continue&lt;br /&gt;
            &lt;br /&gt;
        if t['type'] == 'TITLE':&lt;br /&gt;
            print t['s']&lt;br /&gt;
            if hlist:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
                hlist = [t]&lt;br /&gt;
                doc0 = ET.parse(template)&lt;br /&gt;
                slideId = None&lt;br /&gt;
            else:&lt;br /&gt;
                hlist.append(t)&lt;br /&gt;
            dst = doc0.xpath('//s:g[@id=&amp;quot;gr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'PAUSE':&lt;br /&gt;
            if pauseOk:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        hlist.append(t)&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'BLANK':&lt;br /&gt;
            continue&lt;br /&gt;
        if t['type'] == 'TAB':&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_tab%d&amp;quot;]' % t['tabs'], NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            #break&lt;br /&gt;
            continue&lt;br /&gt;
           &lt;br /&gt;
    process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
def process(instance, doc0, hlist, slideId, slide):&lt;br /&gt;
&lt;br /&gt;
    instance = instance.replace('%s', '%04d'%slide)&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
&lt;br /&gt;
    bullDim = {}&lt;br /&gt;
    tabDim = {}&lt;br /&gt;
    for n in range(0,tabLevels):&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;bu_%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'width', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'bu_%d'%n, i))&lt;br /&gt;
            bullDim[n] = b&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;fr_tab%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'fr_tab%d'%n, i))&lt;br /&gt;
            tabDim[n] = b&lt;br /&gt;
&lt;br /&gt;
    delta = 0.&lt;br /&gt;
    firstVert = True&lt;br /&gt;
    for n, i in enumerate(hlist):&lt;br /&gt;
        if hlist[n]['type'] in ['TITLE']:&lt;br /&gt;
            continue  # already in right place, not a vertical element&lt;br /&gt;
&lt;br /&gt;
        hgt = 0&lt;br /&gt;
        if hlist[n-1]['type'] == 'BLANK':&lt;br /&gt;
            hgt = 30&lt;br /&gt;
        else:&lt;br /&gt;
            hgt = float(getDim(instance, hlist[n-1]['id'], 'height'))&lt;br /&gt;
&lt;br /&gt;
        hgt += 5&lt;br /&gt;
&lt;br /&gt;
        if firstVert:&lt;br /&gt;
            hgt = 0&lt;br /&gt;
            firstVert = False&lt;br /&gt;
&lt;br /&gt;
        delta += hgt&lt;br /&gt;
&lt;br /&gt;
        if hlist[n]['type'] == 'BLANK': continue&lt;br /&gt;
&lt;br /&gt;
        tabs = hlist[n]['tabs']&lt;br /&gt;
&lt;br /&gt;
        r = doc0.xpath('//s:flowRoot[@id=&amp;quot;%s&amp;quot;]' % hlist[n]['id'], NS)[0]&lt;br /&gt;
        clearId(r, what='transform')&lt;br /&gt;
        r = r.xpath('.//s:rect', NS)[0]&lt;br /&gt;
        r.set('x', str(tabDim[tabs]['x']))&lt;br /&gt;
        y = tabDim[tabs]['y'] + delta&lt;br /&gt;
        #print '%s -&amp;gt; %s' % (str(r.get('y')), str(y))&lt;br /&gt;
        #print tabDim[tabs]['y']&lt;br /&gt;
        r.set('y', str(y))&lt;br /&gt;
&lt;br /&gt;
        if (hlist[n]['type'] == 'TAB' and hlist[n]['tabs'] in bullDim&lt;br /&gt;
            and 'isDone' not in hlist[n]):&lt;br /&gt;
            hlist[n]['isDone'] = True&lt;br /&gt;
            dx = (tabDim[tabs]['x'] &lt;br /&gt;
                  - bullDim[tabs]['x']&lt;br /&gt;
                  - 1.5*bullDim[tabs]['width']&lt;br /&gt;
                  )&lt;br /&gt;
            dy = (y &lt;br /&gt;
                  + tabDim[tabs]['height'] / 2.&lt;br /&gt;
                  - bullDim[tabs]['y'] &lt;br /&gt;
                  - bullDim[tabs]['height'] / 2.&lt;br /&gt;
                  )&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            #print y, tabDim[tabs]['height'], bullDim[tabs]['y'], bullDim[tabs]['height'], dy&lt;br /&gt;
            clone = ET.Element('svg:use')&lt;br /&gt;
            clone.set('transform', 'translate(%f,%f)' % (dx,dy))&lt;br /&gt;
            clone.set('{%s}href'%NS['xlink'], '#bu_%d'%tabs)&lt;br /&gt;
            dst.append(clone)&lt;br /&gt;
            &lt;br /&gt;
            #path = ET.Element('svg:path')&lt;br /&gt;
            #path.set('d', ('M %f,%f' + ' L %f,%f'*5 + ' z') % (&lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y'], &lt;br /&gt;
            #    bullDim[tabs]['x']+20, bullDim[tabs]['y']+bullDim[tabs]['height']/2., &lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y']+bullDim[tabs]['height'], &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y'], &lt;br /&gt;
            #    tabDim[tabs]['x']+20, tabDim[tabs]['y']+tabDim[tabs]['height']/2., &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y']+tabDim[tabs]['height'], &lt;br /&gt;
            #    ))&lt;br /&gt;
            #dst.append(path)&lt;br /&gt;
            #break&lt;br /&gt;
&lt;br /&gt;
    # look for instance specific parts&lt;br /&gt;
    if slideId:&lt;br /&gt;
        f = instancePath(slideId)&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            comp = ET.parse(f)&lt;br /&gt;
            svg = doc0.xpath('//s:svg', NS)[0]&lt;br /&gt;
            g = &amp;quot;{%s}g&amp;quot;%NS['s']&lt;br /&gt;
            k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
            for n, i in enumerate(svg.getchildren()):&lt;br /&gt;
                if i.tag == g and i.get(k, '').startswith('Instance'):&lt;br /&gt;
                    x = comp.xpath('//s:svg//s:g[@i:label=&amp;quot;%s&amp;quot;]'%i.get(k), NS)&lt;br /&gt;
                    if x:&lt;br /&gt;
                        x = x[0]&lt;br /&gt;
                        cpy = clearId(copy.deepcopy(x))&lt;br /&gt;
                        svg[n] = cpy&lt;br /&gt;
            defs = comp.xpath('//s:svg/s:defs/*', NS)&lt;br /&gt;
            dst = doc0.xpath('//s:svg/s:defs', NS)[0]&lt;br /&gt;
            for d in defs:&lt;br /&gt;
                Id = d.get('id')&lt;br /&gt;
                current = doc0.xpath('//s:svg/s:defs/*[@id=&amp;quot;%s&amp;quot;]'%Id, NS)&lt;br /&gt;
                if not current:&lt;br /&gt;
                    cpy = copy.deepcopy(d)&lt;br /&gt;
                    dst.append(cpy)&lt;br /&gt;
&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
    &lt;br /&gt;
    return slideId&lt;br /&gt;
def instancePath(slideId, component = None):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return path to component for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # return os.path.join(slideId, component)&lt;br /&gt;
    return slideId+'.svg'&lt;br /&gt;
def writeComponents(inkscapeFile, slideId):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;write components from file for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not slideId: return&lt;br /&gt;
    doc0 = ET.parse(inkscapeFile)&lt;br /&gt;
    hasComponents = False&lt;br /&gt;
    for i in doc0.xpath('//s:svg//s:g', NS):&lt;br /&gt;
        k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
        # print i.keys(),k&lt;br /&gt;
        if (k in i.keys() and i.get(k).startswith('Instance')):&lt;br /&gt;
            if len(i) &amp;gt; 0:&lt;br /&gt;
                hasComponents = True&lt;br /&gt;
                break&lt;br /&gt;
                # ET.ElementTree(i).write(f)&lt;br /&gt;
                &lt;br /&gt;
    f = instancePath(slideId, 'components')&lt;br /&gt;
    &lt;br /&gt;
    if hasComponents:&lt;br /&gt;
        if (os.path.abspath(os.path.realpath(inkscapeFile)) != &lt;br /&gt;
            os.path.abspath(os.path.realpath(f))):&lt;br /&gt;
            file(f, 'w').write(file(inkscapeFile).read())&lt;br /&gt;
    else:&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            os.remove(f)&lt;br /&gt;
&lt;br /&gt;
if __name__ == '__main__':&lt;br /&gt;
    import sys&lt;br /&gt;
    instanceFromTemplate(sys.argv[1], 'slide%s.svg', file(sys.argv[2]).read())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=File:Template0.svg&amp;diff=16466</id>
		<title>File:Template0.svg</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=File:Template0.svg&amp;diff=16466"/>
		<updated>2007-10-17T16:57:54Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: Example template for InkSlide&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Example template for InkSlide&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16465</id>
		<title>InkSlide</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16465"/>
		<updated>2007-10-17T16:53:03Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: /* Download */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== InkSlide - quick and easy presentations using Inkscape ==&lt;br /&gt;
&lt;br /&gt;
InkSlide produces slides like this:&lt;br /&gt;
&lt;br /&gt;
[[Image:slide0004.jpg]]&lt;br /&gt;
&lt;br /&gt;
from simple text input like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%slide InkSlide: Features&lt;br /&gt;
Features include wrapped top level text and&lt;br /&gt;
    mulitple&lt;br /&gt;
        levels&lt;br /&gt;
            of wrapped bulleted lists with bullets and font&lt;br /&gt;
information taken from the template file.&lt;br /&gt;
&lt;br /&gt;
Slide specific content like this:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
which is updated when the template changes.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An Inkscape file is used as a template file to define the background, title position and font, fonts and positions for text at different levels of indentation, groups to be cloned and used as bullets, etc.&lt;br /&gt;
&lt;br /&gt;
Content specific to a particular slide can also be created in Inkscape, this content merged with the template and text input to make the final slide, so changes to the template after a particular slide is edited in Inkscape are included.&lt;br /&gt;
&lt;br /&gt;
=== Download ===&lt;br /&gt;
&lt;br /&gt;
Copy the [[InkSlide#Code|text below]] into a file called 'inkslide.py'.&lt;br /&gt;
&lt;br /&gt;
Here is an [[Media:template0.svg|example template]].&lt;br /&gt;
&lt;br /&gt;
=== Use ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python inkslide.py myTemplate.svg mySlides.txt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This creates a slide00xx.svg for each slide in your presentation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python inkslide.py --writeInstance &amp;lt;instanceId&amp;gt; myTemplate.svg mySlides.txt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Creates / updates the Inkscape file you can edit to add special content to this particular slide.   '''NOT IMPLEMENTED YET''' - the code exists, but not the command line hook.&lt;br /&gt;
&lt;br /&gt;
=== How it works ===&lt;br /&gt;
&lt;br /&gt;
First InkSlide parses the text input, something like the example at the top of this page.  It looks for flowed text boxes in the template file with special @id attributes like ''fr_title'' for the title, and ''fr_tab2'' for the second level of bulleted lists.  Copies of these elements are made and placed in a layer called 'Texts'.  At this point, the text is all piled up at the top of the page.&lt;br /&gt;
&lt;br /&gt;
InkSlide saves the file, and uses Inkscape's command line ''--query-height'' and friends to find out how high the wrapped text pieces are.  At it then repositions them properly, adds clones of the bullet markers, etc.  Bullet markers have special @id attributes like ''bu_1'' for the first level of bulleted list.&lt;br /&gt;
&lt;br /&gt;
Finally, InkSlide checks to see if there's an %id tag for the slide, and if so if there's a corresponding Inkscape SVG file with content for this particular slide. This content has to occur in layers whose name starts with ''Instance'', and these layers have to be present in the template file, but you can have more than one, so they can be above and below the regular content.  If such content is found, it's copied into the output slide.&lt;br /&gt;
&lt;br /&gt;
=== The Template File ===&lt;br /&gt;
&lt;br /&gt;
The template file is a regular Inkscape file.  If this list seems hard, just use the example template and modify it to suit your needs.&lt;br /&gt;
&lt;br /&gt;
Required contents:&lt;br /&gt;
&lt;br /&gt;
*A layer called ''Texts'', into which text will be placed.&lt;br /&gt;
*A flowed text frame with an @id of ''fr_title'' to define the font and position for the slide title.  This should contain text like ''Title goes here Sly'' because ''Sly'' includes letters which ascend and descend from the baseline to the greatest extent.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*A group with an @id of ''gr_title'' into which the title will be placed.  This makes it easier to do special effects on the title like shadows.&lt;br /&gt;
*Flowed text frames with an @id of ''fr_tabN'' for each N in 0, 1, 2, 3.  ''fr_tab0'' is used for top level non bulleted text.  These should also include ''Sly'' or similar.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*Objects or groups withan @id of ''bu_N'' for each N in 1, 2, 3, for the three levels of bulleted list.&lt;br /&gt;
&lt;br /&gt;
The fr_* elements define the font, font-size, color etc. for the different levels of text.  They also define the position and wrap width.  See the example template file for a possible arrangement.&lt;br /&gt;
&lt;br /&gt;
Optional contents:&lt;br /&gt;
&lt;br /&gt;
*A background&lt;br /&gt;
*Affiliation text and logos you want to appear on every slide&lt;br /&gt;
*A blurred clone of the gr_title element, offset and behind it.&lt;br /&gt;
*Layers whose name starts with ''Instance'' - these are the layers the user can edit directly in Inkscape for a particular slide.&lt;br /&gt;
&lt;br /&gt;
When you save the template file you should make sure that layers containing place holder text (the fr_* elements) and bullets are invisible, or at least hidden below the background layer, which should be visible.  Likewise the Texts layer should be visible, as well as any Instance layers.&lt;br /&gt;
&lt;br /&gt;
=== Text Input ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%title Inkslide: an example&lt;br /&gt;
This text will appear as a top level paragraph.&lt;br /&gt;
    Indented by a single tab, this text will appear as a bulleted item.&lt;br /&gt;
        Two tabs -&amp;gt; second level bulleted list.&lt;br /&gt;
&lt;br /&gt;
A gap will be left for the preceeding line.&lt;br /&gt;
%pause&lt;br /&gt;
Two slides will be produced for this input, one with everything up to the %pause above,&lt;br /&gt;
and one with the remaining content.  Incremental list display can be done this way.&lt;br /&gt;
&lt;br /&gt;
The %id tag below indicates that a file with a name like 'extra/is_intro.svg' should&lt;br /&gt;
be inspected for extra content for this particular slide.&lt;br /&gt;
%id is_intro&lt;br /&gt;
%title Inkslide: the next slide...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== The GUI ===&lt;br /&gt;
&lt;br /&gt;
A GUI for InkSlide is available as a plug-in for the [http://webpages.charter.net/edreamleo/front.html Leo] outline editor.  This makes slide re-arranging easy, and adds some macro substitution capability.  It also adds buttons for editing slide specific content in Inkscape.&lt;br /&gt;
&lt;br /&gt;
FIXME: post plug-in on Leo wiki and link.&lt;br /&gt;
&lt;br /&gt;
=== To do / status ===&lt;br /&gt;
&lt;br /&gt;
InkSlide is very usable, but is missing some features:&lt;br /&gt;
&lt;br /&gt;
*Inline text formatting (bold, color, etc.) for part of a piece of text&lt;br /&gt;
*Justification switching&lt;br /&gt;
*Verbatim code display&lt;br /&gt;
*Simple image inclusion&lt;br /&gt;
**currently you can do this using the Inkscape editing features&lt;br /&gt;
*more helper tool to make PNG and PDF versions of slides&lt;br /&gt;
*configuring / using a directory for edited slides&lt;br /&gt;
*command line flags for generating specific slides&lt;br /&gt;
&lt;br /&gt;
=== Bugs ===&lt;br /&gt;
&lt;br /&gt;
*There may be an issue with the way slide specific content is merged into the template producing conflicting @id attributes, but I haven't seen this happen yet.&lt;br /&gt;
&lt;br /&gt;
=== Author  / Credits ===&lt;br /&gt;
&lt;br /&gt;
InkSlide was written by Terry Brown, terry-n-brown@yahoo.com - but use underscore, not '-'.&lt;br /&gt;
&lt;br /&gt;
InkSlide was inspired by [http://member.wide.ad.jp/wg/mgp/ MagicPoint]&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# inkslide.py $Id$&lt;br /&gt;
# Author: Terry Brown&lt;br /&gt;
# Created: Thu Oct 11 2007&lt;br /&gt;
&lt;br /&gt;
import lxml.etree as ET&lt;br /&gt;
&lt;br /&gt;
import copy&lt;br /&gt;
import subprocess&lt;br /&gt;
import os.path&lt;br /&gt;
&lt;br /&gt;
NS = { 'i': 'http://www.inkscape.org/namespaces/inkscape',&lt;br /&gt;
       's': 'http://www.w3.org/2000/svg',&lt;br /&gt;
       'xlink' : 'http://www.w3.org/1999/xlink'}&lt;br /&gt;
&lt;br /&gt;
# number of defined tablevels, FIXME, could derive from template?&lt;br /&gt;
tabLevels = 4  &lt;br /&gt;
def clearId(x, what='id', NS={}):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;recursively clear @id on element x and descendants&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if what in x.keys():&lt;br /&gt;
        del x.attrib[what]&lt;br /&gt;
    for i in x.xpath('.//*[@%s]'%what, NS):&lt;br /&gt;
        del i.attrib[what]&lt;br /&gt;
    return x&lt;br /&gt;
def getDim(fn, Id, what):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return dimension of element in fn with @id Id, what is&lt;br /&gt;
    x, y, width, height&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cmd = ('inkscape', '--without-gui', '--query-id', Id, '--query-'+what, fn)&lt;br /&gt;
    proc = subprocess.Popen(cmd, stdout = subprocess.PIPE,&lt;br /&gt;
                            stderr = subprocess.PIPE)&lt;br /&gt;
    # make new pipe for stderr to supress chatter from inkscape&lt;br /&gt;
    proc.wait()&lt;br /&gt;
    return proc.stdout.read()&lt;br /&gt;
def textReader(text):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;iterate parts in input text&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not text: return&lt;br /&gt;
    for line in text.split('\n'):&lt;br /&gt;
        if line.startswith('%slide '):&lt;br /&gt;
            yield {'type':'TITLE', 's':line[7:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%pause'):&lt;br /&gt;
            yield {'type':'PAUSE'}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%id '):&lt;br /&gt;
            yield {'type':'ID', 's':line[4:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if not line.strip():&lt;br /&gt;
            yield {'type':'BLANK'}&lt;br /&gt;
            continue&lt;br /&gt;
        tabs = len(line) - len(line.lstrip('\t'))&lt;br /&gt;
        yield {'type':'TAB', 'tabs':tabs, 's':line.strip()}&lt;br /&gt;
&lt;br /&gt;
nid = 0&lt;br /&gt;
def nextId():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return an unsed Id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # FIXME, should inspect templat doc.&lt;br /&gt;
    global nid&lt;br /&gt;
    nid += 1&lt;br /&gt;
    return 'is'+str(nid)&lt;br /&gt;
&lt;br /&gt;
def instanceFromTemplate(template, instance, text, pauseOk = True):&lt;br /&gt;
&lt;br /&gt;
    slide = 0&lt;br /&gt;
&lt;br /&gt;
    hlist = []&lt;br /&gt;
    doc0 = ET.parse(template)&lt;br /&gt;
    slideId = None&lt;br /&gt;
    &lt;br /&gt;
    for t in textReader(text):&lt;br /&gt;
        &lt;br /&gt;
        if t['type'] == 'ID':&lt;br /&gt;
            slideId = t['s']&lt;br /&gt;
            continue&lt;br /&gt;
            &lt;br /&gt;
        if t['type'] == 'TITLE':&lt;br /&gt;
            print t['s']&lt;br /&gt;
            if hlist:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
                hlist = [t]&lt;br /&gt;
                doc0 = ET.parse(template)&lt;br /&gt;
                slideId = None&lt;br /&gt;
            else:&lt;br /&gt;
                hlist.append(t)&lt;br /&gt;
            dst = doc0.xpath('//s:g[@id=&amp;quot;gr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'PAUSE':&lt;br /&gt;
            if pauseOk:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        hlist.append(t)&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'BLANK':&lt;br /&gt;
            continue&lt;br /&gt;
        if t['type'] == 'TAB':&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_tab%d&amp;quot;]' % t['tabs'], NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            #break&lt;br /&gt;
            continue&lt;br /&gt;
           &lt;br /&gt;
    process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
def process(instance, doc0, hlist, slideId, slide):&lt;br /&gt;
&lt;br /&gt;
    instance = instance.replace('%s', '%04d'%slide)&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
&lt;br /&gt;
    bullDim = {}&lt;br /&gt;
    tabDim = {}&lt;br /&gt;
    for n in range(0,tabLevels):&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;bu_%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'width', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'bu_%d'%n, i))&lt;br /&gt;
            bullDim[n] = b&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;fr_tab%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'fr_tab%d'%n, i))&lt;br /&gt;
            tabDim[n] = b&lt;br /&gt;
&lt;br /&gt;
    delta = 0.&lt;br /&gt;
    firstVert = True&lt;br /&gt;
    for n, i in enumerate(hlist):&lt;br /&gt;
        if hlist[n]['type'] in ['TITLE']:&lt;br /&gt;
            continue  # already in right place, not a vertical element&lt;br /&gt;
&lt;br /&gt;
        hgt = 0&lt;br /&gt;
        if hlist[n-1]['type'] == 'BLANK':&lt;br /&gt;
            hgt = 30&lt;br /&gt;
        else:&lt;br /&gt;
            hgt = float(getDim(instance, hlist[n-1]['id'], 'height'))&lt;br /&gt;
&lt;br /&gt;
        hgt += 5&lt;br /&gt;
&lt;br /&gt;
        if firstVert:&lt;br /&gt;
            hgt = 0&lt;br /&gt;
            firstVert = False&lt;br /&gt;
&lt;br /&gt;
        delta += hgt&lt;br /&gt;
&lt;br /&gt;
        if hlist[n]['type'] == 'BLANK': continue&lt;br /&gt;
&lt;br /&gt;
        tabs = hlist[n]['tabs']&lt;br /&gt;
&lt;br /&gt;
        r = doc0.xpath('//s:flowRoot[@id=&amp;quot;%s&amp;quot;]' % hlist[n]['id'], NS)[0]&lt;br /&gt;
        clearId(r, what='transform')&lt;br /&gt;
        r = r.xpath('.//s:rect', NS)[0]&lt;br /&gt;
        r.set('x', str(tabDim[tabs]['x']))&lt;br /&gt;
        y = tabDim[tabs]['y'] + delta&lt;br /&gt;
        #print '%s -&amp;gt; %s' % (str(r.get('y')), str(y))&lt;br /&gt;
        #print tabDim[tabs]['y']&lt;br /&gt;
        r.set('y', str(y))&lt;br /&gt;
&lt;br /&gt;
        if (hlist[n]['type'] == 'TAB' and hlist[n]['tabs'] in bullDim&lt;br /&gt;
            and 'isDone' not in hlist[n]):&lt;br /&gt;
            hlist[n]['isDone'] = True&lt;br /&gt;
            dx = (tabDim[tabs]['x'] &lt;br /&gt;
                  - bullDim[tabs]['x']&lt;br /&gt;
                  - 1.5*bullDim[tabs]['width']&lt;br /&gt;
                  )&lt;br /&gt;
            dy = (y &lt;br /&gt;
                  + tabDim[tabs]['height'] / 2.&lt;br /&gt;
                  - bullDim[tabs]['y'] &lt;br /&gt;
                  - bullDim[tabs]['height'] / 2.&lt;br /&gt;
                  )&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            #print y, tabDim[tabs]['height'], bullDim[tabs]['y'], bullDim[tabs]['height'], dy&lt;br /&gt;
            clone = ET.Element('svg:use')&lt;br /&gt;
            clone.set('transform', 'translate(%f,%f)' % (dx,dy))&lt;br /&gt;
            clone.set('{%s}href'%NS['xlink'], '#bu_%d'%tabs)&lt;br /&gt;
            dst.append(clone)&lt;br /&gt;
            &lt;br /&gt;
            #path = ET.Element('svg:path')&lt;br /&gt;
            #path.set('d', ('M %f,%f' + ' L %f,%f'*5 + ' z') % (&lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y'], &lt;br /&gt;
            #    bullDim[tabs]['x']+20, bullDim[tabs]['y']+bullDim[tabs]['height']/2., &lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y']+bullDim[tabs]['height'], &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y'], &lt;br /&gt;
            #    tabDim[tabs]['x']+20, tabDim[tabs]['y']+tabDim[tabs]['height']/2., &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y']+tabDim[tabs]['height'], &lt;br /&gt;
            #    ))&lt;br /&gt;
            #dst.append(path)&lt;br /&gt;
            #break&lt;br /&gt;
&lt;br /&gt;
    # look for instance specific parts&lt;br /&gt;
    if slideId:&lt;br /&gt;
        f = instancePath(slideId)&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            comp = ET.parse(f)&lt;br /&gt;
            svg = doc0.xpath('//s:svg', NS)[0]&lt;br /&gt;
            g = &amp;quot;{%s}g&amp;quot;%NS['s']&lt;br /&gt;
            k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
            for n, i in enumerate(svg.getchildren()):&lt;br /&gt;
                if i.tag == g and i.get(k, '').startswith('Instance'):&lt;br /&gt;
                    x = comp.xpath('//s:svg//s:g[@i:label=&amp;quot;%s&amp;quot;]'%i.get(k), NS)&lt;br /&gt;
                    if x:&lt;br /&gt;
                        x = x[0]&lt;br /&gt;
                        cpy = clearId(copy.deepcopy(x))&lt;br /&gt;
                        svg[n] = cpy&lt;br /&gt;
            defs = comp.xpath('//s:svg/s:defs/*', NS)&lt;br /&gt;
            dst = doc0.xpath('//s:svg/s:defs', NS)[0]&lt;br /&gt;
            for d in defs:&lt;br /&gt;
                Id = d.get('id')&lt;br /&gt;
                current = doc0.xpath('//s:svg/s:defs/*[@id=&amp;quot;%s&amp;quot;]'%Id, NS)&lt;br /&gt;
                if not current:&lt;br /&gt;
                    cpy = copy.deepcopy(d)&lt;br /&gt;
                    dst.append(cpy)&lt;br /&gt;
&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
    &lt;br /&gt;
    return slideId&lt;br /&gt;
def instancePath(slideId, component = None):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return path to component for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # return os.path.join(slideId, component)&lt;br /&gt;
    return slideId+'.svg'&lt;br /&gt;
def writeComponents(inkscapeFile, slideId):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;write components from file for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not slideId: return&lt;br /&gt;
    doc0 = ET.parse(inkscapeFile)&lt;br /&gt;
    hasComponents = False&lt;br /&gt;
    for i in doc0.xpath('//s:svg//s:g', NS):&lt;br /&gt;
        k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
        # print i.keys(),k&lt;br /&gt;
        if (k in i.keys() and i.get(k).startswith('Instance')):&lt;br /&gt;
            if len(i) &amp;gt; 0:&lt;br /&gt;
                hasComponents = True&lt;br /&gt;
                break&lt;br /&gt;
                # ET.ElementTree(i).write(f)&lt;br /&gt;
                &lt;br /&gt;
    f = instancePath(slideId, 'components')&lt;br /&gt;
    &lt;br /&gt;
    if hasComponents:&lt;br /&gt;
        if (os.path.abspath(os.path.realpath(inkscapeFile)) != &lt;br /&gt;
            os.path.abspath(os.path.realpath(f))):&lt;br /&gt;
            file(f, 'w').write(file(inkscapeFile).read())&lt;br /&gt;
    else:&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            os.remove(f)&lt;br /&gt;
&lt;br /&gt;
if __name__ == '__main__':&lt;br /&gt;
    import sys&lt;br /&gt;
    instanceFromTemplate(sys.argv[1], 'slide%s.svg', file(sys.argv[2]).read())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16464</id>
		<title>InkSlide</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16464"/>
		<updated>2007-10-17T16:52:29Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: /* Download */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== InkSlide - quick and easy presentations using Inkscape ==&lt;br /&gt;
&lt;br /&gt;
InkSlide produces slides like this:&lt;br /&gt;
&lt;br /&gt;
[[Image:slide0004.jpg]]&lt;br /&gt;
&lt;br /&gt;
from simple text input like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%slide InkSlide: Features&lt;br /&gt;
Features include wrapped top level text and&lt;br /&gt;
    mulitple&lt;br /&gt;
        levels&lt;br /&gt;
            of wrapped bulleted lists with bullets and font&lt;br /&gt;
information taken from the template file.&lt;br /&gt;
&lt;br /&gt;
Slide specific content like this:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
which is updated when the template changes.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An Inkscape file is used as a template file to define the background, title position and font, fonts and positions for text at different levels of indentation, groups to be cloned and used as bullets, etc.&lt;br /&gt;
&lt;br /&gt;
Content specific to a particular slide can also be created in Inkscape, this content merged with the template and text input to make the final slide, so changes to the template after a particular slide is edited in Inkscape are included.&lt;br /&gt;
&lt;br /&gt;
=== Download ===&lt;br /&gt;
&lt;br /&gt;
Copy the [[InkSlide#Code|text below]] into a file called 'inkslide.py'.&lt;br /&gt;
&lt;br /&gt;
Here is an [[Media:template0.svg example template]]&lt;br /&gt;
&lt;br /&gt;
=== Use ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python inkslide.py myTemplate.svg mySlides.txt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This creates a slide00xx.svg for each slide in your presentation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python inkslide.py --writeInstance &amp;lt;instanceId&amp;gt; myTemplate.svg mySlides.txt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Creates / updates the Inkscape file you can edit to add special content to this particular slide.   '''NOT IMPLEMENTED YET''' - the code exists, but not the command line hook.&lt;br /&gt;
&lt;br /&gt;
=== How it works ===&lt;br /&gt;
&lt;br /&gt;
First InkSlide parses the text input, something like the example at the top of this page.  It looks for flowed text boxes in the template file with special @id attributes like ''fr_title'' for the title, and ''fr_tab2'' for the second level of bulleted lists.  Copies of these elements are made and placed in a layer called 'Texts'.  At this point, the text is all piled up at the top of the page.&lt;br /&gt;
&lt;br /&gt;
InkSlide saves the file, and uses Inkscape's command line ''--query-height'' and friends to find out how high the wrapped text pieces are.  At it then repositions them properly, adds clones of the bullet markers, etc.  Bullet markers have special @id attributes like ''bu_1'' for the first level of bulleted list.&lt;br /&gt;
&lt;br /&gt;
Finally, InkSlide checks to see if there's an %id tag for the slide, and if so if there's a corresponding Inkscape SVG file with content for this particular slide. This content has to occur in layers whose name starts with ''Instance'', and these layers have to be present in the template file, but you can have more than one, so they can be above and below the regular content.  If such content is found, it's copied into the output slide.&lt;br /&gt;
&lt;br /&gt;
=== The Template File ===&lt;br /&gt;
&lt;br /&gt;
The template file is a regular Inkscape file.  If this list seems hard, just use the example template and modify it to suit your needs.&lt;br /&gt;
&lt;br /&gt;
Required contents:&lt;br /&gt;
&lt;br /&gt;
*A layer called ''Texts'', into which text will be placed.&lt;br /&gt;
*A flowed text frame with an @id of ''fr_title'' to define the font and position for the slide title.  This should contain text like ''Title goes here Sly'' because ''Sly'' includes letters which ascend and descend from the baseline to the greatest extent.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*A group with an @id of ''gr_title'' into which the title will be placed.  This makes it easier to do special effects on the title like shadows.&lt;br /&gt;
*Flowed text frames with an @id of ''fr_tabN'' for each N in 0, 1, 2, 3.  ''fr_tab0'' is used for top level non bulleted text.  These should also include ''Sly'' or similar.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*Objects or groups withan @id of ''bu_N'' for each N in 1, 2, 3, for the three levels of bulleted list.&lt;br /&gt;
&lt;br /&gt;
The fr_* elements define the font, font-size, color etc. for the different levels of text.  They also define the position and wrap width.  See the example template file for a possible arrangement.&lt;br /&gt;
&lt;br /&gt;
Optional contents:&lt;br /&gt;
&lt;br /&gt;
*A background&lt;br /&gt;
*Affiliation text and logos you want to appear on every slide&lt;br /&gt;
*A blurred clone of the gr_title element, offset and behind it.&lt;br /&gt;
*Layers whose name starts with ''Instance'' - these are the layers the user can edit directly in Inkscape for a particular slide.&lt;br /&gt;
&lt;br /&gt;
When you save the template file you should make sure that layers containing place holder text (the fr_* elements) and bullets are invisible, or at least hidden below the background layer, which should be visible.  Likewise the Texts layer should be visible, as well as any Instance layers.&lt;br /&gt;
&lt;br /&gt;
=== Text Input ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%title Inkslide: an example&lt;br /&gt;
This text will appear as a top level paragraph.&lt;br /&gt;
    Indented by a single tab, this text will appear as a bulleted item.&lt;br /&gt;
        Two tabs -&amp;gt; second level bulleted list.&lt;br /&gt;
&lt;br /&gt;
A gap will be left for the preceeding line.&lt;br /&gt;
%pause&lt;br /&gt;
Two slides will be produced for this input, one with everything up to the %pause above,&lt;br /&gt;
and one with the remaining content.  Incremental list display can be done this way.&lt;br /&gt;
&lt;br /&gt;
The %id tag below indicates that a file with a name like 'extra/is_intro.svg' should&lt;br /&gt;
be inspected for extra content for this particular slide.&lt;br /&gt;
%id is_intro&lt;br /&gt;
%title Inkslide: the next slide...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== The GUI ===&lt;br /&gt;
&lt;br /&gt;
A GUI for InkSlide is available as a plug-in for the [http://webpages.charter.net/edreamleo/front.html Leo] outline editor.  This makes slide re-arranging easy, and adds some macro substitution capability.  It also adds buttons for editing slide specific content in Inkscape.&lt;br /&gt;
&lt;br /&gt;
FIXME: post plug-in on Leo wiki and link.&lt;br /&gt;
&lt;br /&gt;
=== To do / status ===&lt;br /&gt;
&lt;br /&gt;
InkSlide is very usable, but is missing some features:&lt;br /&gt;
&lt;br /&gt;
*Inline text formatting (bold, color, etc.) for part of a piece of text&lt;br /&gt;
*Justification switching&lt;br /&gt;
*Verbatim code display&lt;br /&gt;
*Simple image inclusion&lt;br /&gt;
**currently you can do this using the Inkscape editing features&lt;br /&gt;
*more helper tool to make PNG and PDF versions of slides&lt;br /&gt;
*configuring / using a directory for edited slides&lt;br /&gt;
*command line flags for generating specific slides&lt;br /&gt;
&lt;br /&gt;
=== Bugs ===&lt;br /&gt;
&lt;br /&gt;
*There may be an issue with the way slide specific content is merged into the template producing conflicting @id attributes, but I haven't seen this happen yet.&lt;br /&gt;
&lt;br /&gt;
=== Author  / Credits ===&lt;br /&gt;
&lt;br /&gt;
InkSlide was written by Terry Brown, terry-n-brown@yahoo.com - but use underscore, not '-'.&lt;br /&gt;
&lt;br /&gt;
InkSlide was inspired by [http://member.wide.ad.jp/wg/mgp/ MagicPoint]&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# inkslide.py $Id$&lt;br /&gt;
# Author: Terry Brown&lt;br /&gt;
# Created: Thu Oct 11 2007&lt;br /&gt;
&lt;br /&gt;
import lxml.etree as ET&lt;br /&gt;
&lt;br /&gt;
import copy&lt;br /&gt;
import subprocess&lt;br /&gt;
import os.path&lt;br /&gt;
&lt;br /&gt;
NS = { 'i': 'http://www.inkscape.org/namespaces/inkscape',&lt;br /&gt;
       's': 'http://www.w3.org/2000/svg',&lt;br /&gt;
       'xlink' : 'http://www.w3.org/1999/xlink'}&lt;br /&gt;
&lt;br /&gt;
# number of defined tablevels, FIXME, could derive from template?&lt;br /&gt;
tabLevels = 4  &lt;br /&gt;
def clearId(x, what='id', NS={}):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;recursively clear @id on element x and descendants&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if what in x.keys():&lt;br /&gt;
        del x.attrib[what]&lt;br /&gt;
    for i in x.xpath('.//*[@%s]'%what, NS):&lt;br /&gt;
        del i.attrib[what]&lt;br /&gt;
    return x&lt;br /&gt;
def getDim(fn, Id, what):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return dimension of element in fn with @id Id, what is&lt;br /&gt;
    x, y, width, height&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cmd = ('inkscape', '--without-gui', '--query-id', Id, '--query-'+what, fn)&lt;br /&gt;
    proc = subprocess.Popen(cmd, stdout = subprocess.PIPE,&lt;br /&gt;
                            stderr = subprocess.PIPE)&lt;br /&gt;
    # make new pipe for stderr to supress chatter from inkscape&lt;br /&gt;
    proc.wait()&lt;br /&gt;
    return proc.stdout.read()&lt;br /&gt;
def textReader(text):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;iterate parts in input text&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not text: return&lt;br /&gt;
    for line in text.split('\n'):&lt;br /&gt;
        if line.startswith('%slide '):&lt;br /&gt;
            yield {'type':'TITLE', 's':line[7:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%pause'):&lt;br /&gt;
            yield {'type':'PAUSE'}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%id '):&lt;br /&gt;
            yield {'type':'ID', 's':line[4:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if not line.strip():&lt;br /&gt;
            yield {'type':'BLANK'}&lt;br /&gt;
            continue&lt;br /&gt;
        tabs = len(line) - len(line.lstrip('\t'))&lt;br /&gt;
        yield {'type':'TAB', 'tabs':tabs, 's':line.strip()}&lt;br /&gt;
&lt;br /&gt;
nid = 0&lt;br /&gt;
def nextId():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return an unsed Id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # FIXME, should inspect templat doc.&lt;br /&gt;
    global nid&lt;br /&gt;
    nid += 1&lt;br /&gt;
    return 'is'+str(nid)&lt;br /&gt;
&lt;br /&gt;
def instanceFromTemplate(template, instance, text, pauseOk = True):&lt;br /&gt;
&lt;br /&gt;
    slide = 0&lt;br /&gt;
&lt;br /&gt;
    hlist = []&lt;br /&gt;
    doc0 = ET.parse(template)&lt;br /&gt;
    slideId = None&lt;br /&gt;
    &lt;br /&gt;
    for t in textReader(text):&lt;br /&gt;
        &lt;br /&gt;
        if t['type'] == 'ID':&lt;br /&gt;
            slideId = t['s']&lt;br /&gt;
            continue&lt;br /&gt;
            &lt;br /&gt;
        if t['type'] == 'TITLE':&lt;br /&gt;
            print t['s']&lt;br /&gt;
            if hlist:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
                hlist = [t]&lt;br /&gt;
                doc0 = ET.parse(template)&lt;br /&gt;
                slideId = None&lt;br /&gt;
            else:&lt;br /&gt;
                hlist.append(t)&lt;br /&gt;
            dst = doc0.xpath('//s:g[@id=&amp;quot;gr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'PAUSE':&lt;br /&gt;
            if pauseOk:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        hlist.append(t)&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'BLANK':&lt;br /&gt;
            continue&lt;br /&gt;
        if t['type'] == 'TAB':&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_tab%d&amp;quot;]' % t['tabs'], NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            #break&lt;br /&gt;
            continue&lt;br /&gt;
           &lt;br /&gt;
    process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
def process(instance, doc0, hlist, slideId, slide):&lt;br /&gt;
&lt;br /&gt;
    instance = instance.replace('%s', '%04d'%slide)&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
&lt;br /&gt;
    bullDim = {}&lt;br /&gt;
    tabDim = {}&lt;br /&gt;
    for n in range(0,tabLevels):&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;bu_%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'width', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'bu_%d'%n, i))&lt;br /&gt;
            bullDim[n] = b&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;fr_tab%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'fr_tab%d'%n, i))&lt;br /&gt;
            tabDim[n] = b&lt;br /&gt;
&lt;br /&gt;
    delta = 0.&lt;br /&gt;
    firstVert = True&lt;br /&gt;
    for n, i in enumerate(hlist):&lt;br /&gt;
        if hlist[n]['type'] in ['TITLE']:&lt;br /&gt;
            continue  # already in right place, not a vertical element&lt;br /&gt;
&lt;br /&gt;
        hgt = 0&lt;br /&gt;
        if hlist[n-1]['type'] == 'BLANK':&lt;br /&gt;
            hgt = 30&lt;br /&gt;
        else:&lt;br /&gt;
            hgt = float(getDim(instance, hlist[n-1]['id'], 'height'))&lt;br /&gt;
&lt;br /&gt;
        hgt += 5&lt;br /&gt;
&lt;br /&gt;
        if firstVert:&lt;br /&gt;
            hgt = 0&lt;br /&gt;
            firstVert = False&lt;br /&gt;
&lt;br /&gt;
        delta += hgt&lt;br /&gt;
&lt;br /&gt;
        if hlist[n]['type'] == 'BLANK': continue&lt;br /&gt;
&lt;br /&gt;
        tabs = hlist[n]['tabs']&lt;br /&gt;
&lt;br /&gt;
        r = doc0.xpath('//s:flowRoot[@id=&amp;quot;%s&amp;quot;]' % hlist[n]['id'], NS)[0]&lt;br /&gt;
        clearId(r, what='transform')&lt;br /&gt;
        r = r.xpath('.//s:rect', NS)[0]&lt;br /&gt;
        r.set('x', str(tabDim[tabs]['x']))&lt;br /&gt;
        y = tabDim[tabs]['y'] + delta&lt;br /&gt;
        #print '%s -&amp;gt; %s' % (str(r.get('y')), str(y))&lt;br /&gt;
        #print tabDim[tabs]['y']&lt;br /&gt;
        r.set('y', str(y))&lt;br /&gt;
&lt;br /&gt;
        if (hlist[n]['type'] == 'TAB' and hlist[n]['tabs'] in bullDim&lt;br /&gt;
            and 'isDone' not in hlist[n]):&lt;br /&gt;
            hlist[n]['isDone'] = True&lt;br /&gt;
            dx = (tabDim[tabs]['x'] &lt;br /&gt;
                  - bullDim[tabs]['x']&lt;br /&gt;
                  - 1.5*bullDim[tabs]['width']&lt;br /&gt;
                  )&lt;br /&gt;
            dy = (y &lt;br /&gt;
                  + tabDim[tabs]['height'] / 2.&lt;br /&gt;
                  - bullDim[tabs]['y'] &lt;br /&gt;
                  - bullDim[tabs]['height'] / 2.&lt;br /&gt;
                  )&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            #print y, tabDim[tabs]['height'], bullDim[tabs]['y'], bullDim[tabs]['height'], dy&lt;br /&gt;
            clone = ET.Element('svg:use')&lt;br /&gt;
            clone.set('transform', 'translate(%f,%f)' % (dx,dy))&lt;br /&gt;
            clone.set('{%s}href'%NS['xlink'], '#bu_%d'%tabs)&lt;br /&gt;
            dst.append(clone)&lt;br /&gt;
            &lt;br /&gt;
            #path = ET.Element('svg:path')&lt;br /&gt;
            #path.set('d', ('M %f,%f' + ' L %f,%f'*5 + ' z') % (&lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y'], &lt;br /&gt;
            #    bullDim[tabs]['x']+20, bullDim[tabs]['y']+bullDim[tabs]['height']/2., &lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y']+bullDim[tabs]['height'], &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y'], &lt;br /&gt;
            #    tabDim[tabs]['x']+20, tabDim[tabs]['y']+tabDim[tabs]['height']/2., &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y']+tabDim[tabs]['height'], &lt;br /&gt;
            #    ))&lt;br /&gt;
            #dst.append(path)&lt;br /&gt;
            #break&lt;br /&gt;
&lt;br /&gt;
    # look for instance specific parts&lt;br /&gt;
    if slideId:&lt;br /&gt;
        f = instancePath(slideId)&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            comp = ET.parse(f)&lt;br /&gt;
            svg = doc0.xpath('//s:svg', NS)[0]&lt;br /&gt;
            g = &amp;quot;{%s}g&amp;quot;%NS['s']&lt;br /&gt;
            k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
            for n, i in enumerate(svg.getchildren()):&lt;br /&gt;
                if i.tag == g and i.get(k, '').startswith('Instance'):&lt;br /&gt;
                    x = comp.xpath('//s:svg//s:g[@i:label=&amp;quot;%s&amp;quot;]'%i.get(k), NS)&lt;br /&gt;
                    if x:&lt;br /&gt;
                        x = x[0]&lt;br /&gt;
                        cpy = clearId(copy.deepcopy(x))&lt;br /&gt;
                        svg[n] = cpy&lt;br /&gt;
            defs = comp.xpath('//s:svg/s:defs/*', NS)&lt;br /&gt;
            dst = doc0.xpath('//s:svg/s:defs', NS)[0]&lt;br /&gt;
            for d in defs:&lt;br /&gt;
                Id = d.get('id')&lt;br /&gt;
                current = doc0.xpath('//s:svg/s:defs/*[@id=&amp;quot;%s&amp;quot;]'%Id, NS)&lt;br /&gt;
                if not current:&lt;br /&gt;
                    cpy = copy.deepcopy(d)&lt;br /&gt;
                    dst.append(cpy)&lt;br /&gt;
&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
    &lt;br /&gt;
    return slideId&lt;br /&gt;
def instancePath(slideId, component = None):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return path to component for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # return os.path.join(slideId, component)&lt;br /&gt;
    return slideId+'.svg'&lt;br /&gt;
def writeComponents(inkscapeFile, slideId):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;write components from file for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not slideId: return&lt;br /&gt;
    doc0 = ET.parse(inkscapeFile)&lt;br /&gt;
    hasComponents = False&lt;br /&gt;
    for i in doc0.xpath('//s:svg//s:g', NS):&lt;br /&gt;
        k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
        # print i.keys(),k&lt;br /&gt;
        if (k in i.keys() and i.get(k).startswith('Instance')):&lt;br /&gt;
            if len(i) &amp;gt; 0:&lt;br /&gt;
                hasComponents = True&lt;br /&gt;
                break&lt;br /&gt;
                # ET.ElementTree(i).write(f)&lt;br /&gt;
                &lt;br /&gt;
    f = instancePath(slideId, 'components')&lt;br /&gt;
    &lt;br /&gt;
    if hasComponents:&lt;br /&gt;
        if (os.path.abspath(os.path.realpath(inkscapeFile)) != &lt;br /&gt;
            os.path.abspath(os.path.realpath(f))):&lt;br /&gt;
            file(f, 'w').write(file(inkscapeFile).read())&lt;br /&gt;
    else:&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            os.remove(f)&lt;br /&gt;
&lt;br /&gt;
if __name__ == '__main__':&lt;br /&gt;
    import sys&lt;br /&gt;
    instanceFromTemplate(sys.argv[1], 'slide%s.svg', file(sys.argv[2]).read())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16463</id>
		<title>InkSlide</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16463"/>
		<updated>2007-10-17T16:51:38Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: /* Text Input */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== InkSlide - quick and easy presentations using Inkscape ==&lt;br /&gt;
&lt;br /&gt;
InkSlide produces slides like this:&lt;br /&gt;
&lt;br /&gt;
[[Image:slide0004.jpg]]&lt;br /&gt;
&lt;br /&gt;
from simple text input like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%slide InkSlide: Features&lt;br /&gt;
Features include wrapped top level text and&lt;br /&gt;
    mulitple&lt;br /&gt;
        levels&lt;br /&gt;
            of wrapped bulleted lists with bullets and font&lt;br /&gt;
information taken from the template file.&lt;br /&gt;
&lt;br /&gt;
Slide specific content like this:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
which is updated when the template changes.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An Inkscape file is used as a template file to define the background, title position and font, fonts and positions for text at different levels of indentation, groups to be cloned and used as bullets, etc.&lt;br /&gt;
&lt;br /&gt;
Content specific to a particular slide can also be created in Inkscape, this content merged with the template and text input to make the final slide, so changes to the template after a particular slide is edited in Inkscape are included.&lt;br /&gt;
&lt;br /&gt;
=== Download ===&lt;br /&gt;
&lt;br /&gt;
Copy the [[InkSlide#Code|text below]] into a file called 'inkslide.py'.&lt;br /&gt;
&lt;br /&gt;
=== Use ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python inkslide.py myTemplate.svg mySlides.txt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This creates a slide00xx.svg for each slide in your presentation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python inkslide.py --writeInstance &amp;lt;instanceId&amp;gt; myTemplate.svg mySlides.txt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Creates / updates the Inkscape file you can edit to add special content to this particular slide.   '''NOT IMPLEMENTED YET''' - the code exists, but not the command line hook.&lt;br /&gt;
&lt;br /&gt;
=== How it works ===&lt;br /&gt;
&lt;br /&gt;
First InkSlide parses the text input, something like the example at the top of this page.  It looks for flowed text boxes in the template file with special @id attributes like ''fr_title'' for the title, and ''fr_tab2'' for the second level of bulleted lists.  Copies of these elements are made and placed in a layer called 'Texts'.  At this point, the text is all piled up at the top of the page.&lt;br /&gt;
&lt;br /&gt;
InkSlide saves the file, and uses Inkscape's command line ''--query-height'' and friends to find out how high the wrapped text pieces are.  At it then repositions them properly, adds clones of the bullet markers, etc.  Bullet markers have special @id attributes like ''bu_1'' for the first level of bulleted list.&lt;br /&gt;
&lt;br /&gt;
Finally, InkSlide checks to see if there's an %id tag for the slide, and if so if there's a corresponding Inkscape SVG file with content for this particular slide. This content has to occur in layers whose name starts with ''Instance'', and these layers have to be present in the template file, but you can have more than one, so they can be above and below the regular content.  If such content is found, it's copied into the output slide.&lt;br /&gt;
&lt;br /&gt;
=== The Template File ===&lt;br /&gt;
&lt;br /&gt;
The template file is a regular Inkscape file.  If this list seems hard, just use the example template and modify it to suit your needs.&lt;br /&gt;
&lt;br /&gt;
Required contents:&lt;br /&gt;
&lt;br /&gt;
*A layer called ''Texts'', into which text will be placed.&lt;br /&gt;
*A flowed text frame with an @id of ''fr_title'' to define the font and position for the slide title.  This should contain text like ''Title goes here Sly'' because ''Sly'' includes letters which ascend and descend from the baseline to the greatest extent.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*A group with an @id of ''gr_title'' into which the title will be placed.  This makes it easier to do special effects on the title like shadows.&lt;br /&gt;
*Flowed text frames with an @id of ''fr_tabN'' for each N in 0, 1, 2, 3.  ''fr_tab0'' is used for top level non bulleted text.  These should also include ''Sly'' or similar.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*Objects or groups withan @id of ''bu_N'' for each N in 1, 2, 3, for the three levels of bulleted list.&lt;br /&gt;
&lt;br /&gt;
The fr_* elements define the font, font-size, color etc. for the different levels of text.  They also define the position and wrap width.  See the example template file for a possible arrangement.&lt;br /&gt;
&lt;br /&gt;
Optional contents:&lt;br /&gt;
&lt;br /&gt;
*A background&lt;br /&gt;
*Affiliation text and logos you want to appear on every slide&lt;br /&gt;
*A blurred clone of the gr_title element, offset and behind it.&lt;br /&gt;
*Layers whose name starts with ''Instance'' - these are the layers the user can edit directly in Inkscape for a particular slide.&lt;br /&gt;
&lt;br /&gt;
When you save the template file you should make sure that layers containing place holder text (the fr_* elements) and bullets are invisible, or at least hidden below the background layer, which should be visible.  Likewise the Texts layer should be visible, as well as any Instance layers.&lt;br /&gt;
&lt;br /&gt;
=== Text Input ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%title Inkslide: an example&lt;br /&gt;
This text will appear as a top level paragraph.&lt;br /&gt;
    Indented by a single tab, this text will appear as a bulleted item.&lt;br /&gt;
        Two tabs -&amp;gt; second level bulleted list.&lt;br /&gt;
&lt;br /&gt;
A gap will be left for the preceeding line.&lt;br /&gt;
%pause&lt;br /&gt;
Two slides will be produced for this input, one with everything up to the %pause above,&lt;br /&gt;
and one with the remaining content.  Incremental list display can be done this way.&lt;br /&gt;
&lt;br /&gt;
The %id tag below indicates that a file with a name like 'extra/is_intro.svg' should&lt;br /&gt;
be inspected for extra content for this particular slide.&lt;br /&gt;
%id is_intro&lt;br /&gt;
%title Inkslide: the next slide...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== The GUI ===&lt;br /&gt;
&lt;br /&gt;
A GUI for InkSlide is available as a plug-in for the [http://webpages.charter.net/edreamleo/front.html Leo] outline editor.  This makes slide re-arranging easy, and adds some macro substitution capability.  It also adds buttons for editing slide specific content in Inkscape.&lt;br /&gt;
&lt;br /&gt;
FIXME: post plug-in on Leo wiki and link.&lt;br /&gt;
&lt;br /&gt;
=== To do / status ===&lt;br /&gt;
&lt;br /&gt;
InkSlide is very usable, but is missing some features:&lt;br /&gt;
&lt;br /&gt;
*Inline text formatting (bold, color, etc.) for part of a piece of text&lt;br /&gt;
*Justification switching&lt;br /&gt;
*Verbatim code display&lt;br /&gt;
*Simple image inclusion&lt;br /&gt;
**currently you can do this using the Inkscape editing features&lt;br /&gt;
*more helper tool to make PNG and PDF versions of slides&lt;br /&gt;
*configuring / using a directory for edited slides&lt;br /&gt;
*command line flags for generating specific slides&lt;br /&gt;
&lt;br /&gt;
=== Bugs ===&lt;br /&gt;
&lt;br /&gt;
*There may be an issue with the way slide specific content is merged into the template producing conflicting @id attributes, but I haven't seen this happen yet.&lt;br /&gt;
&lt;br /&gt;
=== Author  / Credits ===&lt;br /&gt;
&lt;br /&gt;
InkSlide was written by Terry Brown, terry-n-brown@yahoo.com - but use underscore, not '-'.&lt;br /&gt;
&lt;br /&gt;
InkSlide was inspired by [http://member.wide.ad.jp/wg/mgp/ MagicPoint]&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# inkslide.py $Id$&lt;br /&gt;
# Author: Terry Brown&lt;br /&gt;
# Created: Thu Oct 11 2007&lt;br /&gt;
&lt;br /&gt;
import lxml.etree as ET&lt;br /&gt;
&lt;br /&gt;
import copy&lt;br /&gt;
import subprocess&lt;br /&gt;
import os.path&lt;br /&gt;
&lt;br /&gt;
NS = { 'i': 'http://www.inkscape.org/namespaces/inkscape',&lt;br /&gt;
       's': 'http://www.w3.org/2000/svg',&lt;br /&gt;
       'xlink' : 'http://www.w3.org/1999/xlink'}&lt;br /&gt;
&lt;br /&gt;
# number of defined tablevels, FIXME, could derive from template?&lt;br /&gt;
tabLevels = 4  &lt;br /&gt;
def clearId(x, what='id', NS={}):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;recursively clear @id on element x and descendants&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if what in x.keys():&lt;br /&gt;
        del x.attrib[what]&lt;br /&gt;
    for i in x.xpath('.//*[@%s]'%what, NS):&lt;br /&gt;
        del i.attrib[what]&lt;br /&gt;
    return x&lt;br /&gt;
def getDim(fn, Id, what):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return dimension of element in fn with @id Id, what is&lt;br /&gt;
    x, y, width, height&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cmd = ('inkscape', '--without-gui', '--query-id', Id, '--query-'+what, fn)&lt;br /&gt;
    proc = subprocess.Popen(cmd, stdout = subprocess.PIPE,&lt;br /&gt;
                            stderr = subprocess.PIPE)&lt;br /&gt;
    # make new pipe for stderr to supress chatter from inkscape&lt;br /&gt;
    proc.wait()&lt;br /&gt;
    return proc.stdout.read()&lt;br /&gt;
def textReader(text):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;iterate parts in input text&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not text: return&lt;br /&gt;
    for line in text.split('\n'):&lt;br /&gt;
        if line.startswith('%slide '):&lt;br /&gt;
            yield {'type':'TITLE', 's':line[7:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%pause'):&lt;br /&gt;
            yield {'type':'PAUSE'}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%id '):&lt;br /&gt;
            yield {'type':'ID', 's':line[4:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if not line.strip():&lt;br /&gt;
            yield {'type':'BLANK'}&lt;br /&gt;
            continue&lt;br /&gt;
        tabs = len(line) - len(line.lstrip('\t'))&lt;br /&gt;
        yield {'type':'TAB', 'tabs':tabs, 's':line.strip()}&lt;br /&gt;
&lt;br /&gt;
nid = 0&lt;br /&gt;
def nextId():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return an unsed Id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # FIXME, should inspect templat doc.&lt;br /&gt;
    global nid&lt;br /&gt;
    nid += 1&lt;br /&gt;
    return 'is'+str(nid)&lt;br /&gt;
&lt;br /&gt;
def instanceFromTemplate(template, instance, text, pauseOk = True):&lt;br /&gt;
&lt;br /&gt;
    slide = 0&lt;br /&gt;
&lt;br /&gt;
    hlist = []&lt;br /&gt;
    doc0 = ET.parse(template)&lt;br /&gt;
    slideId = None&lt;br /&gt;
    &lt;br /&gt;
    for t in textReader(text):&lt;br /&gt;
        &lt;br /&gt;
        if t['type'] == 'ID':&lt;br /&gt;
            slideId = t['s']&lt;br /&gt;
            continue&lt;br /&gt;
            &lt;br /&gt;
        if t['type'] == 'TITLE':&lt;br /&gt;
            print t['s']&lt;br /&gt;
            if hlist:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
                hlist = [t]&lt;br /&gt;
                doc0 = ET.parse(template)&lt;br /&gt;
                slideId = None&lt;br /&gt;
            else:&lt;br /&gt;
                hlist.append(t)&lt;br /&gt;
            dst = doc0.xpath('//s:g[@id=&amp;quot;gr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'PAUSE':&lt;br /&gt;
            if pauseOk:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        hlist.append(t)&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'BLANK':&lt;br /&gt;
            continue&lt;br /&gt;
        if t['type'] == 'TAB':&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_tab%d&amp;quot;]' % t['tabs'], NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            #break&lt;br /&gt;
            continue&lt;br /&gt;
           &lt;br /&gt;
    process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
def process(instance, doc0, hlist, slideId, slide):&lt;br /&gt;
&lt;br /&gt;
    instance = instance.replace('%s', '%04d'%slide)&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
&lt;br /&gt;
    bullDim = {}&lt;br /&gt;
    tabDim = {}&lt;br /&gt;
    for n in range(0,tabLevels):&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;bu_%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'width', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'bu_%d'%n, i))&lt;br /&gt;
            bullDim[n] = b&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;fr_tab%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'fr_tab%d'%n, i))&lt;br /&gt;
            tabDim[n] = b&lt;br /&gt;
&lt;br /&gt;
    delta = 0.&lt;br /&gt;
    firstVert = True&lt;br /&gt;
    for n, i in enumerate(hlist):&lt;br /&gt;
        if hlist[n]['type'] in ['TITLE']:&lt;br /&gt;
            continue  # already in right place, not a vertical element&lt;br /&gt;
&lt;br /&gt;
        hgt = 0&lt;br /&gt;
        if hlist[n-1]['type'] == 'BLANK':&lt;br /&gt;
            hgt = 30&lt;br /&gt;
        else:&lt;br /&gt;
            hgt = float(getDim(instance, hlist[n-1]['id'], 'height'))&lt;br /&gt;
&lt;br /&gt;
        hgt += 5&lt;br /&gt;
&lt;br /&gt;
        if firstVert:&lt;br /&gt;
            hgt = 0&lt;br /&gt;
            firstVert = False&lt;br /&gt;
&lt;br /&gt;
        delta += hgt&lt;br /&gt;
&lt;br /&gt;
        if hlist[n]['type'] == 'BLANK': continue&lt;br /&gt;
&lt;br /&gt;
        tabs = hlist[n]['tabs']&lt;br /&gt;
&lt;br /&gt;
        r = doc0.xpath('//s:flowRoot[@id=&amp;quot;%s&amp;quot;]' % hlist[n]['id'], NS)[0]&lt;br /&gt;
        clearId(r, what='transform')&lt;br /&gt;
        r = r.xpath('.//s:rect', NS)[0]&lt;br /&gt;
        r.set('x', str(tabDim[tabs]['x']))&lt;br /&gt;
        y = tabDim[tabs]['y'] + delta&lt;br /&gt;
        #print '%s -&amp;gt; %s' % (str(r.get('y')), str(y))&lt;br /&gt;
        #print tabDim[tabs]['y']&lt;br /&gt;
        r.set('y', str(y))&lt;br /&gt;
&lt;br /&gt;
        if (hlist[n]['type'] == 'TAB' and hlist[n]['tabs'] in bullDim&lt;br /&gt;
            and 'isDone' not in hlist[n]):&lt;br /&gt;
            hlist[n]['isDone'] = True&lt;br /&gt;
            dx = (tabDim[tabs]['x'] &lt;br /&gt;
                  - bullDim[tabs]['x']&lt;br /&gt;
                  - 1.5*bullDim[tabs]['width']&lt;br /&gt;
                  )&lt;br /&gt;
            dy = (y &lt;br /&gt;
                  + tabDim[tabs]['height'] / 2.&lt;br /&gt;
                  - bullDim[tabs]['y'] &lt;br /&gt;
                  - bullDim[tabs]['height'] / 2.&lt;br /&gt;
                  )&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            #print y, tabDim[tabs]['height'], bullDim[tabs]['y'], bullDim[tabs]['height'], dy&lt;br /&gt;
            clone = ET.Element('svg:use')&lt;br /&gt;
            clone.set('transform', 'translate(%f,%f)' % (dx,dy))&lt;br /&gt;
            clone.set('{%s}href'%NS['xlink'], '#bu_%d'%tabs)&lt;br /&gt;
            dst.append(clone)&lt;br /&gt;
            &lt;br /&gt;
            #path = ET.Element('svg:path')&lt;br /&gt;
            #path.set('d', ('M %f,%f' + ' L %f,%f'*5 + ' z') % (&lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y'], &lt;br /&gt;
            #    bullDim[tabs]['x']+20, bullDim[tabs]['y']+bullDim[tabs]['height']/2., &lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y']+bullDim[tabs]['height'], &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y'], &lt;br /&gt;
            #    tabDim[tabs]['x']+20, tabDim[tabs]['y']+tabDim[tabs]['height']/2., &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y']+tabDim[tabs]['height'], &lt;br /&gt;
            #    ))&lt;br /&gt;
            #dst.append(path)&lt;br /&gt;
            #break&lt;br /&gt;
&lt;br /&gt;
    # look for instance specific parts&lt;br /&gt;
    if slideId:&lt;br /&gt;
        f = instancePath(slideId)&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            comp = ET.parse(f)&lt;br /&gt;
            svg = doc0.xpath('//s:svg', NS)[0]&lt;br /&gt;
            g = &amp;quot;{%s}g&amp;quot;%NS['s']&lt;br /&gt;
            k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
            for n, i in enumerate(svg.getchildren()):&lt;br /&gt;
                if i.tag == g and i.get(k, '').startswith('Instance'):&lt;br /&gt;
                    x = comp.xpath('//s:svg//s:g[@i:label=&amp;quot;%s&amp;quot;]'%i.get(k), NS)&lt;br /&gt;
                    if x:&lt;br /&gt;
                        x = x[0]&lt;br /&gt;
                        cpy = clearId(copy.deepcopy(x))&lt;br /&gt;
                        svg[n] = cpy&lt;br /&gt;
            defs = comp.xpath('//s:svg/s:defs/*', NS)&lt;br /&gt;
            dst = doc0.xpath('//s:svg/s:defs', NS)[0]&lt;br /&gt;
            for d in defs:&lt;br /&gt;
                Id = d.get('id')&lt;br /&gt;
                current = doc0.xpath('//s:svg/s:defs/*[@id=&amp;quot;%s&amp;quot;]'%Id, NS)&lt;br /&gt;
                if not current:&lt;br /&gt;
                    cpy = copy.deepcopy(d)&lt;br /&gt;
                    dst.append(cpy)&lt;br /&gt;
&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
    &lt;br /&gt;
    return slideId&lt;br /&gt;
def instancePath(slideId, component = None):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return path to component for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # return os.path.join(slideId, component)&lt;br /&gt;
    return slideId+'.svg'&lt;br /&gt;
def writeComponents(inkscapeFile, slideId):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;write components from file for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not slideId: return&lt;br /&gt;
    doc0 = ET.parse(inkscapeFile)&lt;br /&gt;
    hasComponents = False&lt;br /&gt;
    for i in doc0.xpath('//s:svg//s:g', NS):&lt;br /&gt;
        k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
        # print i.keys(),k&lt;br /&gt;
        if (k in i.keys() and i.get(k).startswith('Instance')):&lt;br /&gt;
            if len(i) &amp;gt; 0:&lt;br /&gt;
                hasComponents = True&lt;br /&gt;
                break&lt;br /&gt;
                # ET.ElementTree(i).write(f)&lt;br /&gt;
                &lt;br /&gt;
    f = instancePath(slideId, 'components')&lt;br /&gt;
    &lt;br /&gt;
    if hasComponents:&lt;br /&gt;
        if (os.path.abspath(os.path.realpath(inkscapeFile)) != &lt;br /&gt;
            os.path.abspath(os.path.realpath(f))):&lt;br /&gt;
            file(f, 'w').write(file(inkscapeFile).read())&lt;br /&gt;
    else:&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            os.remove(f)&lt;br /&gt;
&lt;br /&gt;
if __name__ == '__main__':&lt;br /&gt;
    import sys&lt;br /&gt;
    instanceFromTemplate(sys.argv[1], 'slide%s.svg', file(sys.argv[2]).read())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16462</id>
		<title>InkSlide</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16462"/>
		<updated>2007-10-17T16:50:32Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: /* The Template File */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== InkSlide - quick and easy presentations using Inkscape ==&lt;br /&gt;
&lt;br /&gt;
InkSlide produces slides like this:&lt;br /&gt;
&lt;br /&gt;
[[Image:slide0004.jpg]]&lt;br /&gt;
&lt;br /&gt;
from simple text input like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%slide InkSlide: Features&lt;br /&gt;
Features include wrapped top level text and&lt;br /&gt;
    mulitple&lt;br /&gt;
        levels&lt;br /&gt;
            of wrapped bulleted lists with bullets and font&lt;br /&gt;
information taken from the template file.&lt;br /&gt;
&lt;br /&gt;
Slide specific content like this:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
which is updated when the template changes.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An Inkscape file is used as a template file to define the background, title position and font, fonts and positions for text at different levels of indentation, groups to be cloned and used as bullets, etc.&lt;br /&gt;
&lt;br /&gt;
Content specific to a particular slide can also be created in Inkscape, this content merged with the template and text input to make the final slide, so changes to the template after a particular slide is edited in Inkscape are included.&lt;br /&gt;
&lt;br /&gt;
=== Download ===&lt;br /&gt;
&lt;br /&gt;
Copy the [[InkSlide#Code|text below]] into a file called 'inkslide.py'.&lt;br /&gt;
&lt;br /&gt;
=== Use ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python inkslide.py myTemplate.svg mySlides.txt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This creates a slide00xx.svg for each slide in your presentation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python inkslide.py --writeInstance &amp;lt;instanceId&amp;gt; myTemplate.svg mySlides.txt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Creates / updates the Inkscape file you can edit to add special content to this particular slide.   '''NOT IMPLEMENTED YET''' - the code exists, but not the command line hook.&lt;br /&gt;
&lt;br /&gt;
=== How it works ===&lt;br /&gt;
&lt;br /&gt;
First InkSlide parses the text input, something like the example at the top of this page.  It looks for flowed text boxes in the template file with special @id attributes like ''fr_title'' for the title, and ''fr_tab2'' for the second level of bulleted lists.  Copies of these elements are made and placed in a layer called 'Texts'.  At this point, the text is all piled up at the top of the page.&lt;br /&gt;
&lt;br /&gt;
InkSlide saves the file, and uses Inkscape's command line ''--query-height'' and friends to find out how high the wrapped text pieces are.  At it then repositions them properly, adds clones of the bullet markers, etc.  Bullet markers have special @id attributes like ''bu_1'' for the first level of bulleted list.&lt;br /&gt;
&lt;br /&gt;
Finally, InkSlide checks to see if there's an %id tag for the slide, and if so if there's a corresponding Inkscape SVG file with content for this particular slide. This content has to occur in layers whose name starts with ''Instance'', and these layers have to be present in the template file, but you can have more than one, so they can be above and below the regular content.  If such content is found, it's copied into the output slide.&lt;br /&gt;
&lt;br /&gt;
=== The Template File ===&lt;br /&gt;
&lt;br /&gt;
The template file is a regular Inkscape file.  If this list seems hard, just use the example template and modify it to suit your needs.&lt;br /&gt;
&lt;br /&gt;
Required contents:&lt;br /&gt;
&lt;br /&gt;
*A layer called ''Texts'', into which text will be placed.&lt;br /&gt;
*A flowed text frame with an @id of ''fr_title'' to define the font and position for the slide title.  This should contain text like ''Title goes here Sly'' because ''Sly'' includes letters which ascend and descend from the baseline to the greatest extent.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*A group with an @id of ''gr_title'' into which the title will be placed.  This makes it easier to do special effects on the title like shadows.&lt;br /&gt;
*Flowed text frames with an @id of ''fr_tabN'' for each N in 0, 1, 2, 3.  ''fr_tab0'' is used for top level non bulleted text.  These should also include ''Sly'' or similar.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*Objects or groups withan @id of ''bu_N'' for each N in 1, 2, 3, for the three levels of bulleted list.&lt;br /&gt;
&lt;br /&gt;
The fr_* elements define the font, font-size, color etc. for the different levels of text.  They also define the position and wrap width.  See the example template file for a possible arrangement.&lt;br /&gt;
&lt;br /&gt;
Optional contents:&lt;br /&gt;
&lt;br /&gt;
*A background&lt;br /&gt;
*Affiliation text and logos you want to appear on every slide&lt;br /&gt;
*A blurred clone of the gr_title element, offset and behind it.&lt;br /&gt;
*Layers whose name starts with ''Instance'' - these are the layers the user can edit directly in Inkscape for a particular slide.&lt;br /&gt;
&lt;br /&gt;
When you save the template file you should make sure that layers containing place holder text (the fr_* elements) and bullets are invisible, or at least hidden below the background layer, which should be visible.  Likewise the Texts layer should be visible, as well as any Instance layers.&lt;br /&gt;
&lt;br /&gt;
=== Text Input ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%title Inkslide: an example&lt;br /&gt;
This text will appear as a top level paragraph.&lt;br /&gt;
    Indented by a single tab, this text will appear as a bulleted item.&lt;br /&gt;
        Two tabs -&amp;gt; second level bulleted list.&lt;br /&gt;
&lt;br /&gt;
A gap will be left for the preceeding line.&lt;br /&gt;
%pause&lt;br /&gt;
Two slides will be produced for this input, one with everything up to the %pause above, and one with the remaining content.  Incremental list display can be done this way.&lt;br /&gt;
&lt;br /&gt;
The %id tag below indicates that a file with a name like 'extra/is_intro.svg' should be inspected for extra content for this particular slide.&lt;br /&gt;
%id is_intro&lt;br /&gt;
%title Inkslide: the next slide...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== The GUI ===&lt;br /&gt;
&lt;br /&gt;
A GUI for InkSlide is available as a plug-in for the [http://webpages.charter.net/edreamleo/front.html Leo] outline editor.  This makes slide re-arranging easy, and adds some macro substitution capability.  It also adds buttons for editing slide specific content in Inkscape.&lt;br /&gt;
&lt;br /&gt;
FIXME: post plug-in on Leo wiki and link.&lt;br /&gt;
&lt;br /&gt;
=== To do / status ===&lt;br /&gt;
&lt;br /&gt;
InkSlide is very usable, but is missing some features:&lt;br /&gt;
&lt;br /&gt;
*Inline text formatting (bold, color, etc.) for part of a piece of text&lt;br /&gt;
*Justification switching&lt;br /&gt;
*Verbatim code display&lt;br /&gt;
*Simple image inclusion&lt;br /&gt;
**currently you can do this using the Inkscape editing features&lt;br /&gt;
*more helper tool to make PNG and PDF versions of slides&lt;br /&gt;
*configuring / using a directory for edited slides&lt;br /&gt;
*command line flags for generating specific slides&lt;br /&gt;
&lt;br /&gt;
=== Bugs ===&lt;br /&gt;
&lt;br /&gt;
*There may be an issue with the way slide specific content is merged into the template producing conflicting @id attributes, but I haven't seen this happen yet.&lt;br /&gt;
&lt;br /&gt;
=== Author  / Credits ===&lt;br /&gt;
&lt;br /&gt;
InkSlide was written by Terry Brown, terry-n-brown@yahoo.com - but use underscore, not '-'.&lt;br /&gt;
&lt;br /&gt;
InkSlide was inspired by [http://member.wide.ad.jp/wg/mgp/ MagicPoint]&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# inkslide.py $Id$&lt;br /&gt;
# Author: Terry Brown&lt;br /&gt;
# Created: Thu Oct 11 2007&lt;br /&gt;
&lt;br /&gt;
import lxml.etree as ET&lt;br /&gt;
&lt;br /&gt;
import copy&lt;br /&gt;
import subprocess&lt;br /&gt;
import os.path&lt;br /&gt;
&lt;br /&gt;
NS = { 'i': 'http://www.inkscape.org/namespaces/inkscape',&lt;br /&gt;
       's': 'http://www.w3.org/2000/svg',&lt;br /&gt;
       'xlink' : 'http://www.w3.org/1999/xlink'}&lt;br /&gt;
&lt;br /&gt;
# number of defined tablevels, FIXME, could derive from template?&lt;br /&gt;
tabLevels = 4  &lt;br /&gt;
def clearId(x, what='id', NS={}):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;recursively clear @id on element x and descendants&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if what in x.keys():&lt;br /&gt;
        del x.attrib[what]&lt;br /&gt;
    for i in x.xpath('.//*[@%s]'%what, NS):&lt;br /&gt;
        del i.attrib[what]&lt;br /&gt;
    return x&lt;br /&gt;
def getDim(fn, Id, what):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return dimension of element in fn with @id Id, what is&lt;br /&gt;
    x, y, width, height&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cmd = ('inkscape', '--without-gui', '--query-id', Id, '--query-'+what, fn)&lt;br /&gt;
    proc = subprocess.Popen(cmd, stdout = subprocess.PIPE,&lt;br /&gt;
                            stderr = subprocess.PIPE)&lt;br /&gt;
    # make new pipe for stderr to supress chatter from inkscape&lt;br /&gt;
    proc.wait()&lt;br /&gt;
    return proc.stdout.read()&lt;br /&gt;
def textReader(text):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;iterate parts in input text&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not text: return&lt;br /&gt;
    for line in text.split('\n'):&lt;br /&gt;
        if line.startswith('%slide '):&lt;br /&gt;
            yield {'type':'TITLE', 's':line[7:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%pause'):&lt;br /&gt;
            yield {'type':'PAUSE'}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%id '):&lt;br /&gt;
            yield {'type':'ID', 's':line[4:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if not line.strip():&lt;br /&gt;
            yield {'type':'BLANK'}&lt;br /&gt;
            continue&lt;br /&gt;
        tabs = len(line) - len(line.lstrip('\t'))&lt;br /&gt;
        yield {'type':'TAB', 'tabs':tabs, 's':line.strip()}&lt;br /&gt;
&lt;br /&gt;
nid = 0&lt;br /&gt;
def nextId():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return an unsed Id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # FIXME, should inspect templat doc.&lt;br /&gt;
    global nid&lt;br /&gt;
    nid += 1&lt;br /&gt;
    return 'is'+str(nid)&lt;br /&gt;
&lt;br /&gt;
def instanceFromTemplate(template, instance, text, pauseOk = True):&lt;br /&gt;
&lt;br /&gt;
    slide = 0&lt;br /&gt;
&lt;br /&gt;
    hlist = []&lt;br /&gt;
    doc0 = ET.parse(template)&lt;br /&gt;
    slideId = None&lt;br /&gt;
    &lt;br /&gt;
    for t in textReader(text):&lt;br /&gt;
        &lt;br /&gt;
        if t['type'] == 'ID':&lt;br /&gt;
            slideId = t['s']&lt;br /&gt;
            continue&lt;br /&gt;
            &lt;br /&gt;
        if t['type'] == 'TITLE':&lt;br /&gt;
            print t['s']&lt;br /&gt;
            if hlist:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
                hlist = [t]&lt;br /&gt;
                doc0 = ET.parse(template)&lt;br /&gt;
                slideId = None&lt;br /&gt;
            else:&lt;br /&gt;
                hlist.append(t)&lt;br /&gt;
            dst = doc0.xpath('//s:g[@id=&amp;quot;gr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'PAUSE':&lt;br /&gt;
            if pauseOk:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        hlist.append(t)&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'BLANK':&lt;br /&gt;
            continue&lt;br /&gt;
        if t['type'] == 'TAB':&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_tab%d&amp;quot;]' % t['tabs'], NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            #break&lt;br /&gt;
            continue&lt;br /&gt;
           &lt;br /&gt;
    process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
def process(instance, doc0, hlist, slideId, slide):&lt;br /&gt;
&lt;br /&gt;
    instance = instance.replace('%s', '%04d'%slide)&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
&lt;br /&gt;
    bullDim = {}&lt;br /&gt;
    tabDim = {}&lt;br /&gt;
    for n in range(0,tabLevels):&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;bu_%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'width', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'bu_%d'%n, i))&lt;br /&gt;
            bullDim[n] = b&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;fr_tab%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'fr_tab%d'%n, i))&lt;br /&gt;
            tabDim[n] = b&lt;br /&gt;
&lt;br /&gt;
    delta = 0.&lt;br /&gt;
    firstVert = True&lt;br /&gt;
    for n, i in enumerate(hlist):&lt;br /&gt;
        if hlist[n]['type'] in ['TITLE']:&lt;br /&gt;
            continue  # already in right place, not a vertical element&lt;br /&gt;
&lt;br /&gt;
        hgt = 0&lt;br /&gt;
        if hlist[n-1]['type'] == 'BLANK':&lt;br /&gt;
            hgt = 30&lt;br /&gt;
        else:&lt;br /&gt;
            hgt = float(getDim(instance, hlist[n-1]['id'], 'height'))&lt;br /&gt;
&lt;br /&gt;
        hgt += 5&lt;br /&gt;
&lt;br /&gt;
        if firstVert:&lt;br /&gt;
            hgt = 0&lt;br /&gt;
            firstVert = False&lt;br /&gt;
&lt;br /&gt;
        delta += hgt&lt;br /&gt;
&lt;br /&gt;
        if hlist[n]['type'] == 'BLANK': continue&lt;br /&gt;
&lt;br /&gt;
        tabs = hlist[n]['tabs']&lt;br /&gt;
&lt;br /&gt;
        r = doc0.xpath('//s:flowRoot[@id=&amp;quot;%s&amp;quot;]' % hlist[n]['id'], NS)[0]&lt;br /&gt;
        clearId(r, what='transform')&lt;br /&gt;
        r = r.xpath('.//s:rect', NS)[0]&lt;br /&gt;
        r.set('x', str(tabDim[tabs]['x']))&lt;br /&gt;
        y = tabDim[tabs]['y'] + delta&lt;br /&gt;
        #print '%s -&amp;gt; %s' % (str(r.get('y')), str(y))&lt;br /&gt;
        #print tabDim[tabs]['y']&lt;br /&gt;
        r.set('y', str(y))&lt;br /&gt;
&lt;br /&gt;
        if (hlist[n]['type'] == 'TAB' and hlist[n]['tabs'] in bullDim&lt;br /&gt;
            and 'isDone' not in hlist[n]):&lt;br /&gt;
            hlist[n]['isDone'] = True&lt;br /&gt;
            dx = (tabDim[tabs]['x'] &lt;br /&gt;
                  - bullDim[tabs]['x']&lt;br /&gt;
                  - 1.5*bullDim[tabs]['width']&lt;br /&gt;
                  )&lt;br /&gt;
            dy = (y &lt;br /&gt;
                  + tabDim[tabs]['height'] / 2.&lt;br /&gt;
                  - bullDim[tabs]['y'] &lt;br /&gt;
                  - bullDim[tabs]['height'] / 2.&lt;br /&gt;
                  )&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            #print y, tabDim[tabs]['height'], bullDim[tabs]['y'], bullDim[tabs]['height'], dy&lt;br /&gt;
            clone = ET.Element('svg:use')&lt;br /&gt;
            clone.set('transform', 'translate(%f,%f)' % (dx,dy))&lt;br /&gt;
            clone.set('{%s}href'%NS['xlink'], '#bu_%d'%tabs)&lt;br /&gt;
            dst.append(clone)&lt;br /&gt;
            &lt;br /&gt;
            #path = ET.Element('svg:path')&lt;br /&gt;
            #path.set('d', ('M %f,%f' + ' L %f,%f'*5 + ' z') % (&lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y'], &lt;br /&gt;
            #    bullDim[tabs]['x']+20, bullDim[tabs]['y']+bullDim[tabs]['height']/2., &lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y']+bullDim[tabs]['height'], &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y'], &lt;br /&gt;
            #    tabDim[tabs]['x']+20, tabDim[tabs]['y']+tabDim[tabs]['height']/2., &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y']+tabDim[tabs]['height'], &lt;br /&gt;
            #    ))&lt;br /&gt;
            #dst.append(path)&lt;br /&gt;
            #break&lt;br /&gt;
&lt;br /&gt;
    # look for instance specific parts&lt;br /&gt;
    if slideId:&lt;br /&gt;
        f = instancePath(slideId)&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            comp = ET.parse(f)&lt;br /&gt;
            svg = doc0.xpath('//s:svg', NS)[0]&lt;br /&gt;
            g = &amp;quot;{%s}g&amp;quot;%NS['s']&lt;br /&gt;
            k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
            for n, i in enumerate(svg.getchildren()):&lt;br /&gt;
                if i.tag == g and i.get(k, '').startswith('Instance'):&lt;br /&gt;
                    x = comp.xpath('//s:svg//s:g[@i:label=&amp;quot;%s&amp;quot;]'%i.get(k), NS)&lt;br /&gt;
                    if x:&lt;br /&gt;
                        x = x[0]&lt;br /&gt;
                        cpy = clearId(copy.deepcopy(x))&lt;br /&gt;
                        svg[n] = cpy&lt;br /&gt;
            defs = comp.xpath('//s:svg/s:defs/*', NS)&lt;br /&gt;
            dst = doc0.xpath('//s:svg/s:defs', NS)[0]&lt;br /&gt;
            for d in defs:&lt;br /&gt;
                Id = d.get('id')&lt;br /&gt;
                current = doc0.xpath('//s:svg/s:defs/*[@id=&amp;quot;%s&amp;quot;]'%Id, NS)&lt;br /&gt;
                if not current:&lt;br /&gt;
                    cpy = copy.deepcopy(d)&lt;br /&gt;
                    dst.append(cpy)&lt;br /&gt;
&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
    &lt;br /&gt;
    return slideId&lt;br /&gt;
def instancePath(slideId, component = None):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return path to component for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # return os.path.join(slideId, component)&lt;br /&gt;
    return slideId+'.svg'&lt;br /&gt;
def writeComponents(inkscapeFile, slideId):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;write components from file for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not slideId: return&lt;br /&gt;
    doc0 = ET.parse(inkscapeFile)&lt;br /&gt;
    hasComponents = False&lt;br /&gt;
    for i in doc0.xpath('//s:svg//s:g', NS):&lt;br /&gt;
        k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
        # print i.keys(),k&lt;br /&gt;
        if (k in i.keys() and i.get(k).startswith('Instance')):&lt;br /&gt;
            if len(i) &amp;gt; 0:&lt;br /&gt;
                hasComponents = True&lt;br /&gt;
                break&lt;br /&gt;
                # ET.ElementTree(i).write(f)&lt;br /&gt;
                &lt;br /&gt;
    f = instancePath(slideId, 'components')&lt;br /&gt;
    &lt;br /&gt;
    if hasComponents:&lt;br /&gt;
        if (os.path.abspath(os.path.realpath(inkscapeFile)) != &lt;br /&gt;
            os.path.abspath(os.path.realpath(f))):&lt;br /&gt;
            file(f, 'w').write(file(inkscapeFile).read())&lt;br /&gt;
    else:&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            os.remove(f)&lt;br /&gt;
&lt;br /&gt;
if __name__ == '__main__':&lt;br /&gt;
    import sys&lt;br /&gt;
    instanceFromTemplate(sys.argv[1], 'slide%s.svg', file(sys.argv[2]).read())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16461</id>
		<title>InkSlide</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16461"/>
		<updated>2007-10-17T16:45:15Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: /* The Template File */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== InkSlide - quick and easy presentations using Inkscape ==&lt;br /&gt;
&lt;br /&gt;
InkSlide produces slides like this:&lt;br /&gt;
&lt;br /&gt;
[[Image:slide0004.jpg]]&lt;br /&gt;
&lt;br /&gt;
from simple text input like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%slide InkSlide: Features&lt;br /&gt;
Features include wrapped top level text and&lt;br /&gt;
    mulitple&lt;br /&gt;
        levels&lt;br /&gt;
            of wrapped bulleted lists with bullets and font&lt;br /&gt;
information taken from the template file.&lt;br /&gt;
&lt;br /&gt;
Slide specific content like this:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
which is updated when the template changes.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An Inkscape file is used as a template file to define the background, title position and font, fonts and positions for text at different levels of indentation, groups to be cloned and used as bullets, etc.&lt;br /&gt;
&lt;br /&gt;
Content specific to a particular slide can also be created in Inkscape, this content merged with the template and text input to make the final slide, so changes to the template after a particular slide is edited in Inkscape are included.&lt;br /&gt;
&lt;br /&gt;
=== Download ===&lt;br /&gt;
&lt;br /&gt;
Copy the [[InkSlide#Code|text below]] into a file called 'inkslide.py'.&lt;br /&gt;
&lt;br /&gt;
=== Use ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python inkslide.py myTemplate.svg mySlides.txt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This creates a slide00xx.svg for each slide in your presentation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python inkslide.py --writeInstance &amp;lt;instanceId&amp;gt; myTemplate.svg mySlides.txt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Creates / updates the Inkscape file you can edit to add special content to this particular slide.   '''NOT IMPLEMENTED YET''' - the code exists, but not the command line hook.&lt;br /&gt;
&lt;br /&gt;
=== How it works ===&lt;br /&gt;
&lt;br /&gt;
First InkSlide parses the text input, something like the example at the top of this page.  It looks for flowed text boxes in the template file with special @id attributes like ''fr_title'' for the title, and ''fr_tab2'' for the second level of bulleted lists.  Copies of these elements are made and placed in a layer called 'Texts'.  At this point, the text is all piled up at the top of the page.&lt;br /&gt;
&lt;br /&gt;
InkSlide saves the file, and uses Inkscape's command line ''--query-height'' and friends to find out how high the wrapped text pieces are.  At it then repositions them properly, adds clones of the bullet markers, etc.  Bullet markers have special @id attributes like ''bu_1'' for the first level of bulleted list.&lt;br /&gt;
&lt;br /&gt;
Finally, InkSlide checks to see if there's an %id tag for the slide, and if so if there's a corresponding Inkscape SVG file with content for this particular slide. This content has to occur in layers whose name starts with ''Instance'', and these layers have to be present in the template file, but you can have more than one, so they can be above and below the regular content.  If such content is found, it's copied into the output slide.&lt;br /&gt;
&lt;br /&gt;
=== The Template File ===&lt;br /&gt;
&lt;br /&gt;
The template file is a regular Inkscape file.  If this list seems hard, just use the example template and modify it to suit your needs.&lt;br /&gt;
&lt;br /&gt;
Required contents:&lt;br /&gt;
&lt;br /&gt;
*A layer called ''Texts'', into which text will be placed.&lt;br /&gt;
*A flowed text frame with an @id of ''fr_title'' to define the font and position for the slide title.  This should contain text like ''Title goes here Sly'' because ''Sly'' includes letters which ascend and descend from the baseline to the greatest extent.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*A group with an @id of ''gr_title'' into which the title will be placed.  This makes it easier to do special effects on the title like shadows.&lt;br /&gt;
*Flowed text frames with an @id of ''fr_tabN'' for each N in 0, 1, 2, 3.  ''fr_tab0'' is used for top level non bulleted text.  These should also include ''Sly'' or similar.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*Objects or groups withan @id of ''bu_N'' for each N in 1, 2, 3, for the three levels of bulleted list.&lt;br /&gt;
&lt;br /&gt;
Optional contents:&lt;br /&gt;
&lt;br /&gt;
*A background&lt;br /&gt;
*Affiliation text and logos you want to appear on every slide&lt;br /&gt;
*A blurred clone of the gr_title element, offset and behind it.&lt;br /&gt;
&lt;br /&gt;
=== Text Input ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%title Inkslide: an example&lt;br /&gt;
This text will appear as a top level paragraph.&lt;br /&gt;
    Indented by a single tab, this text will appear as a bulleted item.&lt;br /&gt;
        Two tabs -&amp;gt; second level bulleted list.&lt;br /&gt;
&lt;br /&gt;
A gap will be left for the preceeding line.&lt;br /&gt;
%pause&lt;br /&gt;
Two slides will be produced for this input, one with everything up to the %pause above, and one with the remaining content.  Incremental list display can be done this way.&lt;br /&gt;
&lt;br /&gt;
The %id tag below indicates that a file with a name like 'extra/is_intro.svg' should be inspected for extra content for this particular slide.&lt;br /&gt;
%id is_intro&lt;br /&gt;
%title Inkslide: the next slide...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== The GUI ===&lt;br /&gt;
&lt;br /&gt;
A GUI for InkSlide is available as a plug-in for the [http://webpages.charter.net/edreamleo/front.html Leo] outline editor.  This makes slide re-arranging easy, and adds some macro substitution capability.  It also adds buttons for editing slide specific content in Inkscape.&lt;br /&gt;
&lt;br /&gt;
FIXME: post plug-in on Leo wiki and link.&lt;br /&gt;
&lt;br /&gt;
=== To do / status ===&lt;br /&gt;
&lt;br /&gt;
InkSlide is very usable, but is missing some features:&lt;br /&gt;
&lt;br /&gt;
*Inline text formatting (bold, color, etc.) for part of a piece of text&lt;br /&gt;
*Justification switching&lt;br /&gt;
*Verbatim code display&lt;br /&gt;
*Simple image inclusion&lt;br /&gt;
**currently you can do this using the Inkscape editing features&lt;br /&gt;
*more helper tool to make PNG and PDF versions of slides&lt;br /&gt;
*configuring / using a directory for edited slides&lt;br /&gt;
*command line flags for generating specific slides&lt;br /&gt;
&lt;br /&gt;
=== Bugs ===&lt;br /&gt;
&lt;br /&gt;
*There may be an issue with the way slide specific content is merged into the template producing conflicting @id attributes, but I haven't seen this happen yet.&lt;br /&gt;
&lt;br /&gt;
=== Author  / Credits ===&lt;br /&gt;
&lt;br /&gt;
InkSlide was written by Terry Brown, terry-n-brown@yahoo.com - but use underscore, not '-'.&lt;br /&gt;
&lt;br /&gt;
InkSlide was inspired by [http://member.wide.ad.jp/wg/mgp/ MagicPoint]&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# inkslide.py $Id$&lt;br /&gt;
# Author: Terry Brown&lt;br /&gt;
# Created: Thu Oct 11 2007&lt;br /&gt;
&lt;br /&gt;
import lxml.etree as ET&lt;br /&gt;
&lt;br /&gt;
import copy&lt;br /&gt;
import subprocess&lt;br /&gt;
import os.path&lt;br /&gt;
&lt;br /&gt;
NS = { 'i': 'http://www.inkscape.org/namespaces/inkscape',&lt;br /&gt;
       's': 'http://www.w3.org/2000/svg',&lt;br /&gt;
       'xlink' : 'http://www.w3.org/1999/xlink'}&lt;br /&gt;
&lt;br /&gt;
# number of defined tablevels, FIXME, could derive from template?&lt;br /&gt;
tabLevels = 4  &lt;br /&gt;
def clearId(x, what='id', NS={}):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;recursively clear @id on element x and descendants&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if what in x.keys():&lt;br /&gt;
        del x.attrib[what]&lt;br /&gt;
    for i in x.xpath('.//*[@%s]'%what, NS):&lt;br /&gt;
        del i.attrib[what]&lt;br /&gt;
    return x&lt;br /&gt;
def getDim(fn, Id, what):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return dimension of element in fn with @id Id, what is&lt;br /&gt;
    x, y, width, height&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cmd = ('inkscape', '--without-gui', '--query-id', Id, '--query-'+what, fn)&lt;br /&gt;
    proc = subprocess.Popen(cmd, stdout = subprocess.PIPE,&lt;br /&gt;
                            stderr = subprocess.PIPE)&lt;br /&gt;
    # make new pipe for stderr to supress chatter from inkscape&lt;br /&gt;
    proc.wait()&lt;br /&gt;
    return proc.stdout.read()&lt;br /&gt;
def textReader(text):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;iterate parts in input text&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not text: return&lt;br /&gt;
    for line in text.split('\n'):&lt;br /&gt;
        if line.startswith('%slide '):&lt;br /&gt;
            yield {'type':'TITLE', 's':line[7:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%pause'):&lt;br /&gt;
            yield {'type':'PAUSE'}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%id '):&lt;br /&gt;
            yield {'type':'ID', 's':line[4:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if not line.strip():&lt;br /&gt;
            yield {'type':'BLANK'}&lt;br /&gt;
            continue&lt;br /&gt;
        tabs = len(line) - len(line.lstrip('\t'))&lt;br /&gt;
        yield {'type':'TAB', 'tabs':tabs, 's':line.strip()}&lt;br /&gt;
&lt;br /&gt;
nid = 0&lt;br /&gt;
def nextId():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return an unsed Id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # FIXME, should inspect templat doc.&lt;br /&gt;
    global nid&lt;br /&gt;
    nid += 1&lt;br /&gt;
    return 'is'+str(nid)&lt;br /&gt;
&lt;br /&gt;
def instanceFromTemplate(template, instance, text, pauseOk = True):&lt;br /&gt;
&lt;br /&gt;
    slide = 0&lt;br /&gt;
&lt;br /&gt;
    hlist = []&lt;br /&gt;
    doc0 = ET.parse(template)&lt;br /&gt;
    slideId = None&lt;br /&gt;
    &lt;br /&gt;
    for t in textReader(text):&lt;br /&gt;
        &lt;br /&gt;
        if t['type'] == 'ID':&lt;br /&gt;
            slideId = t['s']&lt;br /&gt;
            continue&lt;br /&gt;
            &lt;br /&gt;
        if t['type'] == 'TITLE':&lt;br /&gt;
            print t['s']&lt;br /&gt;
            if hlist:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
                hlist = [t]&lt;br /&gt;
                doc0 = ET.parse(template)&lt;br /&gt;
                slideId = None&lt;br /&gt;
            else:&lt;br /&gt;
                hlist.append(t)&lt;br /&gt;
            dst = doc0.xpath('//s:g[@id=&amp;quot;gr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'PAUSE':&lt;br /&gt;
            if pauseOk:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        hlist.append(t)&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'BLANK':&lt;br /&gt;
            continue&lt;br /&gt;
        if t['type'] == 'TAB':&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_tab%d&amp;quot;]' % t['tabs'], NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            #break&lt;br /&gt;
            continue&lt;br /&gt;
           &lt;br /&gt;
    process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
def process(instance, doc0, hlist, slideId, slide):&lt;br /&gt;
&lt;br /&gt;
    instance = instance.replace('%s', '%04d'%slide)&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
&lt;br /&gt;
    bullDim = {}&lt;br /&gt;
    tabDim = {}&lt;br /&gt;
    for n in range(0,tabLevels):&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;bu_%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'width', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'bu_%d'%n, i))&lt;br /&gt;
            bullDim[n] = b&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;fr_tab%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'fr_tab%d'%n, i))&lt;br /&gt;
            tabDim[n] = b&lt;br /&gt;
&lt;br /&gt;
    delta = 0.&lt;br /&gt;
    firstVert = True&lt;br /&gt;
    for n, i in enumerate(hlist):&lt;br /&gt;
        if hlist[n]['type'] in ['TITLE']:&lt;br /&gt;
            continue  # already in right place, not a vertical element&lt;br /&gt;
&lt;br /&gt;
        hgt = 0&lt;br /&gt;
        if hlist[n-1]['type'] == 'BLANK':&lt;br /&gt;
            hgt = 30&lt;br /&gt;
        else:&lt;br /&gt;
            hgt = float(getDim(instance, hlist[n-1]['id'], 'height'))&lt;br /&gt;
&lt;br /&gt;
        hgt += 5&lt;br /&gt;
&lt;br /&gt;
        if firstVert:&lt;br /&gt;
            hgt = 0&lt;br /&gt;
            firstVert = False&lt;br /&gt;
&lt;br /&gt;
        delta += hgt&lt;br /&gt;
&lt;br /&gt;
        if hlist[n]['type'] == 'BLANK': continue&lt;br /&gt;
&lt;br /&gt;
        tabs = hlist[n]['tabs']&lt;br /&gt;
&lt;br /&gt;
        r = doc0.xpath('//s:flowRoot[@id=&amp;quot;%s&amp;quot;]' % hlist[n]['id'], NS)[0]&lt;br /&gt;
        clearId(r, what='transform')&lt;br /&gt;
        r = r.xpath('.//s:rect', NS)[0]&lt;br /&gt;
        r.set('x', str(tabDim[tabs]['x']))&lt;br /&gt;
        y = tabDim[tabs]['y'] + delta&lt;br /&gt;
        #print '%s -&amp;gt; %s' % (str(r.get('y')), str(y))&lt;br /&gt;
        #print tabDim[tabs]['y']&lt;br /&gt;
        r.set('y', str(y))&lt;br /&gt;
&lt;br /&gt;
        if (hlist[n]['type'] == 'TAB' and hlist[n]['tabs'] in bullDim&lt;br /&gt;
            and 'isDone' not in hlist[n]):&lt;br /&gt;
            hlist[n]['isDone'] = True&lt;br /&gt;
            dx = (tabDim[tabs]['x'] &lt;br /&gt;
                  - bullDim[tabs]['x']&lt;br /&gt;
                  - 1.5*bullDim[tabs]['width']&lt;br /&gt;
                  )&lt;br /&gt;
            dy = (y &lt;br /&gt;
                  + tabDim[tabs]['height'] / 2.&lt;br /&gt;
                  - bullDim[tabs]['y'] &lt;br /&gt;
                  - bullDim[tabs]['height'] / 2.&lt;br /&gt;
                  )&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            #print y, tabDim[tabs]['height'], bullDim[tabs]['y'], bullDim[tabs]['height'], dy&lt;br /&gt;
            clone = ET.Element('svg:use')&lt;br /&gt;
            clone.set('transform', 'translate(%f,%f)' % (dx,dy))&lt;br /&gt;
            clone.set('{%s}href'%NS['xlink'], '#bu_%d'%tabs)&lt;br /&gt;
            dst.append(clone)&lt;br /&gt;
            &lt;br /&gt;
            #path = ET.Element('svg:path')&lt;br /&gt;
            #path.set('d', ('M %f,%f' + ' L %f,%f'*5 + ' z') % (&lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y'], &lt;br /&gt;
            #    bullDim[tabs]['x']+20, bullDim[tabs]['y']+bullDim[tabs]['height']/2., &lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y']+bullDim[tabs]['height'], &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y'], &lt;br /&gt;
            #    tabDim[tabs]['x']+20, tabDim[tabs]['y']+tabDim[tabs]['height']/2., &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y']+tabDim[tabs]['height'], &lt;br /&gt;
            #    ))&lt;br /&gt;
            #dst.append(path)&lt;br /&gt;
            #break&lt;br /&gt;
&lt;br /&gt;
    # look for instance specific parts&lt;br /&gt;
    if slideId:&lt;br /&gt;
        f = instancePath(slideId)&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            comp = ET.parse(f)&lt;br /&gt;
            svg = doc0.xpath('//s:svg', NS)[0]&lt;br /&gt;
            g = &amp;quot;{%s}g&amp;quot;%NS['s']&lt;br /&gt;
            k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
            for n, i in enumerate(svg.getchildren()):&lt;br /&gt;
                if i.tag == g and i.get(k, '').startswith('Instance'):&lt;br /&gt;
                    x = comp.xpath('//s:svg//s:g[@i:label=&amp;quot;%s&amp;quot;]'%i.get(k), NS)&lt;br /&gt;
                    if x:&lt;br /&gt;
                        x = x[0]&lt;br /&gt;
                        cpy = clearId(copy.deepcopy(x))&lt;br /&gt;
                        svg[n] = cpy&lt;br /&gt;
            defs = comp.xpath('//s:svg/s:defs/*', NS)&lt;br /&gt;
            dst = doc0.xpath('//s:svg/s:defs', NS)[0]&lt;br /&gt;
            for d in defs:&lt;br /&gt;
                Id = d.get('id')&lt;br /&gt;
                current = doc0.xpath('//s:svg/s:defs/*[@id=&amp;quot;%s&amp;quot;]'%Id, NS)&lt;br /&gt;
                if not current:&lt;br /&gt;
                    cpy = copy.deepcopy(d)&lt;br /&gt;
                    dst.append(cpy)&lt;br /&gt;
&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
    &lt;br /&gt;
    return slideId&lt;br /&gt;
def instancePath(slideId, component = None):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return path to component for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # return os.path.join(slideId, component)&lt;br /&gt;
    return slideId+'.svg'&lt;br /&gt;
def writeComponents(inkscapeFile, slideId):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;write components from file for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not slideId: return&lt;br /&gt;
    doc0 = ET.parse(inkscapeFile)&lt;br /&gt;
    hasComponents = False&lt;br /&gt;
    for i in doc0.xpath('//s:svg//s:g', NS):&lt;br /&gt;
        k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
        # print i.keys(),k&lt;br /&gt;
        if (k in i.keys() and i.get(k).startswith('Instance')):&lt;br /&gt;
            if len(i) &amp;gt; 0:&lt;br /&gt;
                hasComponents = True&lt;br /&gt;
                break&lt;br /&gt;
                # ET.ElementTree(i).write(f)&lt;br /&gt;
                &lt;br /&gt;
    f = instancePath(slideId, 'components')&lt;br /&gt;
    &lt;br /&gt;
    if hasComponents:&lt;br /&gt;
        if (os.path.abspath(os.path.realpath(inkscapeFile)) != &lt;br /&gt;
            os.path.abspath(os.path.realpath(f))):&lt;br /&gt;
            file(f, 'w').write(file(inkscapeFile).read())&lt;br /&gt;
    else:&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            os.remove(f)&lt;br /&gt;
&lt;br /&gt;
if __name__ == '__main__':&lt;br /&gt;
    import sys&lt;br /&gt;
    instanceFromTemplate(sys.argv[1], 'slide%s.svg', file(sys.argv[2]).read())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16460</id>
		<title>InkSlide</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16460"/>
		<updated>2007-10-17T16:44:35Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: /* The Template File */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== InkSlide - quick and easy presentations using Inkscape ==&lt;br /&gt;
&lt;br /&gt;
InkSlide produces slides like this:&lt;br /&gt;
&lt;br /&gt;
[[Image:slide0004.jpg]]&lt;br /&gt;
&lt;br /&gt;
from simple text input like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%slide InkSlide: Features&lt;br /&gt;
Features include wrapped top level text and&lt;br /&gt;
    mulitple&lt;br /&gt;
        levels&lt;br /&gt;
            of wrapped bulleted lists with bullets and font&lt;br /&gt;
information taken from the template file.&lt;br /&gt;
&lt;br /&gt;
Slide specific content like this:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
which is updated when the template changes.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An Inkscape file is used as a template file to define the background, title position and font, fonts and positions for text at different levels of indentation, groups to be cloned and used as bullets, etc.&lt;br /&gt;
&lt;br /&gt;
Content specific to a particular slide can also be created in Inkscape, this content merged with the template and text input to make the final slide, so changes to the template after a particular slide is edited in Inkscape are included.&lt;br /&gt;
&lt;br /&gt;
=== Download ===&lt;br /&gt;
&lt;br /&gt;
Copy the [[InkSlide#Code|text below]] into a file called 'inkslide.py'.&lt;br /&gt;
&lt;br /&gt;
=== Use ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python inkslide.py myTemplate.svg mySlides.txt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This creates a slide00xx.svg for each slide in your presentation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python inkslide.py --writeInstance &amp;lt;instanceId&amp;gt; myTemplate.svg mySlides.txt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Creates / updates the Inkscape file you can edit to add special content to this particular slide.   '''NOT IMPLEMENTED YET''' - the code exists, but not the command line hook.&lt;br /&gt;
&lt;br /&gt;
=== How it works ===&lt;br /&gt;
&lt;br /&gt;
First InkSlide parses the text input, something like the example at the top of this page.  It looks for flowed text boxes in the template file with special @id attributes like ''fr_title'' for the title, and ''fr_tab2'' for the second level of bulleted lists.  Copies of these elements are made and placed in a layer called 'Texts'.  At this point, the text is all piled up at the top of the page.&lt;br /&gt;
&lt;br /&gt;
InkSlide saves the file, and uses Inkscape's command line ''--query-height'' and friends to find out how high the wrapped text pieces are.  At it then repositions them properly, adds clones of the bullet markers, etc.  Bullet markers have special @id attributes like ''bu_1'' for the first level of bulleted list.&lt;br /&gt;
&lt;br /&gt;
Finally, InkSlide checks to see if there's an %id tag for the slide, and if so if there's a corresponding Inkscape SVG file with content for this particular slide. This content has to occur in layers whose name starts with ''Instance'', and these layers have to be present in the template file, but you can have more than one, so they can be above and below the regular content.  If such content is found, it's copied into the output slide.&lt;br /&gt;
&lt;br /&gt;
=== The Template File ===&lt;br /&gt;
&lt;br /&gt;
The template file is a regular Inkscape file.  If this list seems hard, just use the example template and modify it to suit your needs.&lt;br /&gt;
&lt;br /&gt;
Required contents:&lt;br /&gt;
&lt;br /&gt;
*A layer called ''Texts'', into which text will be placed.&lt;br /&gt;
*A flowed text frame with an @id of ''fr_title'' to define the font and position for the slide title.  This should contain text like ''Title goes here Sly'' because ''Sly' includes letters which ascend and descend from the baseline to the greatest extent.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*A group with an @id of ''gr_title'' into which the title will be placed.  This makes it easier to do special effects on the title like shadows.&lt;br /&gt;
*Flowed text frames with an @id of ''fr_tabN'' for each N in 0, 1, 2, 3.  ''fr_tab0'' is used for top level non bulleted text.  These should also include ''Sly'' or similar.  The placeholder text must be '''only''' one line because it's used to calculate line height.&lt;br /&gt;
*Objects or groups withan @id of ''bu_N'' for each N in 1, 2, 3, for the three levels of bulleted list.&lt;br /&gt;
&lt;br /&gt;
Optional contents:&lt;br /&gt;
&lt;br /&gt;
*A background&lt;br /&gt;
*Affiliation text and logos you want to appear on every slide&lt;br /&gt;
*A blurred clone of the gr_title element, offset and behind it.&lt;br /&gt;
&lt;br /&gt;
=== Text Input ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%title Inkslide: an example&lt;br /&gt;
This text will appear as a top level paragraph.&lt;br /&gt;
    Indented by a single tab, this text will appear as a bulleted item.&lt;br /&gt;
        Two tabs -&amp;gt; second level bulleted list.&lt;br /&gt;
&lt;br /&gt;
A gap will be left for the preceeding line.&lt;br /&gt;
%pause&lt;br /&gt;
Two slides will be produced for this input, one with everything up to the %pause above, and one with the remaining content.  Incremental list display can be done this way.&lt;br /&gt;
&lt;br /&gt;
The %id tag below indicates that a file with a name like 'extra/is_intro.svg' should be inspected for extra content for this particular slide.&lt;br /&gt;
%id is_intro&lt;br /&gt;
%title Inkslide: the next slide...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== The GUI ===&lt;br /&gt;
&lt;br /&gt;
A GUI for InkSlide is available as a plug-in for the [http://webpages.charter.net/edreamleo/front.html Leo] outline editor.  This makes slide re-arranging easy, and adds some macro substitution capability.  It also adds buttons for editing slide specific content in Inkscape.&lt;br /&gt;
&lt;br /&gt;
FIXME: post plug-in on Leo wiki and link.&lt;br /&gt;
&lt;br /&gt;
=== To do / status ===&lt;br /&gt;
&lt;br /&gt;
InkSlide is very usable, but is missing some features:&lt;br /&gt;
&lt;br /&gt;
*Inline text formatting (bold, color, etc.) for part of a piece of text&lt;br /&gt;
*Justification switching&lt;br /&gt;
*Verbatim code display&lt;br /&gt;
*Simple image inclusion&lt;br /&gt;
**currently you can do this using the Inkscape editing features&lt;br /&gt;
*more helper tool to make PNG and PDF versions of slides&lt;br /&gt;
*configuring / using a directory for edited slides&lt;br /&gt;
*command line flags for generating specific slides&lt;br /&gt;
&lt;br /&gt;
=== Bugs ===&lt;br /&gt;
&lt;br /&gt;
*There may be an issue with the way slide specific content is merged into the template producing conflicting @id attributes, but I haven't seen this happen yet.&lt;br /&gt;
&lt;br /&gt;
=== Author  / Credits ===&lt;br /&gt;
&lt;br /&gt;
InkSlide was written by Terry Brown, terry-n-brown@yahoo.com - but use underscore, not '-'.&lt;br /&gt;
&lt;br /&gt;
InkSlide was inspired by [http://member.wide.ad.jp/wg/mgp/ MagicPoint]&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# inkslide.py $Id$&lt;br /&gt;
# Author: Terry Brown&lt;br /&gt;
# Created: Thu Oct 11 2007&lt;br /&gt;
&lt;br /&gt;
import lxml.etree as ET&lt;br /&gt;
&lt;br /&gt;
import copy&lt;br /&gt;
import subprocess&lt;br /&gt;
import os.path&lt;br /&gt;
&lt;br /&gt;
NS = { 'i': 'http://www.inkscape.org/namespaces/inkscape',&lt;br /&gt;
       's': 'http://www.w3.org/2000/svg',&lt;br /&gt;
       'xlink' : 'http://www.w3.org/1999/xlink'}&lt;br /&gt;
&lt;br /&gt;
# number of defined tablevels, FIXME, could derive from template?&lt;br /&gt;
tabLevels = 4  &lt;br /&gt;
def clearId(x, what='id', NS={}):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;recursively clear @id on element x and descendants&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if what in x.keys():&lt;br /&gt;
        del x.attrib[what]&lt;br /&gt;
    for i in x.xpath('.//*[@%s]'%what, NS):&lt;br /&gt;
        del i.attrib[what]&lt;br /&gt;
    return x&lt;br /&gt;
def getDim(fn, Id, what):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return dimension of element in fn with @id Id, what is&lt;br /&gt;
    x, y, width, height&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cmd = ('inkscape', '--without-gui', '--query-id', Id, '--query-'+what, fn)&lt;br /&gt;
    proc = subprocess.Popen(cmd, stdout = subprocess.PIPE,&lt;br /&gt;
                            stderr = subprocess.PIPE)&lt;br /&gt;
    # make new pipe for stderr to supress chatter from inkscape&lt;br /&gt;
    proc.wait()&lt;br /&gt;
    return proc.stdout.read()&lt;br /&gt;
def textReader(text):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;iterate parts in input text&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not text: return&lt;br /&gt;
    for line in text.split('\n'):&lt;br /&gt;
        if line.startswith('%slide '):&lt;br /&gt;
            yield {'type':'TITLE', 's':line[7:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%pause'):&lt;br /&gt;
            yield {'type':'PAUSE'}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%id '):&lt;br /&gt;
            yield {'type':'ID', 's':line[4:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if not line.strip():&lt;br /&gt;
            yield {'type':'BLANK'}&lt;br /&gt;
            continue&lt;br /&gt;
        tabs = len(line) - len(line.lstrip('\t'))&lt;br /&gt;
        yield {'type':'TAB', 'tabs':tabs, 's':line.strip()}&lt;br /&gt;
&lt;br /&gt;
nid = 0&lt;br /&gt;
def nextId():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return an unsed Id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # FIXME, should inspect templat doc.&lt;br /&gt;
    global nid&lt;br /&gt;
    nid += 1&lt;br /&gt;
    return 'is'+str(nid)&lt;br /&gt;
&lt;br /&gt;
def instanceFromTemplate(template, instance, text, pauseOk = True):&lt;br /&gt;
&lt;br /&gt;
    slide = 0&lt;br /&gt;
&lt;br /&gt;
    hlist = []&lt;br /&gt;
    doc0 = ET.parse(template)&lt;br /&gt;
    slideId = None&lt;br /&gt;
    &lt;br /&gt;
    for t in textReader(text):&lt;br /&gt;
        &lt;br /&gt;
        if t['type'] == 'ID':&lt;br /&gt;
            slideId = t['s']&lt;br /&gt;
            continue&lt;br /&gt;
            &lt;br /&gt;
        if t['type'] == 'TITLE':&lt;br /&gt;
            print t['s']&lt;br /&gt;
            if hlist:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
                hlist = [t]&lt;br /&gt;
                doc0 = ET.parse(template)&lt;br /&gt;
                slideId = None&lt;br /&gt;
            else:&lt;br /&gt;
                hlist.append(t)&lt;br /&gt;
            dst = doc0.xpath('//s:g[@id=&amp;quot;gr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'PAUSE':&lt;br /&gt;
            if pauseOk:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        hlist.append(t)&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'BLANK':&lt;br /&gt;
            continue&lt;br /&gt;
        if t['type'] == 'TAB':&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_tab%d&amp;quot;]' % t['tabs'], NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            #break&lt;br /&gt;
            continue&lt;br /&gt;
           &lt;br /&gt;
    process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
def process(instance, doc0, hlist, slideId, slide):&lt;br /&gt;
&lt;br /&gt;
    instance = instance.replace('%s', '%04d'%slide)&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
&lt;br /&gt;
    bullDim = {}&lt;br /&gt;
    tabDim = {}&lt;br /&gt;
    for n in range(0,tabLevels):&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;bu_%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'width', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'bu_%d'%n, i))&lt;br /&gt;
            bullDim[n] = b&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;fr_tab%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'fr_tab%d'%n, i))&lt;br /&gt;
            tabDim[n] = b&lt;br /&gt;
&lt;br /&gt;
    delta = 0.&lt;br /&gt;
    firstVert = True&lt;br /&gt;
    for n, i in enumerate(hlist):&lt;br /&gt;
        if hlist[n]['type'] in ['TITLE']:&lt;br /&gt;
            continue  # already in right place, not a vertical element&lt;br /&gt;
&lt;br /&gt;
        hgt = 0&lt;br /&gt;
        if hlist[n-1]['type'] == 'BLANK':&lt;br /&gt;
            hgt = 30&lt;br /&gt;
        else:&lt;br /&gt;
            hgt = float(getDim(instance, hlist[n-1]['id'], 'height'))&lt;br /&gt;
&lt;br /&gt;
        hgt += 5&lt;br /&gt;
&lt;br /&gt;
        if firstVert:&lt;br /&gt;
            hgt = 0&lt;br /&gt;
            firstVert = False&lt;br /&gt;
&lt;br /&gt;
        delta += hgt&lt;br /&gt;
&lt;br /&gt;
        if hlist[n]['type'] == 'BLANK': continue&lt;br /&gt;
&lt;br /&gt;
        tabs = hlist[n]['tabs']&lt;br /&gt;
&lt;br /&gt;
        r = doc0.xpath('//s:flowRoot[@id=&amp;quot;%s&amp;quot;]' % hlist[n]['id'], NS)[0]&lt;br /&gt;
        clearId(r, what='transform')&lt;br /&gt;
        r = r.xpath('.//s:rect', NS)[0]&lt;br /&gt;
        r.set('x', str(tabDim[tabs]['x']))&lt;br /&gt;
        y = tabDim[tabs]['y'] + delta&lt;br /&gt;
        #print '%s -&amp;gt; %s' % (str(r.get('y')), str(y))&lt;br /&gt;
        #print tabDim[tabs]['y']&lt;br /&gt;
        r.set('y', str(y))&lt;br /&gt;
&lt;br /&gt;
        if (hlist[n]['type'] == 'TAB' and hlist[n]['tabs'] in bullDim&lt;br /&gt;
            and 'isDone' not in hlist[n]):&lt;br /&gt;
            hlist[n]['isDone'] = True&lt;br /&gt;
            dx = (tabDim[tabs]['x'] &lt;br /&gt;
                  - bullDim[tabs]['x']&lt;br /&gt;
                  - 1.5*bullDim[tabs]['width']&lt;br /&gt;
                  )&lt;br /&gt;
            dy = (y &lt;br /&gt;
                  + tabDim[tabs]['height'] / 2.&lt;br /&gt;
                  - bullDim[tabs]['y'] &lt;br /&gt;
                  - bullDim[tabs]['height'] / 2.&lt;br /&gt;
                  )&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            #print y, tabDim[tabs]['height'], bullDim[tabs]['y'], bullDim[tabs]['height'], dy&lt;br /&gt;
            clone = ET.Element('svg:use')&lt;br /&gt;
            clone.set('transform', 'translate(%f,%f)' % (dx,dy))&lt;br /&gt;
            clone.set('{%s}href'%NS['xlink'], '#bu_%d'%tabs)&lt;br /&gt;
            dst.append(clone)&lt;br /&gt;
            &lt;br /&gt;
            #path = ET.Element('svg:path')&lt;br /&gt;
            #path.set('d', ('M %f,%f' + ' L %f,%f'*5 + ' z') % (&lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y'], &lt;br /&gt;
            #    bullDim[tabs]['x']+20, bullDim[tabs]['y']+bullDim[tabs]['height']/2., &lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y']+bullDim[tabs]['height'], &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y'], &lt;br /&gt;
            #    tabDim[tabs]['x']+20, tabDim[tabs]['y']+tabDim[tabs]['height']/2., &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y']+tabDim[tabs]['height'], &lt;br /&gt;
            #    ))&lt;br /&gt;
            #dst.append(path)&lt;br /&gt;
            #break&lt;br /&gt;
&lt;br /&gt;
    # look for instance specific parts&lt;br /&gt;
    if slideId:&lt;br /&gt;
        f = instancePath(slideId)&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            comp = ET.parse(f)&lt;br /&gt;
            svg = doc0.xpath('//s:svg', NS)[0]&lt;br /&gt;
            g = &amp;quot;{%s}g&amp;quot;%NS['s']&lt;br /&gt;
            k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
            for n, i in enumerate(svg.getchildren()):&lt;br /&gt;
                if i.tag == g and i.get(k, '').startswith('Instance'):&lt;br /&gt;
                    x = comp.xpath('//s:svg//s:g[@i:label=&amp;quot;%s&amp;quot;]'%i.get(k), NS)&lt;br /&gt;
                    if x:&lt;br /&gt;
                        x = x[0]&lt;br /&gt;
                        cpy = clearId(copy.deepcopy(x))&lt;br /&gt;
                        svg[n] = cpy&lt;br /&gt;
            defs = comp.xpath('//s:svg/s:defs/*', NS)&lt;br /&gt;
            dst = doc0.xpath('//s:svg/s:defs', NS)[0]&lt;br /&gt;
            for d in defs:&lt;br /&gt;
                Id = d.get('id')&lt;br /&gt;
                current = doc0.xpath('//s:svg/s:defs/*[@id=&amp;quot;%s&amp;quot;]'%Id, NS)&lt;br /&gt;
                if not current:&lt;br /&gt;
                    cpy = copy.deepcopy(d)&lt;br /&gt;
                    dst.append(cpy)&lt;br /&gt;
&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
    &lt;br /&gt;
    return slideId&lt;br /&gt;
def instancePath(slideId, component = None):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return path to component for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # return os.path.join(slideId, component)&lt;br /&gt;
    return slideId+'.svg'&lt;br /&gt;
def writeComponents(inkscapeFile, slideId):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;write components from file for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not slideId: return&lt;br /&gt;
    doc0 = ET.parse(inkscapeFile)&lt;br /&gt;
    hasComponents = False&lt;br /&gt;
    for i in doc0.xpath('//s:svg//s:g', NS):&lt;br /&gt;
        k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
        # print i.keys(),k&lt;br /&gt;
        if (k in i.keys() and i.get(k).startswith('Instance')):&lt;br /&gt;
            if len(i) &amp;gt; 0:&lt;br /&gt;
                hasComponents = True&lt;br /&gt;
                break&lt;br /&gt;
                # ET.ElementTree(i).write(f)&lt;br /&gt;
                &lt;br /&gt;
    f = instancePath(slideId, 'components')&lt;br /&gt;
    &lt;br /&gt;
    if hasComponents:&lt;br /&gt;
        if (os.path.abspath(os.path.realpath(inkscapeFile)) != &lt;br /&gt;
            os.path.abspath(os.path.realpath(f))):&lt;br /&gt;
            file(f, 'w').write(file(inkscapeFile).read())&lt;br /&gt;
    else:&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            os.remove(f)&lt;br /&gt;
&lt;br /&gt;
if __name__ == '__main__':&lt;br /&gt;
    import sys&lt;br /&gt;
    instanceFromTemplate(sys.argv[1], 'slide%s.svg', file(sys.argv[2]).read())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16459</id>
		<title>InkSlide</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16459"/>
		<updated>2007-10-17T16:29:47Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: /* InkSlide - quick and easy presentations using Inkscape */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== InkSlide - quick and easy presentations using Inkscape ==&lt;br /&gt;
&lt;br /&gt;
InkSlide produces slides like this:&lt;br /&gt;
&lt;br /&gt;
[[Image:slide0004.jpg]]&lt;br /&gt;
&lt;br /&gt;
from simple text input like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%slide InkSlide: Features&lt;br /&gt;
Features include wrapped top level text and&lt;br /&gt;
    mulitple&lt;br /&gt;
        levels&lt;br /&gt;
            of wrapped bulleted lists with bullets and font&lt;br /&gt;
information taken from the template file.&lt;br /&gt;
&lt;br /&gt;
Slide specific content like this:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
which is updated when the template changes.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An Inkscape file is used as a template file to define the background, title position and font, fonts and positions for text at different levels of indentation, groups to be cloned and used as bullets, etc.&lt;br /&gt;
&lt;br /&gt;
Content specific to a particular slide can also be created in Inkscape, this content merged with the template and text input to make the final slide, so changes to the template after a particular slide is edited in Inkscape are included.&lt;br /&gt;
&lt;br /&gt;
=== Download ===&lt;br /&gt;
&lt;br /&gt;
Copy the [[InkSlide#Code|text below]] into a file called 'inkslide.py'.&lt;br /&gt;
&lt;br /&gt;
=== Use ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python inkslide.py myTemplate.svg mySlides.txt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This creates a slide00xx.svg for each slide in your presentation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python inkslide.py --writeInstance &amp;lt;instanceId&amp;gt; myTemplate.svg mySlides.txt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Creates / updates the Inkscape file you can edit to add special content to this particular slide.   '''NOT IMPLEMENTED YET''' - the code exists, but not the command line hook.&lt;br /&gt;
&lt;br /&gt;
=== How it works ===&lt;br /&gt;
&lt;br /&gt;
First InkSlide parses the text input, something like the example at the top of this page.  It looks for flowed text boxes in the template file with special @id attributes like ''fr_title'' for the title, and ''fr_tab2'' for the second level of bulleted lists.  Copies of these elements are made and placed in a layer called 'Texts'.  At this point, the text is all piled up at the top of the page.&lt;br /&gt;
&lt;br /&gt;
InkSlide saves the file, and uses Inkscape's command line ''--query-height'' and friends to find out how high the wrapped text pieces are.  At it then repositions them properly, adds clones of the bullet markers, etc.  Bullet markers have special @id attributes like ''bu_1'' for the first level of bulleted list.&lt;br /&gt;
&lt;br /&gt;
Finally, InkSlide checks to see if there's an %id tag for the slide, and if so if there's a corresponding Inkscape SVG file with content for this particular slide. This content has to occur in layers whose name starts with ''Instance'', and these layers have to be present in the template file, but you can have more than one, so they can be above and below the regular content.  If such content is found, it's copied into the output slide.&lt;br /&gt;
&lt;br /&gt;
=== The Template File ===&lt;br /&gt;
&lt;br /&gt;
=== Text Input ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%title Inkslide: an example&lt;br /&gt;
This text will appear as a top level paragraph.&lt;br /&gt;
    Indented by a single tab, this text will appear as a bulleted item.&lt;br /&gt;
        Two tabs -&amp;gt; second level bulleted list.&lt;br /&gt;
&lt;br /&gt;
A gap will be left for the preceeding line.&lt;br /&gt;
%pause&lt;br /&gt;
Two slides will be produced for this input, one with everything up to the %pause above, and one with the remaining content.  Incremental list display can be done this way.&lt;br /&gt;
&lt;br /&gt;
The %id tag below indicates that a file with a name like 'extra/is_intro.svg' should be inspected for extra content for this particular slide.&lt;br /&gt;
%id is_intro&lt;br /&gt;
%title Inkslide: the next slide...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== The GUI ===&lt;br /&gt;
&lt;br /&gt;
A GUI for InkSlide is available as a plug-in for the [http://webpages.charter.net/edreamleo/front.html Leo] outline editor.  This makes slide re-arranging easy, and adds some macro substitution capability.  It also adds buttons for editing slide specific content in Inkscape.&lt;br /&gt;
&lt;br /&gt;
FIXME: post plug-in on Leo wiki and link.&lt;br /&gt;
&lt;br /&gt;
=== To do / status ===&lt;br /&gt;
&lt;br /&gt;
InkSlide is very usable, but is missing some features:&lt;br /&gt;
&lt;br /&gt;
*Inline text formatting (bold, color, etc.) for part of a piece of text&lt;br /&gt;
*Justification switching&lt;br /&gt;
*Verbatim code display&lt;br /&gt;
*Simple image inclusion&lt;br /&gt;
**currently you can do this using the Inkscape editing features&lt;br /&gt;
*more helper tool to make PNG and PDF versions of slides&lt;br /&gt;
*configuring / using a directory for edited slides&lt;br /&gt;
*command line flags for generating specific slides&lt;br /&gt;
&lt;br /&gt;
=== Bugs ===&lt;br /&gt;
&lt;br /&gt;
*There may be an issue with the way slide specific content is merged into the template producing conflicting @id attributes, but I haven't seen this happen yet.&lt;br /&gt;
&lt;br /&gt;
=== Author  / Credits ===&lt;br /&gt;
&lt;br /&gt;
InkSlide was written by Terry Brown, terry-n-brown@yahoo.com - but use underscore, not '-'.&lt;br /&gt;
&lt;br /&gt;
InkSlide was inspired by [http://member.wide.ad.jp/wg/mgp/ MagicPoint]&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# inkslide.py $Id$&lt;br /&gt;
# Author: Terry Brown&lt;br /&gt;
# Created: Thu Oct 11 2007&lt;br /&gt;
&lt;br /&gt;
import lxml.etree as ET&lt;br /&gt;
&lt;br /&gt;
import copy&lt;br /&gt;
import subprocess&lt;br /&gt;
import os.path&lt;br /&gt;
&lt;br /&gt;
NS = { 'i': 'http://www.inkscape.org/namespaces/inkscape',&lt;br /&gt;
       's': 'http://www.w3.org/2000/svg',&lt;br /&gt;
       'xlink' : 'http://www.w3.org/1999/xlink'}&lt;br /&gt;
&lt;br /&gt;
# number of defined tablevels, FIXME, could derive from template?&lt;br /&gt;
tabLevels = 4  &lt;br /&gt;
def clearId(x, what='id', NS={}):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;recursively clear @id on element x and descendants&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if what in x.keys():&lt;br /&gt;
        del x.attrib[what]&lt;br /&gt;
    for i in x.xpath('.//*[@%s]'%what, NS):&lt;br /&gt;
        del i.attrib[what]&lt;br /&gt;
    return x&lt;br /&gt;
def getDim(fn, Id, what):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return dimension of element in fn with @id Id, what is&lt;br /&gt;
    x, y, width, height&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cmd = ('inkscape', '--without-gui', '--query-id', Id, '--query-'+what, fn)&lt;br /&gt;
    proc = subprocess.Popen(cmd, stdout = subprocess.PIPE,&lt;br /&gt;
                            stderr = subprocess.PIPE)&lt;br /&gt;
    # make new pipe for stderr to supress chatter from inkscape&lt;br /&gt;
    proc.wait()&lt;br /&gt;
    return proc.stdout.read()&lt;br /&gt;
def textReader(text):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;iterate parts in input text&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not text: return&lt;br /&gt;
    for line in text.split('\n'):&lt;br /&gt;
        if line.startswith('%slide '):&lt;br /&gt;
            yield {'type':'TITLE', 's':line[7:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%pause'):&lt;br /&gt;
            yield {'type':'PAUSE'}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%id '):&lt;br /&gt;
            yield {'type':'ID', 's':line[4:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if not line.strip():&lt;br /&gt;
            yield {'type':'BLANK'}&lt;br /&gt;
            continue&lt;br /&gt;
        tabs = len(line) - len(line.lstrip('\t'))&lt;br /&gt;
        yield {'type':'TAB', 'tabs':tabs, 's':line.strip()}&lt;br /&gt;
&lt;br /&gt;
nid = 0&lt;br /&gt;
def nextId():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return an unsed Id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # FIXME, should inspect templat doc.&lt;br /&gt;
    global nid&lt;br /&gt;
    nid += 1&lt;br /&gt;
    return 'is'+str(nid)&lt;br /&gt;
&lt;br /&gt;
def instanceFromTemplate(template, instance, text, pauseOk = True):&lt;br /&gt;
&lt;br /&gt;
    slide = 0&lt;br /&gt;
&lt;br /&gt;
    hlist = []&lt;br /&gt;
    doc0 = ET.parse(template)&lt;br /&gt;
    slideId = None&lt;br /&gt;
    &lt;br /&gt;
    for t in textReader(text):&lt;br /&gt;
        &lt;br /&gt;
        if t['type'] == 'ID':&lt;br /&gt;
            slideId = t['s']&lt;br /&gt;
            continue&lt;br /&gt;
            &lt;br /&gt;
        if t['type'] == 'TITLE':&lt;br /&gt;
            print t['s']&lt;br /&gt;
            if hlist:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
                hlist = [t]&lt;br /&gt;
                doc0 = ET.parse(template)&lt;br /&gt;
                slideId = None&lt;br /&gt;
            else:&lt;br /&gt;
                hlist.append(t)&lt;br /&gt;
            dst = doc0.xpath('//s:g[@id=&amp;quot;gr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'PAUSE':&lt;br /&gt;
            if pauseOk:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        hlist.append(t)&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'BLANK':&lt;br /&gt;
            continue&lt;br /&gt;
        if t['type'] == 'TAB':&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_tab%d&amp;quot;]' % t['tabs'], NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            #break&lt;br /&gt;
            continue&lt;br /&gt;
           &lt;br /&gt;
    process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
def process(instance, doc0, hlist, slideId, slide):&lt;br /&gt;
&lt;br /&gt;
    instance = instance.replace('%s', '%04d'%slide)&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
&lt;br /&gt;
    bullDim = {}&lt;br /&gt;
    tabDim = {}&lt;br /&gt;
    for n in range(0,tabLevels):&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;bu_%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'width', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'bu_%d'%n, i))&lt;br /&gt;
            bullDim[n] = b&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;fr_tab%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'fr_tab%d'%n, i))&lt;br /&gt;
            tabDim[n] = b&lt;br /&gt;
&lt;br /&gt;
    delta = 0.&lt;br /&gt;
    firstVert = True&lt;br /&gt;
    for n, i in enumerate(hlist):&lt;br /&gt;
        if hlist[n]['type'] in ['TITLE']:&lt;br /&gt;
            continue  # already in right place, not a vertical element&lt;br /&gt;
&lt;br /&gt;
        hgt = 0&lt;br /&gt;
        if hlist[n-1]['type'] == 'BLANK':&lt;br /&gt;
            hgt = 30&lt;br /&gt;
        else:&lt;br /&gt;
            hgt = float(getDim(instance, hlist[n-1]['id'], 'height'))&lt;br /&gt;
&lt;br /&gt;
        hgt += 5&lt;br /&gt;
&lt;br /&gt;
        if firstVert:&lt;br /&gt;
            hgt = 0&lt;br /&gt;
            firstVert = False&lt;br /&gt;
&lt;br /&gt;
        delta += hgt&lt;br /&gt;
&lt;br /&gt;
        if hlist[n]['type'] == 'BLANK': continue&lt;br /&gt;
&lt;br /&gt;
        tabs = hlist[n]['tabs']&lt;br /&gt;
&lt;br /&gt;
        r = doc0.xpath('//s:flowRoot[@id=&amp;quot;%s&amp;quot;]' % hlist[n]['id'], NS)[0]&lt;br /&gt;
        clearId(r, what='transform')&lt;br /&gt;
        r = r.xpath('.//s:rect', NS)[0]&lt;br /&gt;
        r.set('x', str(tabDim[tabs]['x']))&lt;br /&gt;
        y = tabDim[tabs]['y'] + delta&lt;br /&gt;
        #print '%s -&amp;gt; %s' % (str(r.get('y')), str(y))&lt;br /&gt;
        #print tabDim[tabs]['y']&lt;br /&gt;
        r.set('y', str(y))&lt;br /&gt;
&lt;br /&gt;
        if (hlist[n]['type'] == 'TAB' and hlist[n]['tabs'] in bullDim&lt;br /&gt;
            and 'isDone' not in hlist[n]):&lt;br /&gt;
            hlist[n]['isDone'] = True&lt;br /&gt;
            dx = (tabDim[tabs]['x'] &lt;br /&gt;
                  - bullDim[tabs]['x']&lt;br /&gt;
                  - 1.5*bullDim[tabs]['width']&lt;br /&gt;
                  )&lt;br /&gt;
            dy = (y &lt;br /&gt;
                  + tabDim[tabs]['height'] / 2.&lt;br /&gt;
                  - bullDim[tabs]['y'] &lt;br /&gt;
                  - bullDim[tabs]['height'] / 2.&lt;br /&gt;
                  )&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            #print y, tabDim[tabs]['height'], bullDim[tabs]['y'], bullDim[tabs]['height'], dy&lt;br /&gt;
            clone = ET.Element('svg:use')&lt;br /&gt;
            clone.set('transform', 'translate(%f,%f)' % (dx,dy))&lt;br /&gt;
            clone.set('{%s}href'%NS['xlink'], '#bu_%d'%tabs)&lt;br /&gt;
            dst.append(clone)&lt;br /&gt;
            &lt;br /&gt;
            #path = ET.Element('svg:path')&lt;br /&gt;
            #path.set('d', ('M %f,%f' + ' L %f,%f'*5 + ' z') % (&lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y'], &lt;br /&gt;
            #    bullDim[tabs]['x']+20, bullDim[tabs]['y']+bullDim[tabs]['height']/2., &lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y']+bullDim[tabs]['height'], &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y'], &lt;br /&gt;
            #    tabDim[tabs]['x']+20, tabDim[tabs]['y']+tabDim[tabs]['height']/2., &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y']+tabDim[tabs]['height'], &lt;br /&gt;
            #    ))&lt;br /&gt;
            #dst.append(path)&lt;br /&gt;
            #break&lt;br /&gt;
&lt;br /&gt;
    # look for instance specific parts&lt;br /&gt;
    if slideId:&lt;br /&gt;
        f = instancePath(slideId)&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            comp = ET.parse(f)&lt;br /&gt;
            svg = doc0.xpath('//s:svg', NS)[0]&lt;br /&gt;
            g = &amp;quot;{%s}g&amp;quot;%NS['s']&lt;br /&gt;
            k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
            for n, i in enumerate(svg.getchildren()):&lt;br /&gt;
                if i.tag == g and i.get(k, '').startswith('Instance'):&lt;br /&gt;
                    x = comp.xpath('//s:svg//s:g[@i:label=&amp;quot;%s&amp;quot;]'%i.get(k), NS)&lt;br /&gt;
                    if x:&lt;br /&gt;
                        x = x[0]&lt;br /&gt;
                        cpy = clearId(copy.deepcopy(x))&lt;br /&gt;
                        svg[n] = cpy&lt;br /&gt;
            defs = comp.xpath('//s:svg/s:defs/*', NS)&lt;br /&gt;
            dst = doc0.xpath('//s:svg/s:defs', NS)[0]&lt;br /&gt;
            for d in defs:&lt;br /&gt;
                Id = d.get('id')&lt;br /&gt;
                current = doc0.xpath('//s:svg/s:defs/*[@id=&amp;quot;%s&amp;quot;]'%Id, NS)&lt;br /&gt;
                if not current:&lt;br /&gt;
                    cpy = copy.deepcopy(d)&lt;br /&gt;
                    dst.append(cpy)&lt;br /&gt;
&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
    &lt;br /&gt;
    return slideId&lt;br /&gt;
def instancePath(slideId, component = None):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return path to component for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # return os.path.join(slideId, component)&lt;br /&gt;
    return slideId+'.svg'&lt;br /&gt;
def writeComponents(inkscapeFile, slideId):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;write components from file for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not slideId: return&lt;br /&gt;
    doc0 = ET.parse(inkscapeFile)&lt;br /&gt;
    hasComponents = False&lt;br /&gt;
    for i in doc0.xpath('//s:svg//s:g', NS):&lt;br /&gt;
        k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
        # print i.keys(),k&lt;br /&gt;
        if (k in i.keys() and i.get(k).startswith('Instance')):&lt;br /&gt;
            if len(i) &amp;gt; 0:&lt;br /&gt;
                hasComponents = True&lt;br /&gt;
                break&lt;br /&gt;
                # ET.ElementTree(i).write(f)&lt;br /&gt;
                &lt;br /&gt;
    f = instancePath(slideId, 'components')&lt;br /&gt;
    &lt;br /&gt;
    if hasComponents:&lt;br /&gt;
        if (os.path.abspath(os.path.realpath(inkscapeFile)) != &lt;br /&gt;
            os.path.abspath(os.path.realpath(f))):&lt;br /&gt;
            file(f, 'w').write(file(inkscapeFile).read())&lt;br /&gt;
    else:&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            os.remove(f)&lt;br /&gt;
&lt;br /&gt;
if __name__ == '__main__':&lt;br /&gt;
    import sys&lt;br /&gt;
    instanceFromTemplate(sys.argv[1], 'slide%s.svg', file(sys.argv[2]).read())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16458</id>
		<title>InkSlide</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16458"/>
		<updated>2007-10-17T16:03:55Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: /* How it works */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== InkSlide - quick and easy presentations using Inkscape ==&lt;br /&gt;
&lt;br /&gt;
InkSlide produces slides like this:&lt;br /&gt;
&lt;br /&gt;
[[Image:slide0004.jpg]]&lt;br /&gt;
&lt;br /&gt;
from simple text input like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%slide InkSlide: Features&lt;br /&gt;
Features include wrapped top level text and&lt;br /&gt;
    mulitple&lt;br /&gt;
        levels&lt;br /&gt;
            of wrapped bulleted lists with bullets and font&lt;br /&gt;
information taken from the template file.&lt;br /&gt;
&lt;br /&gt;
Slide specific content like this:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
which is updated when the template changes.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An Inkscape file is used as a template file to define the background, title position and font, fonts and positions for text at different levels of indentation, groups to be cloned and used as bullets, etc.&lt;br /&gt;
&lt;br /&gt;
Content specific to a particular slide can also be created in Inkscape, this content merged with the template and text input to make the final slide, so changes to the template after a particular slide is edited in Inkscape are included.&lt;br /&gt;
&lt;br /&gt;
=== Download ===&lt;br /&gt;
&lt;br /&gt;
Copy the [[InkSlide#Code|text below]] into a file called 'inkslide.py'.&lt;br /&gt;
&lt;br /&gt;
=== Use ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python inkslide.py myTemplate.svg mySlides.txt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This creates a slide00xx.svg for each slide in your presentation.&lt;br /&gt;
&lt;br /&gt;
=== How it works ===&lt;br /&gt;
&lt;br /&gt;
First InkSlide parses the text input, something like the example at the top of this page.  It looks for flowed text boxes in the template file with special @id attributes like ''fr_title'' for the title, and ''fr_tab2'' for the second level of bulleted lists.  Copies of these elements are made and placed in a layer called 'Texts'.  At this point, the text is all piled up at the top of the page.&lt;br /&gt;
&lt;br /&gt;
InkSlide saves the file, and uses Inkscape's command line ''--query-height'' and friends to find out how high the wrapped text pieces are.  At it then repositions them properly, adds clones of the bullet markers, etc.  Bullet markers have special @id attributes like ''bu_1'' for the first level of bulleted list.&lt;br /&gt;
&lt;br /&gt;
Finally, InkSlide checks to see if there's an %id tag for the slide, and if so if there's a corresponding Inkscape SVG file with content for this particular slide. This content has to occur in layers whose name starts with ''Instance'', and these layers have to be present in the template file, but you can have more than one, so they can be above and below the regular content.  If such content is found, it's copied into the output slide.&lt;br /&gt;
&lt;br /&gt;
=== To do / status ===&lt;br /&gt;
&lt;br /&gt;
InkSlide is very usable, but is missing some features:&lt;br /&gt;
&lt;br /&gt;
*Inline text formatting (bold, color, etc.) for part of a piece of text&lt;br /&gt;
*Justification switching&lt;br /&gt;
*Verbatim code display&lt;br /&gt;
*Simple image inclusion&lt;br /&gt;
**currently you can do this using the Inkscape editing features&lt;br /&gt;
*more helper tool to make PNG and PDF versions of slides&lt;br /&gt;
*configuring / using a directory for edited slides&lt;br /&gt;
&lt;br /&gt;
=== Bugs ===&lt;br /&gt;
&lt;br /&gt;
*There may be an issue with the way slide specific content is merged into the template producing conflicting @id attributes, but I haven't seen this happen yet.&lt;br /&gt;
&lt;br /&gt;
=== Author ===&lt;br /&gt;
&lt;br /&gt;
Terry Brown, terry-n-brown@yahoo.com - but use underscore, not '-'.&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# inkslide.py $Id$&lt;br /&gt;
# Author: Terry Brown&lt;br /&gt;
# Created: Thu Oct 11 2007&lt;br /&gt;
&lt;br /&gt;
import lxml.etree as ET&lt;br /&gt;
&lt;br /&gt;
import copy&lt;br /&gt;
import subprocess&lt;br /&gt;
import os.path&lt;br /&gt;
&lt;br /&gt;
NS = { 'i': 'http://www.inkscape.org/namespaces/inkscape',&lt;br /&gt;
       's': 'http://www.w3.org/2000/svg',&lt;br /&gt;
       'xlink' : 'http://www.w3.org/1999/xlink'}&lt;br /&gt;
&lt;br /&gt;
# number of defined tablevels, FIXME, could derive from template?&lt;br /&gt;
tabLevels = 4  &lt;br /&gt;
def clearId(x, what='id', NS={}):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;recursively clear @id on element x and descendants&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if what in x.keys():&lt;br /&gt;
        del x.attrib[what]&lt;br /&gt;
    for i in x.xpath('.//*[@%s]'%what, NS):&lt;br /&gt;
        del i.attrib[what]&lt;br /&gt;
    return x&lt;br /&gt;
def getDim(fn, Id, what):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return dimension of element in fn with @id Id, what is&lt;br /&gt;
    x, y, width, height&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cmd = ('inkscape', '--without-gui', '--query-id', Id, '--query-'+what, fn)&lt;br /&gt;
    proc = subprocess.Popen(cmd, stdout = subprocess.PIPE,&lt;br /&gt;
                            stderr = subprocess.PIPE)&lt;br /&gt;
    # make new pipe for stderr to supress chatter from inkscape&lt;br /&gt;
    proc.wait()&lt;br /&gt;
    return proc.stdout.read()&lt;br /&gt;
def textReader(text):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;iterate parts in input text&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not text: return&lt;br /&gt;
    for line in text.split('\n'):&lt;br /&gt;
        if line.startswith('%slide '):&lt;br /&gt;
            yield {'type':'TITLE', 's':line[7:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%pause'):&lt;br /&gt;
            yield {'type':'PAUSE'}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%id '):&lt;br /&gt;
            yield {'type':'ID', 's':line[4:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if not line.strip():&lt;br /&gt;
            yield {'type':'BLANK'}&lt;br /&gt;
            continue&lt;br /&gt;
        tabs = len(line) - len(line.lstrip('\t'))&lt;br /&gt;
        yield {'type':'TAB', 'tabs':tabs, 's':line.strip()}&lt;br /&gt;
&lt;br /&gt;
nid = 0&lt;br /&gt;
def nextId():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return an unsed Id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # FIXME, should inspect templat doc.&lt;br /&gt;
    global nid&lt;br /&gt;
    nid += 1&lt;br /&gt;
    return 'is'+str(nid)&lt;br /&gt;
&lt;br /&gt;
def instanceFromTemplate(template, instance, text, pauseOk = True):&lt;br /&gt;
&lt;br /&gt;
    slide = 0&lt;br /&gt;
&lt;br /&gt;
    hlist = []&lt;br /&gt;
    doc0 = ET.parse(template)&lt;br /&gt;
    slideId = None&lt;br /&gt;
    &lt;br /&gt;
    for t in textReader(text):&lt;br /&gt;
        &lt;br /&gt;
        if t['type'] == 'ID':&lt;br /&gt;
            slideId = t['s']&lt;br /&gt;
            continue&lt;br /&gt;
            &lt;br /&gt;
        if t['type'] == 'TITLE':&lt;br /&gt;
            print t['s']&lt;br /&gt;
            if hlist:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
                hlist = [t]&lt;br /&gt;
                doc0 = ET.parse(template)&lt;br /&gt;
                slideId = None&lt;br /&gt;
            else:&lt;br /&gt;
                hlist.append(t)&lt;br /&gt;
            dst = doc0.xpath('//s:g[@id=&amp;quot;gr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'PAUSE':&lt;br /&gt;
            if pauseOk:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        hlist.append(t)&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'BLANK':&lt;br /&gt;
            continue&lt;br /&gt;
        if t['type'] == 'TAB':&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_tab%d&amp;quot;]' % t['tabs'], NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            #break&lt;br /&gt;
            continue&lt;br /&gt;
           &lt;br /&gt;
    process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
def process(instance, doc0, hlist, slideId, slide):&lt;br /&gt;
&lt;br /&gt;
    instance = instance.replace('%s', '%04d'%slide)&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
&lt;br /&gt;
    bullDim = {}&lt;br /&gt;
    tabDim = {}&lt;br /&gt;
    for n in range(0,tabLevels):&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;bu_%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'width', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'bu_%d'%n, i))&lt;br /&gt;
            bullDim[n] = b&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;fr_tab%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'fr_tab%d'%n, i))&lt;br /&gt;
            tabDim[n] = b&lt;br /&gt;
&lt;br /&gt;
    delta = 0.&lt;br /&gt;
    firstVert = True&lt;br /&gt;
    for n, i in enumerate(hlist):&lt;br /&gt;
        if hlist[n]['type'] in ['TITLE']:&lt;br /&gt;
            continue  # already in right place, not a vertical element&lt;br /&gt;
&lt;br /&gt;
        hgt = 0&lt;br /&gt;
        if hlist[n-1]['type'] == 'BLANK':&lt;br /&gt;
            hgt = 30&lt;br /&gt;
        else:&lt;br /&gt;
            hgt = float(getDim(instance, hlist[n-1]['id'], 'height'))&lt;br /&gt;
&lt;br /&gt;
        hgt += 5&lt;br /&gt;
&lt;br /&gt;
        if firstVert:&lt;br /&gt;
            hgt = 0&lt;br /&gt;
            firstVert = False&lt;br /&gt;
&lt;br /&gt;
        delta += hgt&lt;br /&gt;
&lt;br /&gt;
        if hlist[n]['type'] == 'BLANK': continue&lt;br /&gt;
&lt;br /&gt;
        tabs = hlist[n]['tabs']&lt;br /&gt;
&lt;br /&gt;
        r = doc0.xpath('//s:flowRoot[@id=&amp;quot;%s&amp;quot;]' % hlist[n]['id'], NS)[0]&lt;br /&gt;
        clearId(r, what='transform')&lt;br /&gt;
        r = r.xpath('.//s:rect', NS)[0]&lt;br /&gt;
        r.set('x', str(tabDim[tabs]['x']))&lt;br /&gt;
        y = tabDim[tabs]['y'] + delta&lt;br /&gt;
        #print '%s -&amp;gt; %s' % (str(r.get('y')), str(y))&lt;br /&gt;
        #print tabDim[tabs]['y']&lt;br /&gt;
        r.set('y', str(y))&lt;br /&gt;
&lt;br /&gt;
        if (hlist[n]['type'] == 'TAB' and hlist[n]['tabs'] in bullDim&lt;br /&gt;
            and 'isDone' not in hlist[n]):&lt;br /&gt;
            hlist[n]['isDone'] = True&lt;br /&gt;
            dx = (tabDim[tabs]['x'] &lt;br /&gt;
                  - bullDim[tabs]['x']&lt;br /&gt;
                  - 1.5*bullDim[tabs]['width']&lt;br /&gt;
                  )&lt;br /&gt;
            dy = (y &lt;br /&gt;
                  + tabDim[tabs]['height'] / 2.&lt;br /&gt;
                  - bullDim[tabs]['y'] &lt;br /&gt;
                  - bullDim[tabs]['height'] / 2.&lt;br /&gt;
                  )&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            #print y, tabDim[tabs]['height'], bullDim[tabs]['y'], bullDim[tabs]['height'], dy&lt;br /&gt;
            clone = ET.Element('svg:use')&lt;br /&gt;
            clone.set('transform', 'translate(%f,%f)' % (dx,dy))&lt;br /&gt;
            clone.set('{%s}href'%NS['xlink'], '#bu_%d'%tabs)&lt;br /&gt;
            dst.append(clone)&lt;br /&gt;
            &lt;br /&gt;
            #path = ET.Element('svg:path')&lt;br /&gt;
            #path.set('d', ('M %f,%f' + ' L %f,%f'*5 + ' z') % (&lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y'], &lt;br /&gt;
            #    bullDim[tabs]['x']+20, bullDim[tabs]['y']+bullDim[tabs]['height']/2., &lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y']+bullDim[tabs]['height'], &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y'], &lt;br /&gt;
            #    tabDim[tabs]['x']+20, tabDim[tabs]['y']+tabDim[tabs]['height']/2., &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y']+tabDim[tabs]['height'], &lt;br /&gt;
            #    ))&lt;br /&gt;
            #dst.append(path)&lt;br /&gt;
            #break&lt;br /&gt;
&lt;br /&gt;
    # look for instance specific parts&lt;br /&gt;
    if slideId:&lt;br /&gt;
        f = instancePath(slideId)&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            comp = ET.parse(f)&lt;br /&gt;
            svg = doc0.xpath('//s:svg', NS)[0]&lt;br /&gt;
            g = &amp;quot;{%s}g&amp;quot;%NS['s']&lt;br /&gt;
            k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
            for n, i in enumerate(svg.getchildren()):&lt;br /&gt;
                if i.tag == g and i.get(k, '').startswith('Instance'):&lt;br /&gt;
                    x = comp.xpath('//s:svg//s:g[@i:label=&amp;quot;%s&amp;quot;]'%i.get(k), NS)&lt;br /&gt;
                    if x:&lt;br /&gt;
                        x = x[0]&lt;br /&gt;
                        cpy = clearId(copy.deepcopy(x))&lt;br /&gt;
                        svg[n] = cpy&lt;br /&gt;
            defs = comp.xpath('//s:svg/s:defs/*', NS)&lt;br /&gt;
            dst = doc0.xpath('//s:svg/s:defs', NS)[0]&lt;br /&gt;
            for d in defs:&lt;br /&gt;
                Id = d.get('id')&lt;br /&gt;
                current = doc0.xpath('//s:svg/s:defs/*[@id=&amp;quot;%s&amp;quot;]'%Id, NS)&lt;br /&gt;
                if not current:&lt;br /&gt;
                    cpy = copy.deepcopy(d)&lt;br /&gt;
                    dst.append(cpy)&lt;br /&gt;
&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
    &lt;br /&gt;
    return slideId&lt;br /&gt;
def instancePath(slideId, component = None):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return path to component for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # return os.path.join(slideId, component)&lt;br /&gt;
    return slideId+'.svg'&lt;br /&gt;
def writeComponents(inkscapeFile, slideId):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;write components from file for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not slideId: return&lt;br /&gt;
    doc0 = ET.parse(inkscapeFile)&lt;br /&gt;
    hasComponents = False&lt;br /&gt;
    for i in doc0.xpath('//s:svg//s:g', NS):&lt;br /&gt;
        k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
        # print i.keys(),k&lt;br /&gt;
        if (k in i.keys() and i.get(k).startswith('Instance')):&lt;br /&gt;
            if len(i) &amp;gt; 0:&lt;br /&gt;
                hasComponents = True&lt;br /&gt;
                break&lt;br /&gt;
                # ET.ElementTree(i).write(f)&lt;br /&gt;
                &lt;br /&gt;
    f = instancePath(slideId, 'components')&lt;br /&gt;
    &lt;br /&gt;
    if hasComponents:&lt;br /&gt;
        if (os.path.abspath(os.path.realpath(inkscapeFile)) != &lt;br /&gt;
            os.path.abspath(os.path.realpath(f))):&lt;br /&gt;
            file(f, 'w').write(file(inkscapeFile).read())&lt;br /&gt;
    else:&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            os.remove(f)&lt;br /&gt;
&lt;br /&gt;
if __name__ == '__main__':&lt;br /&gt;
    import sys&lt;br /&gt;
    instanceFromTemplate(sys.argv[1], 'slide%s.svg', file(sys.argv[2]).read())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16457</id>
		<title>InkSlide</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16457"/>
		<updated>2007-10-17T15:48:48Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: /* To do / status */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== InkSlide - quick and easy presentations using Inkscape ==&lt;br /&gt;
&lt;br /&gt;
InkSlide produces slides like this:&lt;br /&gt;
&lt;br /&gt;
[[Image:slide0004.jpg]]&lt;br /&gt;
&lt;br /&gt;
from simple text input like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%slide InkSlide: Features&lt;br /&gt;
Features include wrapped top level text and&lt;br /&gt;
    mulitple&lt;br /&gt;
        levels&lt;br /&gt;
            of wrapped bulleted lists with bullets and font&lt;br /&gt;
information taken from the template file.&lt;br /&gt;
&lt;br /&gt;
Slide specific content like this:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
which is updated when the template changes.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An Inkscape file is used as a template file to define the background, title position and font, fonts and positions for text at different levels of indentation, groups to be cloned and used as bullets, etc.&lt;br /&gt;
&lt;br /&gt;
Content specific to a particular slide can also be created in Inkscape, this content merged with the template and text input to make the final slide, so changes to the template after a particular slide is edited in Inkscape are included.&lt;br /&gt;
&lt;br /&gt;
=== Download ===&lt;br /&gt;
&lt;br /&gt;
Copy the [[InkSlide#Code|text below]] into a file called 'inkslide.py'.&lt;br /&gt;
&lt;br /&gt;
=== Use ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python inkslide.py myTemplate.svg mySlides.txt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This creates a slide00xx.svg for each slide in your presentation.&lt;br /&gt;
&lt;br /&gt;
=== How it works ===&lt;br /&gt;
&lt;br /&gt;
=== To do / status ===&lt;br /&gt;
&lt;br /&gt;
InkSlide is very usable, but is missing some features:&lt;br /&gt;
&lt;br /&gt;
*Inline text formatting (bold, color, etc.) for part of a piece of text&lt;br /&gt;
*Justification switching&lt;br /&gt;
*Verbatim code display&lt;br /&gt;
*Simple image inclusion&lt;br /&gt;
**currently you can do this using the Inkscape editing features&lt;br /&gt;
*more helper tool to make PNG and PDF versions of slides&lt;br /&gt;
*configuring / using a directory for edited slides&lt;br /&gt;
&lt;br /&gt;
=== Bugs ===&lt;br /&gt;
&lt;br /&gt;
*There may be an issue with the way slide specific content is merged into the template producing conflicting @id attributes, but I haven't seen this happen yet.&lt;br /&gt;
&lt;br /&gt;
=== Author ===&lt;br /&gt;
&lt;br /&gt;
Terry Brown, terry-n-brown@yahoo.com - but use underscore, not '-'.&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# inkslide.py $Id$&lt;br /&gt;
# Author: Terry Brown&lt;br /&gt;
# Created: Thu Oct 11 2007&lt;br /&gt;
&lt;br /&gt;
import lxml.etree as ET&lt;br /&gt;
&lt;br /&gt;
import copy&lt;br /&gt;
import subprocess&lt;br /&gt;
import os.path&lt;br /&gt;
&lt;br /&gt;
NS = { 'i': 'http://www.inkscape.org/namespaces/inkscape',&lt;br /&gt;
       's': 'http://www.w3.org/2000/svg',&lt;br /&gt;
       'xlink' : 'http://www.w3.org/1999/xlink'}&lt;br /&gt;
&lt;br /&gt;
# number of defined tablevels, FIXME, could derive from template?&lt;br /&gt;
tabLevels = 4  &lt;br /&gt;
def clearId(x, what='id', NS={}):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;recursively clear @id on element x and descendants&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if what in x.keys():&lt;br /&gt;
        del x.attrib[what]&lt;br /&gt;
    for i in x.xpath('.//*[@%s]'%what, NS):&lt;br /&gt;
        del i.attrib[what]&lt;br /&gt;
    return x&lt;br /&gt;
def getDim(fn, Id, what):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return dimension of element in fn with @id Id, what is&lt;br /&gt;
    x, y, width, height&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cmd = ('inkscape', '--without-gui', '--query-id', Id, '--query-'+what, fn)&lt;br /&gt;
    proc = subprocess.Popen(cmd, stdout = subprocess.PIPE,&lt;br /&gt;
                            stderr = subprocess.PIPE)&lt;br /&gt;
    # make new pipe for stderr to supress chatter from inkscape&lt;br /&gt;
    proc.wait()&lt;br /&gt;
    return proc.stdout.read()&lt;br /&gt;
def textReader(text):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;iterate parts in input text&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not text: return&lt;br /&gt;
    for line in text.split('\n'):&lt;br /&gt;
        if line.startswith('%slide '):&lt;br /&gt;
            yield {'type':'TITLE', 's':line[7:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%pause'):&lt;br /&gt;
            yield {'type':'PAUSE'}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%id '):&lt;br /&gt;
            yield {'type':'ID', 's':line[4:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if not line.strip():&lt;br /&gt;
            yield {'type':'BLANK'}&lt;br /&gt;
            continue&lt;br /&gt;
        tabs = len(line) - len(line.lstrip('\t'))&lt;br /&gt;
        yield {'type':'TAB', 'tabs':tabs, 's':line.strip()}&lt;br /&gt;
&lt;br /&gt;
nid = 0&lt;br /&gt;
def nextId():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return an unsed Id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # FIXME, should inspect templat doc.&lt;br /&gt;
    global nid&lt;br /&gt;
    nid += 1&lt;br /&gt;
    return 'is'+str(nid)&lt;br /&gt;
&lt;br /&gt;
def instanceFromTemplate(template, instance, text, pauseOk = True):&lt;br /&gt;
&lt;br /&gt;
    slide = 0&lt;br /&gt;
&lt;br /&gt;
    hlist = []&lt;br /&gt;
    doc0 = ET.parse(template)&lt;br /&gt;
    slideId = None&lt;br /&gt;
    &lt;br /&gt;
    for t in textReader(text):&lt;br /&gt;
        &lt;br /&gt;
        if t['type'] == 'ID':&lt;br /&gt;
            slideId = t['s']&lt;br /&gt;
            continue&lt;br /&gt;
            &lt;br /&gt;
        if t['type'] == 'TITLE':&lt;br /&gt;
            print t['s']&lt;br /&gt;
            if hlist:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
                hlist = [t]&lt;br /&gt;
                doc0 = ET.parse(template)&lt;br /&gt;
                slideId = None&lt;br /&gt;
            else:&lt;br /&gt;
                hlist.append(t)&lt;br /&gt;
            dst = doc0.xpath('//s:g[@id=&amp;quot;gr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'PAUSE':&lt;br /&gt;
            if pauseOk:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        hlist.append(t)&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'BLANK':&lt;br /&gt;
            continue&lt;br /&gt;
        if t['type'] == 'TAB':&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_tab%d&amp;quot;]' % t['tabs'], NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            #break&lt;br /&gt;
            continue&lt;br /&gt;
           &lt;br /&gt;
    process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
def process(instance, doc0, hlist, slideId, slide):&lt;br /&gt;
&lt;br /&gt;
    instance = instance.replace('%s', '%04d'%slide)&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
&lt;br /&gt;
    bullDim = {}&lt;br /&gt;
    tabDim = {}&lt;br /&gt;
    for n in range(0,tabLevels):&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;bu_%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'width', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'bu_%d'%n, i))&lt;br /&gt;
            bullDim[n] = b&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;fr_tab%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'fr_tab%d'%n, i))&lt;br /&gt;
            tabDim[n] = b&lt;br /&gt;
&lt;br /&gt;
    delta = 0.&lt;br /&gt;
    firstVert = True&lt;br /&gt;
    for n, i in enumerate(hlist):&lt;br /&gt;
        if hlist[n]['type'] in ['TITLE']:&lt;br /&gt;
            continue  # already in right place, not a vertical element&lt;br /&gt;
&lt;br /&gt;
        hgt = 0&lt;br /&gt;
        if hlist[n-1]['type'] == 'BLANK':&lt;br /&gt;
            hgt = 30&lt;br /&gt;
        else:&lt;br /&gt;
            hgt = float(getDim(instance, hlist[n-1]['id'], 'height'))&lt;br /&gt;
&lt;br /&gt;
        hgt += 5&lt;br /&gt;
&lt;br /&gt;
        if firstVert:&lt;br /&gt;
            hgt = 0&lt;br /&gt;
            firstVert = False&lt;br /&gt;
&lt;br /&gt;
        delta += hgt&lt;br /&gt;
&lt;br /&gt;
        if hlist[n]['type'] == 'BLANK': continue&lt;br /&gt;
&lt;br /&gt;
        tabs = hlist[n]['tabs']&lt;br /&gt;
&lt;br /&gt;
        r = doc0.xpath('//s:flowRoot[@id=&amp;quot;%s&amp;quot;]' % hlist[n]['id'], NS)[0]&lt;br /&gt;
        clearId(r, what='transform')&lt;br /&gt;
        r = r.xpath('.//s:rect', NS)[0]&lt;br /&gt;
        r.set('x', str(tabDim[tabs]['x']))&lt;br /&gt;
        y = tabDim[tabs]['y'] + delta&lt;br /&gt;
        #print '%s -&amp;gt; %s' % (str(r.get('y')), str(y))&lt;br /&gt;
        #print tabDim[tabs]['y']&lt;br /&gt;
        r.set('y', str(y))&lt;br /&gt;
&lt;br /&gt;
        if (hlist[n]['type'] == 'TAB' and hlist[n]['tabs'] in bullDim&lt;br /&gt;
            and 'isDone' not in hlist[n]):&lt;br /&gt;
            hlist[n]['isDone'] = True&lt;br /&gt;
            dx = (tabDim[tabs]['x'] &lt;br /&gt;
                  - bullDim[tabs]['x']&lt;br /&gt;
                  - 1.5*bullDim[tabs]['width']&lt;br /&gt;
                  )&lt;br /&gt;
            dy = (y &lt;br /&gt;
                  + tabDim[tabs]['height'] / 2.&lt;br /&gt;
                  - bullDim[tabs]['y'] &lt;br /&gt;
                  - bullDim[tabs]['height'] / 2.&lt;br /&gt;
                  )&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            #print y, tabDim[tabs]['height'], bullDim[tabs]['y'], bullDim[tabs]['height'], dy&lt;br /&gt;
            clone = ET.Element('svg:use')&lt;br /&gt;
            clone.set('transform', 'translate(%f,%f)' % (dx,dy))&lt;br /&gt;
            clone.set('{%s}href'%NS['xlink'], '#bu_%d'%tabs)&lt;br /&gt;
            dst.append(clone)&lt;br /&gt;
            &lt;br /&gt;
            #path = ET.Element('svg:path')&lt;br /&gt;
            #path.set('d', ('M %f,%f' + ' L %f,%f'*5 + ' z') % (&lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y'], &lt;br /&gt;
            #    bullDim[tabs]['x']+20, bullDim[tabs]['y']+bullDim[tabs]['height']/2., &lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y']+bullDim[tabs]['height'], &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y'], &lt;br /&gt;
            #    tabDim[tabs]['x']+20, tabDim[tabs]['y']+tabDim[tabs]['height']/2., &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y']+tabDim[tabs]['height'], &lt;br /&gt;
            #    ))&lt;br /&gt;
            #dst.append(path)&lt;br /&gt;
            #break&lt;br /&gt;
&lt;br /&gt;
    # look for instance specific parts&lt;br /&gt;
    if slideId:&lt;br /&gt;
        f = instancePath(slideId)&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            comp = ET.parse(f)&lt;br /&gt;
            svg = doc0.xpath('//s:svg', NS)[0]&lt;br /&gt;
            g = &amp;quot;{%s}g&amp;quot;%NS['s']&lt;br /&gt;
            k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
            for n, i in enumerate(svg.getchildren()):&lt;br /&gt;
                if i.tag == g and i.get(k, '').startswith('Instance'):&lt;br /&gt;
                    x = comp.xpath('//s:svg//s:g[@i:label=&amp;quot;%s&amp;quot;]'%i.get(k), NS)&lt;br /&gt;
                    if x:&lt;br /&gt;
                        x = x[0]&lt;br /&gt;
                        cpy = clearId(copy.deepcopy(x))&lt;br /&gt;
                        svg[n] = cpy&lt;br /&gt;
            defs = comp.xpath('//s:svg/s:defs/*', NS)&lt;br /&gt;
            dst = doc0.xpath('//s:svg/s:defs', NS)[0]&lt;br /&gt;
            for d in defs:&lt;br /&gt;
                Id = d.get('id')&lt;br /&gt;
                current = doc0.xpath('//s:svg/s:defs/*[@id=&amp;quot;%s&amp;quot;]'%Id, NS)&lt;br /&gt;
                if not current:&lt;br /&gt;
                    cpy = copy.deepcopy(d)&lt;br /&gt;
                    dst.append(cpy)&lt;br /&gt;
&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
    &lt;br /&gt;
    return slideId&lt;br /&gt;
def instancePath(slideId, component = None):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return path to component for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # return os.path.join(slideId, component)&lt;br /&gt;
    return slideId+'.svg'&lt;br /&gt;
def writeComponents(inkscapeFile, slideId):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;write components from file for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not slideId: return&lt;br /&gt;
    doc0 = ET.parse(inkscapeFile)&lt;br /&gt;
    hasComponents = False&lt;br /&gt;
    for i in doc0.xpath('//s:svg//s:g', NS):&lt;br /&gt;
        k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
        # print i.keys(),k&lt;br /&gt;
        if (k in i.keys() and i.get(k).startswith('Instance')):&lt;br /&gt;
            if len(i) &amp;gt; 0:&lt;br /&gt;
                hasComponents = True&lt;br /&gt;
                break&lt;br /&gt;
                # ET.ElementTree(i).write(f)&lt;br /&gt;
                &lt;br /&gt;
    f = instancePath(slideId, 'components')&lt;br /&gt;
    &lt;br /&gt;
    if hasComponents:&lt;br /&gt;
        if (os.path.abspath(os.path.realpath(inkscapeFile)) != &lt;br /&gt;
            os.path.abspath(os.path.realpath(f))):&lt;br /&gt;
            file(f, 'w').write(file(inkscapeFile).read())&lt;br /&gt;
    else:&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            os.remove(f)&lt;br /&gt;
&lt;br /&gt;
if __name__ == '__main__':&lt;br /&gt;
    import sys&lt;br /&gt;
    instanceFromTemplate(sys.argv[1], 'slide%s.svg', file(sys.argv[2]).read())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16456</id>
		<title>InkSlide</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16456"/>
		<updated>2007-10-17T15:43:53Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: /* InkSlide - quick and easy presentations using Inkscape */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== InkSlide - quick and easy presentations using Inkscape ==&lt;br /&gt;
&lt;br /&gt;
InkSlide produces slides like this:&lt;br /&gt;
&lt;br /&gt;
[[Image:slide0004.jpg]]&lt;br /&gt;
&lt;br /&gt;
from simple text input like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%slide InkSlide: Features&lt;br /&gt;
Features include wrapped top level text and&lt;br /&gt;
    mulitple&lt;br /&gt;
        levels&lt;br /&gt;
            of wrapped bulleted lists with bullets and font&lt;br /&gt;
information taken from the template file.&lt;br /&gt;
&lt;br /&gt;
Slide specific content like this:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
which is updated when the template changes.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An Inkscape file is used as a template file to define the background, title position and font, fonts and positions for text at different levels of indentation, groups to be cloned and used as bullets, etc.&lt;br /&gt;
&lt;br /&gt;
Content specific to a particular slide can also be created in Inkscape, this content merged with the template and text input to make the final slide, so changes to the template after a particular slide is edited in Inkscape are included.&lt;br /&gt;
&lt;br /&gt;
=== Download ===&lt;br /&gt;
&lt;br /&gt;
Copy the [[InkSlide#Code|text below]] into a file called 'inkslide.py'.&lt;br /&gt;
&lt;br /&gt;
=== Use ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python inkslide.py myTemplate.svg mySlides.txt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This creates a slide00xx.svg for each slide in your presentation.&lt;br /&gt;
&lt;br /&gt;
=== How it works ===&lt;br /&gt;
&lt;br /&gt;
=== To do / status ===&lt;br /&gt;
&lt;br /&gt;
InkSlide is very usable, but is missing some features:&lt;br /&gt;
&lt;br /&gt;
*Inline text formatting (bold, color, etc.) for part of a piece of text&lt;br /&gt;
*Justification switching&lt;br /&gt;
*Verbatim code display&lt;br /&gt;
*Simple image inclusion&lt;br /&gt;
  * currently you can do this using the Inkscape editing features&lt;br /&gt;
*more helper tool to make PNG and PDF versions of slides&lt;br /&gt;
*configuring / using a directory for edited slides&lt;br /&gt;
&lt;br /&gt;
=== Bugs ===&lt;br /&gt;
&lt;br /&gt;
*There may be an issue with the way slide specific content is merged into the template producing conflicting @id attributes, but I haven't seen this happen yet.&lt;br /&gt;
&lt;br /&gt;
=== Author ===&lt;br /&gt;
&lt;br /&gt;
Terry Brown, terry-n-brown@yahoo.com - but use underscore, not '-'.&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# inkslide.py $Id$&lt;br /&gt;
# Author: Terry Brown&lt;br /&gt;
# Created: Thu Oct 11 2007&lt;br /&gt;
&lt;br /&gt;
import lxml.etree as ET&lt;br /&gt;
&lt;br /&gt;
import copy&lt;br /&gt;
import subprocess&lt;br /&gt;
import os.path&lt;br /&gt;
&lt;br /&gt;
NS = { 'i': 'http://www.inkscape.org/namespaces/inkscape',&lt;br /&gt;
       's': 'http://www.w3.org/2000/svg',&lt;br /&gt;
       'xlink' : 'http://www.w3.org/1999/xlink'}&lt;br /&gt;
&lt;br /&gt;
# number of defined tablevels, FIXME, could derive from template?&lt;br /&gt;
tabLevels = 4  &lt;br /&gt;
def clearId(x, what='id', NS={}):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;recursively clear @id on element x and descendants&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if what in x.keys():&lt;br /&gt;
        del x.attrib[what]&lt;br /&gt;
    for i in x.xpath('.//*[@%s]'%what, NS):&lt;br /&gt;
        del i.attrib[what]&lt;br /&gt;
    return x&lt;br /&gt;
def getDim(fn, Id, what):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return dimension of element in fn with @id Id, what is&lt;br /&gt;
    x, y, width, height&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cmd = ('inkscape', '--without-gui', '--query-id', Id, '--query-'+what, fn)&lt;br /&gt;
    proc = subprocess.Popen(cmd, stdout = subprocess.PIPE,&lt;br /&gt;
                            stderr = subprocess.PIPE)&lt;br /&gt;
    # make new pipe for stderr to supress chatter from inkscape&lt;br /&gt;
    proc.wait()&lt;br /&gt;
    return proc.stdout.read()&lt;br /&gt;
def textReader(text):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;iterate parts in input text&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not text: return&lt;br /&gt;
    for line in text.split('\n'):&lt;br /&gt;
        if line.startswith('%slide '):&lt;br /&gt;
            yield {'type':'TITLE', 's':line[7:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%pause'):&lt;br /&gt;
            yield {'type':'PAUSE'}&lt;br /&gt;
            continue&lt;br /&gt;
        if line.startswith('%id '):&lt;br /&gt;
            yield {'type':'ID', 's':line[4:].strip()}&lt;br /&gt;
            continue&lt;br /&gt;
        if not line.strip():&lt;br /&gt;
            yield {'type':'BLANK'}&lt;br /&gt;
            continue&lt;br /&gt;
        tabs = len(line) - len(line.lstrip('\t'))&lt;br /&gt;
        yield {'type':'TAB', 'tabs':tabs, 's':line.strip()}&lt;br /&gt;
&lt;br /&gt;
nid = 0&lt;br /&gt;
def nextId():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return an unsed Id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # FIXME, should inspect templat doc.&lt;br /&gt;
    global nid&lt;br /&gt;
    nid += 1&lt;br /&gt;
    return 'is'+str(nid)&lt;br /&gt;
&lt;br /&gt;
def instanceFromTemplate(template, instance, text, pauseOk = True):&lt;br /&gt;
&lt;br /&gt;
    slide = 0&lt;br /&gt;
&lt;br /&gt;
    hlist = []&lt;br /&gt;
    doc0 = ET.parse(template)&lt;br /&gt;
    slideId = None&lt;br /&gt;
    &lt;br /&gt;
    for t in textReader(text):&lt;br /&gt;
        &lt;br /&gt;
        if t['type'] == 'ID':&lt;br /&gt;
            slideId = t['s']&lt;br /&gt;
            continue&lt;br /&gt;
            &lt;br /&gt;
        if t['type'] == 'TITLE':&lt;br /&gt;
            print t['s']&lt;br /&gt;
            if hlist:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
                hlist = [t]&lt;br /&gt;
                doc0 = ET.parse(template)&lt;br /&gt;
                slideId = None&lt;br /&gt;
            else:&lt;br /&gt;
                hlist.append(t)&lt;br /&gt;
            dst = doc0.xpath('//s:g[@id=&amp;quot;gr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_title&amp;quot;]', NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'PAUSE':&lt;br /&gt;
            if pauseOk:&lt;br /&gt;
                process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
                slide = slide + 1&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        hlist.append(t)&lt;br /&gt;
&lt;br /&gt;
        if t['type'] == 'BLANK':&lt;br /&gt;
            continue&lt;br /&gt;
        if t['type'] == 'TAB':&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            src = doc0.xpath('//s:flowRoot[@id=&amp;quot;fr_tab%d&amp;quot;]' % t['tabs'], NS)[0]&lt;br /&gt;
            cpy = clearId(copy.deepcopy(src))&lt;br /&gt;
            Id = nextId()&lt;br /&gt;
            cpy.set('id', Id)&lt;br /&gt;
            hlist[-1]['id']=Id&lt;br /&gt;
            cpy.xpath('s:flowPara',NS)[0].text = t['s']&lt;br /&gt;
            dst.append(cpy)&lt;br /&gt;
            #break&lt;br /&gt;
            continue&lt;br /&gt;
           &lt;br /&gt;
    process(instance, doc0, hlist, slideId, slide)&lt;br /&gt;
def process(instance, doc0, hlist, slideId, slide):&lt;br /&gt;
&lt;br /&gt;
    instance = instance.replace('%s', '%04d'%slide)&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
&lt;br /&gt;
    bullDim = {}&lt;br /&gt;
    tabDim = {}&lt;br /&gt;
    for n in range(0,tabLevels):&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;bu_%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'width', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'bu_%d'%n, i))&lt;br /&gt;
            bullDim[n] = b&lt;br /&gt;
        b = {}&lt;br /&gt;
        x = doc0.xpath('//*[@id=&amp;quot;fr_tab%d&amp;quot;]'%n, NS)&lt;br /&gt;
        if x:&lt;br /&gt;
            # clearId(x[0], what='transform')&lt;br /&gt;
            for i in 'x', 'y', 'height':&lt;br /&gt;
                b[i] = float(getDim(instance, 'fr_tab%d'%n, i))&lt;br /&gt;
            tabDim[n] = b&lt;br /&gt;
&lt;br /&gt;
    delta = 0.&lt;br /&gt;
    firstVert = True&lt;br /&gt;
    for n, i in enumerate(hlist):&lt;br /&gt;
        if hlist[n]['type'] in ['TITLE']:&lt;br /&gt;
            continue  # already in right place, not a vertical element&lt;br /&gt;
&lt;br /&gt;
        hgt = 0&lt;br /&gt;
        if hlist[n-1]['type'] == 'BLANK':&lt;br /&gt;
            hgt = 30&lt;br /&gt;
        else:&lt;br /&gt;
            hgt = float(getDim(instance, hlist[n-1]['id'], 'height'))&lt;br /&gt;
&lt;br /&gt;
        hgt += 5&lt;br /&gt;
&lt;br /&gt;
        if firstVert:&lt;br /&gt;
            hgt = 0&lt;br /&gt;
            firstVert = False&lt;br /&gt;
&lt;br /&gt;
        delta += hgt&lt;br /&gt;
&lt;br /&gt;
        if hlist[n]['type'] == 'BLANK': continue&lt;br /&gt;
&lt;br /&gt;
        tabs = hlist[n]['tabs']&lt;br /&gt;
&lt;br /&gt;
        r = doc0.xpath('//s:flowRoot[@id=&amp;quot;%s&amp;quot;]' % hlist[n]['id'], NS)[0]&lt;br /&gt;
        clearId(r, what='transform')&lt;br /&gt;
        r = r.xpath('.//s:rect', NS)[0]&lt;br /&gt;
        r.set('x', str(tabDim[tabs]['x']))&lt;br /&gt;
        y = tabDim[tabs]['y'] + delta&lt;br /&gt;
        #print '%s -&amp;gt; %s' % (str(r.get('y')), str(y))&lt;br /&gt;
        #print tabDim[tabs]['y']&lt;br /&gt;
        r.set('y', str(y))&lt;br /&gt;
&lt;br /&gt;
        if (hlist[n]['type'] == 'TAB' and hlist[n]['tabs'] in bullDim&lt;br /&gt;
            and 'isDone' not in hlist[n]):&lt;br /&gt;
            hlist[n]['isDone'] = True&lt;br /&gt;
            dx = (tabDim[tabs]['x'] &lt;br /&gt;
                  - bullDim[tabs]['x']&lt;br /&gt;
                  - 1.5*bullDim[tabs]['width']&lt;br /&gt;
                  )&lt;br /&gt;
            dy = (y &lt;br /&gt;
                  + tabDim[tabs]['height'] / 2.&lt;br /&gt;
                  - bullDim[tabs]['y'] &lt;br /&gt;
                  - bullDim[tabs]['height'] / 2.&lt;br /&gt;
                  )&lt;br /&gt;
            dst = doc0.xpath('//s:g[@i:label=&amp;quot;Texts&amp;quot;]', NS)[0]&lt;br /&gt;
            #print y, tabDim[tabs]['height'], bullDim[tabs]['y'], bullDim[tabs]['height'], dy&lt;br /&gt;
            clone = ET.Element('svg:use')&lt;br /&gt;
            clone.set('transform', 'translate(%f,%f)' % (dx,dy))&lt;br /&gt;
            clone.set('{%s}href'%NS['xlink'], '#bu_%d'%tabs)&lt;br /&gt;
            dst.append(clone)&lt;br /&gt;
            &lt;br /&gt;
            #path = ET.Element('svg:path')&lt;br /&gt;
            #path.set('d', ('M %f,%f' + ' L %f,%f'*5 + ' z') % (&lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y'], &lt;br /&gt;
            #    bullDim[tabs]['x']+20, bullDim[tabs]['y']+bullDim[tabs]['height']/2., &lt;br /&gt;
            #    bullDim[tabs]['x'], bullDim[tabs]['y']+bullDim[tabs]['height'], &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y'], &lt;br /&gt;
            #    tabDim[tabs]['x']+20, tabDim[tabs]['y']+tabDim[tabs]['height']/2., &lt;br /&gt;
            #    tabDim[tabs]['x'], tabDim[tabs]['y']+tabDim[tabs]['height'], &lt;br /&gt;
            #    ))&lt;br /&gt;
            #dst.append(path)&lt;br /&gt;
            #break&lt;br /&gt;
&lt;br /&gt;
    # look for instance specific parts&lt;br /&gt;
    if slideId:&lt;br /&gt;
        f = instancePath(slideId)&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            comp = ET.parse(f)&lt;br /&gt;
            svg = doc0.xpath('//s:svg', NS)[0]&lt;br /&gt;
            g = &amp;quot;{%s}g&amp;quot;%NS['s']&lt;br /&gt;
            k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
            for n, i in enumerate(svg.getchildren()):&lt;br /&gt;
                if i.tag == g and i.get(k, '').startswith('Instance'):&lt;br /&gt;
                    x = comp.xpath('//s:svg//s:g[@i:label=&amp;quot;%s&amp;quot;]'%i.get(k), NS)&lt;br /&gt;
                    if x:&lt;br /&gt;
                        x = x[0]&lt;br /&gt;
                        cpy = clearId(copy.deepcopy(x))&lt;br /&gt;
                        svg[n] = cpy&lt;br /&gt;
            defs = comp.xpath('//s:svg/s:defs/*', NS)&lt;br /&gt;
            dst = doc0.xpath('//s:svg/s:defs', NS)[0]&lt;br /&gt;
            for d in defs:&lt;br /&gt;
                Id = d.get('id')&lt;br /&gt;
                current = doc0.xpath('//s:svg/s:defs/*[@id=&amp;quot;%s&amp;quot;]'%Id, NS)&lt;br /&gt;
                if not current:&lt;br /&gt;
                    cpy = copy.deepcopy(d)&lt;br /&gt;
                    dst.append(cpy)&lt;br /&gt;
&lt;br /&gt;
    doc0.write(file(instance, 'w'))&lt;br /&gt;
    &lt;br /&gt;
    return slideId&lt;br /&gt;
def instancePath(slideId, component = None):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;return path to component for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # return os.path.join(slideId, component)&lt;br /&gt;
    return slideId+'.svg'&lt;br /&gt;
def writeComponents(inkscapeFile, slideId):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;write components from file for slide&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not slideId: return&lt;br /&gt;
    doc0 = ET.parse(inkscapeFile)&lt;br /&gt;
    hasComponents = False&lt;br /&gt;
    for i in doc0.xpath('//s:svg//s:g', NS):&lt;br /&gt;
        k = &amp;quot;{%s}label&amp;quot;%NS['i']&lt;br /&gt;
        # print i.keys(),k&lt;br /&gt;
        if (k in i.keys() and i.get(k).startswith('Instance')):&lt;br /&gt;
            if len(i) &amp;gt; 0:&lt;br /&gt;
                hasComponents = True&lt;br /&gt;
                break&lt;br /&gt;
                # ET.ElementTree(i).write(f)&lt;br /&gt;
                &lt;br /&gt;
    f = instancePath(slideId, 'components')&lt;br /&gt;
    &lt;br /&gt;
    if hasComponents:&lt;br /&gt;
        if (os.path.abspath(os.path.realpath(inkscapeFile)) != &lt;br /&gt;
            os.path.abspath(os.path.realpath(f))):&lt;br /&gt;
            file(f, 'w').write(file(inkscapeFile).read())&lt;br /&gt;
    else:&lt;br /&gt;
        if os.path.isfile(f):&lt;br /&gt;
            os.remove(f)&lt;br /&gt;
&lt;br /&gt;
if __name__ == '__main__':&lt;br /&gt;
    import sys&lt;br /&gt;
    instanceFromTemplate(sys.argv[1], 'slide%s.svg', file(sys.argv[2]).read())&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16455</id>
		<title>InkSlide</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16455"/>
		<updated>2007-10-17T15:22:21Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: /* Download */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== InkSlide - quick and easy presentations using Inkscape ==&lt;br /&gt;
&lt;br /&gt;
InkSlide produces slides like this:&lt;br /&gt;
&lt;br /&gt;
[[Image:slide0004.jpg]]&lt;br /&gt;
&lt;br /&gt;
from simple text input like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%slide InkSlide: Features&lt;br /&gt;
Features include wrapped top level text and&lt;br /&gt;
    mulitple&lt;br /&gt;
        levels&lt;br /&gt;
            of wrapped bulleted lists with bullets and font&lt;br /&gt;
information taken from the template file.&lt;br /&gt;
&lt;br /&gt;
Slide specific content like this:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
which is updated when the template changes.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An Inkscape file is used as a template file to define the background, title position and font, fonts and positions for text at different levels of indentation, groups to be cloned and used as bullets, etc.&lt;br /&gt;
&lt;br /&gt;
Content specific to a particular slide can also be created in Inkscape, this content merged with the template and text input to make the final slide, so changes to the template after a particular slide is edited in Inkscape are included.&lt;br /&gt;
&lt;br /&gt;
=== Download ===&lt;br /&gt;
&lt;br /&gt;
[[Media:inkslide.py]]&lt;br /&gt;
&lt;br /&gt;
=== Use ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python inkslide.py myTemplate.svg mySlides.txt&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16454</id>
		<title>InkSlide</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16454"/>
		<updated>2007-10-17T15:21:28Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: /* InkSlide - quick and easy presentations using Inkscape */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== InkSlide - quick and easy presentations using Inkscape ==&lt;br /&gt;
&lt;br /&gt;
InkSlide produces slides like this:&lt;br /&gt;
&lt;br /&gt;
[[Image:slide0004.jpg]]&lt;br /&gt;
&lt;br /&gt;
from simple text input like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%slide InkSlide: Features&lt;br /&gt;
Features include wrapped top level text and&lt;br /&gt;
    mulitple&lt;br /&gt;
        levels&lt;br /&gt;
            of wrapped bulleted lists with bullets and font&lt;br /&gt;
information taken from the template file.&lt;br /&gt;
&lt;br /&gt;
Slide specific content like this:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
which is updated when the template changes.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An Inkscape file is used as a template file to define the background, title position and font, fonts and positions for text at different levels of indentation, groups to be cloned and used as bullets, etc.&lt;br /&gt;
&lt;br /&gt;
Content specific to a particular slide can also be created in Inkscape, this content merged with the template and text input to make the final slide, so changes to the template after a particular slide is edited in Inkscape are included.&lt;br /&gt;
&lt;br /&gt;
=== Download ===&lt;br /&gt;
&lt;br /&gt;
[[inkslide.py]]&lt;br /&gt;
&lt;br /&gt;
=== Use ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python inkslide.py myTemplate.svg mySlides.txt&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=File:Slide0004.jpg&amp;diff=16453</id>
		<title>File:Slide0004.jpg</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=File:Slide0004.jpg&amp;diff=16453"/>
		<updated>2007-10-17T15:13:14Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: Example of a slide created with InkSlide&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Example of a slide created with InkSlide&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16452</id>
		<title>InkSlide</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=InkSlide&amp;diff=16452"/>
		<updated>2007-10-17T15:12:08Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: New page: == InkSlide - quick and easy presentations using Inkscape ==  InkSlide produces slides like this:  Image:slide0004.jpg  from simple text input like this:  &amp;lt;pre&amp;gt; %slide InkSlide: Featur...&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== InkSlide - quick and easy presentations using Inkscape ==&lt;br /&gt;
&lt;br /&gt;
InkSlide produces slides like this:&lt;br /&gt;
&lt;br /&gt;
[[Image:slide0004.jpg]]&lt;br /&gt;
&lt;br /&gt;
from simple text input like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%slide InkSlide: Features&lt;br /&gt;
Features include wrapped top level text and&lt;br /&gt;
        mulitple&lt;br /&gt;
                levels&lt;br /&gt;
                        of wrapped bulleted lists with bullets and font informat&lt;br /&gt;
ion taken from the template file.&lt;br /&gt;
&lt;br /&gt;
Slide specific content like this:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
which is updated when the template changes.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
	<entry>
		<id>https://wiki.inkscape.org/wiki/index.php?title=Tools&amp;diff=16451</id>
		<title>Tools</title>
		<link rel="alternate" type="text/html" href="https://wiki.inkscape.org/wiki/index.php?title=Tools&amp;diff=16451"/>
		<updated>2007-10-17T15:01:35Z</updated>

		<summary type="html">&lt;p&gt;Terry brown: /* Multipage presentation or book */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== SVG Validator === &lt;br /&gt;
A [http://jiggles.w3.org/svgvalidator/ validator service] (and downloadable tool) is provided by the [[W3C]].  It will complain about sodipodi or inkscape namespaced items in the document, unless you've exported to plain SVG.&lt;br /&gt;
&lt;br /&gt;
=== SVG::Metadata === &lt;br /&gt;
[http://search.cpan.org/CPAN/authors/id/B/BR/BRYCE/SVG-Metadata-0.10.tar.gz Perl module and set of scripts] to help in adding and processing metadata in SVG files.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Vectorize/trace ==&lt;br /&gt;
=== Potrace === &lt;br /&gt;
[[Potrace]] is now embedded into Inkscape. You don't need to run it separately. &lt;br /&gt;
*[[Potrace|Using Potrace through Inkscape]]&lt;br /&gt;
*[http://potrace.sourceforge.net/ Potrace page at Sourceforge]&lt;br /&gt;
&lt;br /&gt;
=== Autotrace and Frontline (alternatives to Potrace) === &lt;br /&gt;
'''Autotrace''' an open source vectorization program by Martin Weber. That means you can convert bitmap images (like scans) to vector graphics.  While not optimal for photos, it does good job on line art and poster-like images. Doesn't do too well with color.&lt;br /&gt;
&lt;br /&gt;
Autotrace has some features not available in the embedded [[Potrace]] dialog in Inkscape. For example, there is a ''centerline'' switch that is especially usefull for tracing roads (in maps) or font characters. &lt;br /&gt;
&lt;br /&gt;
'''Frontline''' is GUI frontend to Autotrace by Masatake Yamato. Frontline can be run as standalone aplication, but it can also be directly accessed from Inkscape and GIMP.&lt;br /&gt;
&lt;br /&gt;
If you want to have tracing option, you have to install both Autotrace and  Frontline before compiling Inkscape.  Inkscape automatically detects these programs, and adds inline raster to vector conversion option to image menu.&lt;br /&gt;
&lt;br /&gt;
Both Autotrace and Frontline can be downloaded from [http://autotrace.sourceforge.net/ Sourceforge]. Binaries are  available for windows and linux.&lt;br /&gt;
&lt;br /&gt;
=== Delineate === &lt;br /&gt;
Front end written in java that does interesting things with color images.&lt;br /&gt;
*[http://delineate.sourceforge.net/ Delineate at Sourceforge]&lt;br /&gt;
&lt;br /&gt;
== Convert to SVG ==&lt;br /&gt;
=== svg2pdf ===&lt;br /&gt;
&lt;br /&gt;
This simple but useful application is kindly provided by Carl Worth ([http://cairographics.org/ cairo]).&lt;br /&gt;
==== How to obtain it: ====&lt;br /&gt;
 git clone git://people.freedesktop.org/~cworth/svg2pdf&lt;br /&gt;
And to update it dive into newly created directory called '''''svg2pdf''''' and type this in console:&lt;br /&gt;
 git pull&lt;br /&gt;
(You must of course have '''git''' package installed)&lt;br /&gt;
&lt;br /&gt;
==== Building ====&lt;br /&gt;
If build-time dependencies are satisfied&lt;br /&gt;
 librsvg and cairo&lt;br /&gt;
you only need to type&lt;br /&gt;
 make&lt;br /&gt;
&lt;br /&gt;
==== Usage ====&lt;br /&gt;
 ./svg2pdf input_file.svg output_file.pdf&lt;br /&gt;
&lt;br /&gt;
Note: from version 0.46 Inkscape can both read and write PDFs directly from the open/save dialogs. These tools however allow this conversion where 0.46 is not available, and also provide a means of validating if bugs encountered are Inkscape related.&lt;br /&gt;
&lt;br /&gt;
=== [[uniconvertor]] ===&lt;br /&gt;
UniConvertor is an universal vector graphics translator. The project uses sK1 engine to convert one format to another. Project has following import filters: CDR, CMX, AI, CGM, WMF, XFIG, SVG, SK, SK1, AFF and export filters: AI, SVG, SK, SK1, CGM, WMF.&lt;br /&gt;
*[http://sourceforge.net/projects/uniconvertor/ Uniconvertor]&lt;br /&gt;
&lt;br /&gt;
=== ill2svg === &lt;br /&gt;
ill2svg (Illustrator to SVG) is a small script that helps turn Adobe &lt;br /&gt;
Illustrator .ai files into SVG. While it doesn't always do a perfect job,&lt;br /&gt;
it can sometimes be of great help.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To use the program, just type:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;ill2svg.pl [-l &amp;quot;string&amp;quot; -h] infile &amp;amp;gt; outfile.svg&lt;br /&gt;
&lt;br /&gt;
options: &lt;br /&gt;
    -l specify the file's line-ending convention: dos, mac,&lt;br /&gt;
    or unix; the default is unix&lt;br /&gt;
&lt;br /&gt;
    -h print this message and exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ai2svg.py === &lt;br /&gt;
ai2svg (Illustrator to SVG, recoded in python) is as well a small script that helps turn Adobe Illustrator .ai files into SVG. The Python recode aims to help developers more familiar to python than perl, as well fixing some lacks from ill2svg.pl - supports dashes, layers, textboxes (ascii-latin), and joined pathes.&lt;br /&gt;
&lt;br /&gt;
To use the program, just type:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python ai2svg.py drawing.ai&lt;br /&gt;
&lt;br /&gt;
the result will appear as neighbour, named drawing.ai.svg&lt;br /&gt;
&lt;br /&gt;
options: &lt;br /&gt;
    --help print this message and exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
available at http://www15.brinkster.com/nitrofurano/python/ (that .zip file, in the tools folder) (404 Page not found on 2007-10-15)&lt;br /&gt;
&lt;br /&gt;
=== fig2vect ===&lt;br /&gt;
fig2vector (fig vector format to other vector formats, including SVG) is a converter from 'Fig format' long time used (at least since 1998) unix vector format ([http://epb.lbl.gov/xfig/ Xfig], figurine, jfig, winfig...), large kind of application output on this format in unix, mostly scientific applications.&lt;br /&gt;
&lt;br /&gt;
* [http://fig2vect.sourceforge.net/ fig2vect]&lt;br /&gt;
&lt;br /&gt;
=== gimp2sodipodi === &lt;br /&gt;
Shandy Brown has assembled a somewhat obscure but useful set of tools to get from GIMP to Sodipodi (and thus to Inkscape).  That is, convert raster to vector.&lt;br /&gt;
*[http://sjbrown.users.geeky.net/gimp2sodi/HOWTO.html gimp2sodipodi instructions]&lt;br /&gt;
&lt;br /&gt;
=== GIMP ===&lt;br /&gt;
GIMP is a bitmap graphics tool in the first place, but:&lt;br /&gt;
* it imports SVG (with help of librsvg) into bitmap or vector path, exports SVG.&lt;br /&gt;
* GIMP's selection can be converted to a vector path, and paths can be exported/imported as SVG.&lt;br /&gt;
&lt;br /&gt;
GIMP can be used to create nice textures to use in Inkscape or shapes that can be vectorized in Inkscape. Importing SVG drawings from Inkscape to GIMP allows you to do several effects by filling and stroking shapes.&lt;br /&gt;
&lt;br /&gt;
There is a limited support for drag'n'drop between GIMP and Inkscape:&lt;br /&gt;
* path, can be drag'n'droped from Gimp path dialog to Inkscape&lt;br /&gt;
* pictures can be drag'n'droped from Gimp document history to Inkscape if it's in a file format that Inkscape understands.&lt;br /&gt;
&lt;br /&gt;
*[http://www.gimp.org gimp.org]&lt;br /&gt;
&lt;br /&gt;
=== ConversionSVG ===&lt;br /&gt;
&lt;br /&gt;
ConversionSVG is a GUI that send command line to Inkscape to export in batch SVG to PNG, PS, PDF or EPS.  It features a Wizard to help novice to use it.  You can choose a size in pixels for your export and give some others parameters.&lt;br /&gt;
&lt;br /&gt;
*http://sourceforge.net/projects/conversionsvg/&lt;br /&gt;
&lt;br /&gt;
=== Kig ===&lt;br /&gt;
Kig is a geometry educational tool, it can import from most of the free geom softwares format : kig (own format), kigz (own format compresed), kgeo, seg (Kseg), fgeo (Dr Geo) anf fig, and can export them to SVG, xfig, latex, and image. This can be useful to integrate a geometry activity in a more complet document realized in Inkscape for web reading or printing for schools.&lt;br /&gt;
&lt;br /&gt;
*http://edu.kde.org/kig&lt;br /&gt;
&lt;br /&gt;
== Color scheme &amp;amp; palettes ==&lt;br /&gt;
=== Agave ===&lt;br /&gt;
Agave is a color scheme tool allowing to create palettes based on standard color composition rules, it export it's palettes in the format managed by Gimp and Inkscape.&lt;br /&gt;
&lt;br /&gt;
*[http://home.gna.org/colorscheme/ Agave]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Video editing, for building animation ==&lt;br /&gt;
=== Linux Video Editing System === &lt;br /&gt;
LIVES is the Linux Video Editing System that permits editing and&lt;br /&gt;
making video without worries about formats, frame sizes, or frame&lt;br /&gt;
rates.  It doesn't support SVG directly yet, but you can create animated&lt;br /&gt;
works by moving your shapes in Inkscape and exporting as numbered&lt;br /&gt;
png's.&lt;br /&gt;
&lt;br /&gt;
*[http://www.xs4all.nl/%7Esalsaman/lives/ LIVES]&lt;br /&gt;
&lt;br /&gt;
=== Kdenlive ===&lt;br /&gt;
Kdenlive is a non-linear video editor for Linux, that allow editing, mixing several video and audio using several timelines with previews, add audio/video effects using effects stacks, add text for title or subtitle. It support SVG (but not svgz, at least in svn 20070410).&lt;br /&gt;
&lt;br /&gt;
*[http://kdenlive.org/ Kdenlive]&lt;br /&gt;
&lt;br /&gt;
=== Open Movie Editor ===&lt;br /&gt;
Open Movie Editor is a non linear video editor, allowing editing, mixing several audio and vido, don't know if it supports svg, but as LIVES it can be used for work with png frames to build animation&lt;br /&gt;
&lt;br /&gt;
*[http://openmovieeditor.sourceforge.net Open Movie Editor]&lt;br /&gt;
&lt;br /&gt;
== Multipage presentation or book ==&lt;br /&gt;
&lt;br /&gt;
=== [[InkSlide]] ===&lt;br /&gt;
&lt;br /&gt;
[[InkSlide]] is a presentation creation tool (a small python program) that uses an Inkscape SVG file as a template for rapidly producing slides with text, bulleted lists, images and slide specific SVG content.&lt;br /&gt;
&lt;br /&gt;
* [[InkSlide]]&lt;br /&gt;
&lt;br /&gt;
=== slides ===&lt;br /&gt;
&lt;br /&gt;
Slides is a command-line unix utility to create a multi-page PDF file from a sequence of SVG files.  This is very helpful for creating slide presentations.  For example, it is excellent for making lectures.  You can make incremental slides very easily (i.e., slides which grow with each step, such as listings).  This is done by marking incremental slides in your inkscape svg document.  A tutorial is available on the web page. &lt;br /&gt;
&lt;br /&gt;
*[http://volition.leeds.ac.uk/slides/ Slides]&lt;br /&gt;
&lt;br /&gt;
=== Scribus ===&lt;br /&gt;
&lt;br /&gt;
Scribus is a publishing software supporting SVG, allowing to organise Inkscape documents as a full featured pre-press tool. It doesn't support SVG filters (blur/blend modes...), but manage most of other features.&lt;br /&gt;
&lt;br /&gt;
*[http://www.scribus.net/ Scribus]&lt;br /&gt;
&lt;br /&gt;
[[Category:About Inkscape]]&lt;br /&gt;
&lt;br /&gt;
== Charts &amp;amp; Graphs ==&lt;br /&gt;
=== Gnuplot ===&lt;br /&gt;
Gnuplot, the command line (with several frontends) driven interactive data and function plotting tool has SVG output for graphs &amp;amp; charts.&lt;br /&gt;
* Input can be functions or datasets&lt;br /&gt;
* Output in 2D, 3D...&lt;br /&gt;
* http://www.gnuplot.info/&lt;br /&gt;
&lt;br /&gt;
=== matplotlib ===&lt;br /&gt;
Matplotlib is a python plotting library and can also output SVG.&lt;br /&gt;
* http://matplotlib.sourceforge.net/&lt;br /&gt;
&lt;br /&gt;
=== Gnumeric ===&lt;br /&gt;
Gnumeric, the Gnome spreadsheet output a variety of graphs/charts in SVG format.&lt;br /&gt;
* http://www.gnome.org/projects/gnumeric/&lt;br /&gt;
&lt;br /&gt;
=== SVG Charter ===&lt;br /&gt;
SVG charter is a perl script dedicated to generate SVG charts on web servers. &lt;br /&gt;
* http://charter.sourceforge.net/&lt;br /&gt;
&lt;br /&gt;
== Diagrams ==&lt;br /&gt;
=== Graphviz ===&lt;br /&gt;
Graphviz is a tool for automatic graph drawing, it produces structural information as diagrams of abstract graphs and networks in SVG and other vector formats. Several diagrams softwares use this tool or dot file managed by this tool in variety application domains including:&lt;br /&gt;
* UML&lt;br /&gt;
* databases&lt;br /&gt;
* networks&lt;br /&gt;
* source code analysers objects/functions relations&lt;br /&gt;
* code profiling analalysis&lt;br /&gt;
* web server log analyser&lt;br /&gt;
* packages dependencies&lt;br /&gt;
* ...&lt;br /&gt;
&lt;br /&gt;
*[http://www.graphviz.org/ graphviz]&lt;br /&gt;
*[http://freshmeat.net/search/?q=graphviz Some free software using graphviz to output diagrams]&lt;/div&gt;</summary>
		<author><name>Terry brown</name></author>
	</entry>
</feed>