Skip to content

Instantly share code, notes, and snippets.

@ospalh
Last active August 29, 2015 14:05
Show Gist options
  • Save ospalh/954f8e9439eb87b5f093 to your computer and use it in GitHub Desktop.
Save ospalh/954f8e9439eb87b5f093 to your computer and use it in GitHub Desktop.
Extract stroke data and number positions out of an SVG file and put them into a KanjiVG file
#! /usr/bin/env python3
# -*- coding: utf-8 ; mode: python -*-
# © Copyright 2014 Roland Sieker <ospalh@gmail.com>
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
"""Copy data from general SVG file to KanjiVG file
This is a helper script to copy data back from an SVG file edited with
a graphical editor such as inkscape to an KanjiVG SVG file.
Example use with inkscape:
* Open a KanjiVG file like `091ce.svg` in inkscape
* Before moving nubers or whole strokes, ungroup the elements you want
to work with
* For the strokes, repeat until there is no "ungroup" menu item shown
* Edit. Move the numbers, strokes, points of a stroke…
* Save *As* with a *different* name. Say `091ce_i.svg`, or use a
differnt directory.
* Run this script: copy-strokes-and-positions.py 091ce_i.svg 091ce.svg
* Check that the result looks as it should!
* Do `git diff` to check that the structure of the file hasn’t been
changed, only the actual data bits.
N.B.: This is rather brittle. If anything isn’t as it should be, the
program just stops. It also ignores translation of groups. Ungroup
everything in inkscape before moving strokes.
"""
from collections import namedtuple
import argparse
import re
import xml.etree.ElementTree as ET
__version__ = '0.1.0'
svg_ns = "http://www.w3.org/2000/svg"
kvg_ns = "http://kanjivg.tagaini.net"
xlink_ns = "http://www.w3.org/1999/xlink"
ET.register_namespace('svg', svg_ns)
ET.register_namespace('kvg', kvg_ns)
ET.register_namespace('xlink', xlink_ns)
Point = namedtuple('Point', ['x', 'y'])
def copy_numbers(svg_tree, kvg_lines):
"""Copy stroke number positios from a svg into a kanjivg file"""
# We do hardly any checking. If something is wrong, just blow up.
number_dict = {}
for number in svg_tree.getiterator('{{{ns}}}text'.format(ns=svg_ns)):
number_dict[number.text] = Point(
float(number.get('x')), float(number.get('y')))
for lnum, line in enumerate(kvg_lines):
num_match = re.search('>(.*?)</text>', line)
try:
new_point = number_dict[num_match.group(1)]
except (AttributeError, KeyError):
# AttributeError: num_match is None, no text on this line
# KeyError: we have a text, but we didn’t see that in the svg file
# In both cases just go on with the next line
continue
kvg_lines[lnum] = re.sub(
'matrix\(1 0 0 1 .*? .*?\)',
'matrix(1 0 0 1 {x:.2f} {y:.2f})'.format(
x=new_point.x, y=new_point.y), line)
# This may fail. But then we want it to blow up: We have an id
# match, but nowhere to put the new data.
# Also round to two decimal places to keep this somewhat human
# readable and in line with the old way.
return kvg_lines
def copy_stroke_data(svg_tree, kvg_lines):
"""copy the stroke data in a kanjivg file"""
# We do hardly any checking. If something is wrong, just blow up.
d_dict = {}
for path in svg_tree.getiterator('{{{ns}}}path'.format(ns=svg_ns)):
d_dict[path.get('id')] = re.sub('^m', 'M', path.get('d'))
# Make sure we use “M”. For some reason inkscape likes to
# start its paths with “m”
for lnum, line in enumerate(kvg_lines):
id_match = re.search('id="(.*?)"', line)
try:
new_data = d_dict[id_match.group(1)]
except (AttributeError, KeyError):
# AttributeError: id_match is None, no id on this line
# KeyError: we have an id, but we didn’t see that in the svg file
# In both cases just go on with the next line
continue
kvg_lines[lnum] = re.sub(' d=".*?"', ' d="{0}"'.format(new_data), line)
# This may fail. But then we want it to blow up: We have an id
# match, but nowhere to put the new data.
return kvg_lines
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description=u"""Copy stroke and number data from one file to another.
Take the vector data of the strokes and the positions from one
file and put them into the second. A typical use case is
copying only the relevant data from a file edited with a
graphical SVG editor like inkscape back into the original
KanjiVG file. N.B. the program is rather simple-minded, and
ignores the translations of groups. Ungroup everything before
you move strokes around in inkscape. """)
group = parser.add_mutually_exclusive_group()
group.add_argument('-n', '--numbers', action='store_const', dest='mode',
const='n',
help=u"""Copy only the number positions.""")
group.add_argument('-d', '--data', action='store_const', dest='mode',
const='d',
help=u"""Copy only the stroke data.""")
group.add_argument('-b', '--both', action='store_const', dest='mode',
const='b',
help=u"""\
Copy both data and number positions. (default)""")
parser.add_argument('svg_file', type=str, help='edited SVG file')
parser.add_argument('kvg_file', type=str, help='kanjiVG file')
parser.set_defaults(mode='b')
args = parser.parse_args()
s_tree = ET.parse(args.svg_file).getroot()
with open(args.kvg_file) as kf:
k_lines = kf.readlines()
if args.mode == 'b' or args.mode == 'd':
k_lines = copy_stroke_data(s_tree, k_lines)
if args.mode == 'b' or args.mode == 'n':
k_lines = copy_numbers(s_tree, k_lines)
with open(args.kvg_file, 'w') as kf:
for l in k_lines:
kf.write(l)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment