Create PDF slides from layers of an Inkscape drawing
#!/usr/bin/env python3 | |
# Convert an Inkscape SVG file into a set of PDF slides for presentation | |
# or printing. | |
# | |
# Requires inkscape and pdftk to be installed and in the PATH. | |
# | |
# Inspired by mkpdfs.rb (https://gist.github.com/emk/961877) | |
# Steven Bell <botsnlinux@gmail.com> | |
# | |
# By default, each layer in the SVG file produces one slide. | |
# Layers are ordered from bottom to top (i.e., the bottom layer is the first slide). | |
# A layer whose name begins with '+' will be added to the previous layer | |
# A layer whose name begins with '_' will be used as a "base" layer for all | |
# slides that follow. | |
# A layer whose name begins with '.' will always be hidden. This is useful for | |
# guides, templates, or just hiding things that aren't finished. | |
# There are a set of special tags that will get filled in when placed in a text | |
# field as ${tag}. Currently the only tag is ${slide}, which inserts the slide | |
# number. This only works for normal text fields, not text boxes. | |
from lxml import etree | |
import os | |
from sys import argv | |
def delete_temporary_files(): | |
os.system("rm -f " + tempsvg) | |
os.system("rm -f slide-*.pdf") | |
# Configuration | |
if len(argv) < 2: | |
print("Incorrect number of arguments") | |
print("Usage: mkpdfs.py SVGFILE\n") | |
exit() | |
srcfile = argv[1] | |
tmpdir = './' | |
tempsvg = 'temp.svg' | |
coalesce_animations = False # Whether to flatten animations for printouts | |
delete_temporary_files() | |
# Load the file and get the namespaces | |
doc = etree.fromstringlist(open(srcfile)).getroottree() | |
ns = doc.getroot().nsmap | |
layers = doc.findall('/svg:g[@inkscape:groupmode="layer"]', namespaces=ns) | |
# Find all the text strings that we're going to have to replace | |
texts = doc.findall('//tspan', namespaces=ns) | |
subst_elements = [] # Text elements where we have to substitute something | |
subst_strings = [] # The corresponding strings of text | |
for t in texts: | |
if t.text is not None and t.text.find('${slide}') is not -1: | |
subst_elements.append(t) | |
subst_strings.append(t.text) | |
# First pass: | |
# Make all of the layers invisible | |
# and find the last layer which should be visible | |
last_visible_layer = None | |
for l in layers: | |
l.attrib['style'] = 'display:none' | |
label = l.attrib['{http://www.inkscape.org/namespaces/inkscape}label'] | |
if label[0] is not '.' and label[0] is not '_': | |
last_visible_layer = l | |
# Second pass: | |
# Build up the layers and create files | |
slide_num = 0 # Number that we put into slides | |
page_count = 0 # Used to name the PDF files we export | |
base_layers = [] # Layers which are always shown once added | |
tvisible_layers = [] # Layers visible at the current point in time | |
for i,l in enumerate(layers): | |
label = l.attrib['{http://www.inkscape.org/namespaces/inkscape}label'] | |
if label[0] is '.': | |
# Hidden, just skip this layer | |
continue | |
elif label[0] is '_': | |
# Base layer, add it to the list but don't make a slide for it | |
base_layers.append(l) | |
continue | |
elif label[0] is '+': | |
# Additive layer, just append it to the current list | |
visible_layers.append(l); | |
else: | |
# Normal case, reset all the layers and add this one | |
visible_layers = base_layers + [l]; | |
slide_num += 1 | |
if coalesce_animations and l is not last_visible_layer: | |
next_label = layers[i+1].attrib['{http://www.inkscape.org/namespaces/inkscape}label'] | |
if next_label[0] is '+' or next_label[0] is '.': | |
# Then don't render just yet | |
print("skipping!") | |
continue | |
for vl in visible_layers: | |
vl.attrib['style'] = 'display:inline' | |
# Do the string substitutions | |
for s in range(len(subst_elements)): | |
subst_elements[s].text = subst_strings[s].replace('${slide}', str(slide_num)) | |
# Save the updated SVG file | |
doc.write(tmpdir + os.path.sep + tempsvg) | |
# Call Inkscape to render it | |
pdf_name = tmpdir + os.path.sep + "slide-{:03d}.pdf".format(page_count) | |
page_count += 1 | |
print("Exporting {id} as {name}".format(id=l.attrib['id'], name=pdf_name)) | |
os.system("inkscape --export-pdf={path} --export-area-page {temp}".format(path=pdf_name, temp=tempsvg)) | |
# Restore things back to the way they were for the next run | |
for vl in visible_layers: | |
vl.attrib['style'] = 'display:none' | |
# Merge everything using pdftk | |
out_path = srcfile[:-4] + '.pdf' | |
if os.system("pdftk slide-*.pdf cat output {}".format(out_path)): | |
print("Failed to combine pdfs! Check that pdftk is installed") | |
else: | |
print("Output written to {}".format(out_path)) | |
delete_temporary_files() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment