かきかたえんぴつ

いつかどこかで何かの役にたつかもしれないメモ

続・Inkscape SVG の各レイヤを PDF にエクスポートする

プレゼンのスライド作りに Inkscape を使いたいので、前回のスクリプトを拡張した。

以下のスクリプトは、Inkscape で作成した SVG の各レイヤを各ページにもつ PDF を出力する。'background' という名前のレイヤはつねに表示される。id に 'pages' を含むテキストオブジェクトはページ指示器とみなされ、「ページ番号 / 全ページ数」で置き換えられる。

JessyInk で同じことができるのだけれど、自分でコードを書いたほうがいろいろ自由にできるので。

inkscape および ghostscript

  • svg2pdf.py
import os, sys
import re
import tempfile
from xml.etree.ElementTree import *

svg_ns      = 'http://www.w3.org/2000/svg'
inkscape_ns = 'http://www.inkscape.org/namespaces/inkscape'
sodipodi_ns = 'http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd'

gs_exe = 'gswin32c'
is_exe = 'inkscape'
bg_keyword = 'background'
pi_keyword = 'pages'      

argv = sys.argv
argc = len(argv)
if(argc < 2):
    print 'Usage: svg2pdf.py hoge.svg'
    quit()

path = argv[1]
base, ext = os.path.splitext(path)

tree = parse(path)
pdfs = ''
fg_list = [] # foreground layers
bg_list = [] # background layers
pi_list = [] # page-indicators

# finding page indicator
for text in tree.findall('.//{%s}text' % svg_ns):
    if text.get('id').find(pi_keyword) >= 0:
        pi_list.append(text)

# setting all foreground layers hidden and all background visible
for layer in tree.findall('.//{%s}g' % svg_ns):
    if re.search(r'^layer[0-9]+$', layer.get('id')) == None:
        continue
    
    if layer.get('{%s}label' % inkscape_ns).find(bg_keyword) >= 0:
        layer.set('style', 'display:inline')
        bg_list.append(layer)
    else:
        layer.set('style', 'display:none')
        fg_list.append(layer)

# create a temporary pdf file for each foreground layer
for i, layer in enumerate(fg_list):
    layer.set('style', 'display:inline')

    for pi in pi_list:
        pi[0].text = '%d / %d' % (i+1, len(fg_list))
    
    temp_svg = tempfile.mkstemp(suffix='.svg')
    tree.write(temp_svg[1], 'UTF-8')
    
    temp_pdf = tempfile.mkstemp(suffix='.pdf')
    os.close(temp_pdf[0])
    cmd = '%s -C -f \"%s\" -A %s' \
          % (is_exe, temp_svg[1], temp_pdf[1])
    print cmd
    os.system(cmd)
    pdfs += '%s ' % temp_pdf[1]

    os.close(temp_svg[0])
    os.remove(temp_svg[1])
    
    layer.set('style', 'display:none')

# bind all the pdf files into one
cmd = '%s -dNOPAUSE -sDEVICE=pdfwrite ' % gs_exe \
      + '-dAutoFilterColorImages=false ' \
      + '-dColorImageFilter=/FlateEncode ' \
      + '-sOUTPUTFILE=%s.pdf -dBATCH %s' \
      % (base, pdfs)
print cmd
os.system(cmd)

# delete all the temporary pdf files
for temp_pdf_path in pdfs.split():
    os.remove(temp_pdf_path)
  • 2013.02.12 レイヤ検索のさいに正規表現を使うように変更。