Switch to JSON rendering frontend

This commit is contained in:
Shaunak Kishore
2015-08-26 23:56:42 -04:00
parent c85e70034a
commit 3ee25c4d0c
7 changed files with 80 additions and 119 deletions

2
.gitignore vendored
View File

@ -1,4 +1,2 @@
*.pyc *.pyc
*.swp *.swp
chars.html
chars.log

110
main.py
View File

@ -1,110 +0,0 @@
#!/usr/bin/python
'''
Extracts one or more characters from each of the svg fonts in the SVG directory
and packages them into a 'chars.html' output file.
'''
import math
import os
import random
import sys
import median
import stroke_extractor
COLORS = ['#0074D9', '#2ECC40', '#FFDC00', '#FF4136', '#7FDBFF',
'#001F3F', '#39CCCC', '#3D9970', '#01FF70', '#FF851B']
SCALE = 0.16
SVG_DIR = 'derived'
TRANSFORM = 'scale({0:.2g}, -{0:0.2g}) translate(0, -900)'.format(SCALE)
def augment_glyph(glyph):
'''
Takes an HTML SVG object and returns a list of addition SVG elements that
should be added to the glyph to show diagnostic data for our algorithm.
'''
name = get_html_attribute(glyph, 'glyph-name')
d = get_html_attribute(glyph, 'd')
assert name and d, 'Missing glyph-name or d for glyph:\n{0}'.format(glyph)
extractor = stroke_extractor.StrokeExtractor(name, d)
# We augment the glyph with three types of information:
# - The extracted strokes. Each one is drawn in a random color.
# - The endpoints of the original paths, with corners in red, others in blue.
# - The detected bridges, line segments drawn in white.
result = []
for i, stroke in enumerate(extractor.strokes):
result.append('<path fill="{0}" {1} d="{2}" />'.format(
COLORS[i % len(COLORS)], 'stroke="black" stroke-width="2"', stroke.d()))
for path in extractor.paths:
for element in path:
result.append(
'<circle cx="{0}" cy="{1}" r="4" fill="blue" stroke="blue"/>'.format(
int(element.end.real), int(element.end.imag)))
corners = extractor.corners
for corner in corners.itervalues():
result.append(
'<circle cx="{0}" cy="{1}" r="4" fill="red" stroke="red" '
'data-angle="{2}"/>'.format(
int(corner.point.real), int(corner.point.imag), corner.angle))
for index1 in extractor.bridges:
for index2 in extractor.bridges[index1]:
if index1 > index2:
continue
result.append(
'<line x1="{0}" y1="{1}" x2="{2}" y2="{3}" style="{4}"/>'.format(
int(corners[index1].point.real), int(corners[index1].point.imag),
int(corners[index2].point.real), int(corners[index2].point.imag),
'stroke:white;stroke-width:8'))
return result
def get_html_attribute(glyph, attribute):
'''
Takes an HTML SVG object and returns the path data from the "d" field.
'''
left = ' {0}="'.format(attribute)
start = max(glyph.find(left), glyph.find(left.replace(' ', '\n')))
end = glyph.find('"', start + len(left))
assert start >= 0 and end >= 0, \
'Glyph missing {0}=".*" block:\n{1}'.format(attribute, repr(glyph))
return glyph[start + len(left):end].replace('\n', ' ')
if __name__ == '__main__':
assert len(sys.argv) > 1, 'Usage: ./getchar.py <unicode_codepoint>+'
svgs = [file_name for file_name in os.listdir(SVG_DIR)
if file_name.endswith('.svg') and not file_name.startswith('.')]
glyphs = []
for file_name in svgs:
glyphs.append([])
with open(os.path.join(SVG_DIR, file_name)) as file:
data = file.read()
for codepoint in sys.argv[1:]:
index = data.find('unicode="&#x{0};"'.format(codepoint))
if index < 0:
print >> sys.stderr, '{0}: missing {1}'.format(file_name, codepoint)
continue
(left, right) = ('<glyph', '/>')
(start, end) = (data.rfind(left, 0, index), data.find(right, index))
if start < 0 or end < 0:
print >> sys.stderr, '{0}: malformed {1}'.format(file_name, codepoint)
continue
glyphs[-1].append(data[start:end + len(right)])
# Construct an HTML file that includes the extracted glyphs, along with
# diagnostic data for our stroke extraction algorithm.
with open('chars.html', 'w') as f:
f.write('<!DOCTYPE html>\n <html>\n <body>\n')
for row in glyphs:
f.write(' <div>\n')
for glyph in row:
size = int(1024*SCALE)
f.write(' <svg width="{0}" height="{0}">\n'.format(size))
f.write(' <g transform="{0}">\n'.format(TRANSFORM))
f.write(glyph.replace('<glyph', '<path') + '\n')
for extra in augment_glyph(glyph):
f.write(extra + '\n')
f.write(' </g>\n')
f.write(' </svg>\n')
f.write(' </div>\n')
f.write(' </body>\n </html>')

59
scripts/main.py Executable file
View File

@ -0,0 +1,59 @@
#!/usr/bin/python
'''
Extracts one or more characters from each of the svg fonts in the SVG directory
and prints data for them to stderr in JSON format. The output data is a list of
dictionaries with the following keys:
- name: string glyph name
- d: string SVG path data
- extractor: stroke data + diagnostics (see stroke_extractor for details)
'''
import argparse
import json
import sys
import stroke_extractor
def get_html_attribute(glyph, attribute):
'''
Takes an HTML SVG object and returns the path data from the "d" field.
'''
left = ' {0}="'.format(attribute)
start = max(glyph.find(left), glyph.find(left.replace(' ', '\n')))
end = glyph.find('"', start + len(left))
assert start >= 0 and end >= 0, \
'Glyph missing {0}=".*" block:\n{1}'.format(attribute, repr(glyph))
return glyph[start + len(left):end].replace('\n', ' ')
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('-f', '--font', dest='font',
help='SVG font to read characters from.', required=True)
(options, args) = parser.parse_known_args()
# For each Unicode codepoint among the positional arguments, extract the glyph
# that corresponds to that codepoint from the given SVG font.
glyphs = []
with open(options.font) as font:
data = font.read()
for codepoint in args:
index = data.find('unicode="&#x{0};"'.format(codepoint))
if index < 0:
print >> sys.stderr, '{0}: missing {1}'.format(options.font, codepoint)
continue
(left, right) = ('<glyph', '/>')
(start, end) = (data.rfind(left, 0, index), data.find(right, index))
if start < 0 or end < 0:
print >> sys.stderr, '{0}: malformed {1}'.format(options.font, codepoint)
continue
glyphs.append(data[start:end + len(right)])
# Print data for each of the extracted glyphs in JSON format.
result = []
for glyph in glyphs:
name = get_html_attribute(glyph, 'glyph-name')
d = get_html_attribute(glyph, 'd')
assert name and d, 'Missing glyph-name or d for glyph:\n{0}'.format(glyph)
extractor = stroke_extractor.StrokeExtractor(name, d)
data = {'name': name, 'd': d, 'extractor': extractor.get_data()}
result.append(data)
print json.dumps(result)

0
median.py → scripts/median.py Executable file → Normal file
View File

21
stroke_extractor.py → scripts/stroke_extractor.py Executable file → Normal file
View File

@ -346,6 +346,27 @@ class StrokeExtractor(object):
result[corner.index] = corner result[corner.index] = corner
return result return result
def get_data(self):
'''
Returns a representation of the data extracted from this glyph that can be
serialized to JSON. The result is a dictionary with the following keys:
- points: list of [x, y] pairs of endpoints on the glyph's SVG path
- corners: list of [x, y] pairs of points that are also corners
- bridges: list of pairs of corners [[x1, y1], [x2, y2]] that are bridges
- strokes: list of SVG path data strings for the extracted strokes
'''
pair = lambda point: [int(point.real), int(point.imag)]
return {
'points': [pair(element.end) for path in self.paths for element in path],
'corners': [pair(corner.point) for corner in self.corners.itervalues()],
'bridges': [
[pair(self.corners[index1].point), pair(self.corners[index2].point)]
for (index1, others) in self.bridges.iteritems() for index2 in others
if index1 < index2
],
'strokes': [stroke.d() for stroke in self.strokes],
}
def log(self, message): def log(self, message):
self.messages.append(message) self.messages.append(message)

7
test
View File

@ -1,7 +0,0 @@
#!/bin/bash
#./main.py 4f60 597d 5b78 5b66 4e08 4e18 4e28 4e38 4e48 4e58 4e68 4e78 4e80 4e81 4e82 4e83 4e70 4e71 4e72 4e73 4e74 4e75 4e76 4e77 4e07 4e17 4e27 4e37 4e47 4e57 4e67 4e77 > chars.log
#./main.py 4e00 4e10 4e20 4e30 4e40 4e50 4e60 4e70 4e80 4e90 4ea0 4eb0 4ec0 4ed0 4ee0 4ef0 \
# 4e01 4e11 4e21 4e31 4e41 4e51 4e61 4e71 4e81 4e91 4ea1 4eb1 4ec1 4ed1 4ee1 4ef1 \
# 4e02 4e12 4e22 4e32 4e42 4e52 4e62 4e72 4e82 4e92 4ea2 4eb2 4ec2 4ed2 4ee2 4ef2 \
# 4e03 4e13 4e23 4e33 4e43 4e53 4e63 4e73 4e83 4e93 4ea3 4eb3 4ec3 4ed3 4ee3 4ef3
./main.py 51b0 72d0 6211 5fc3 4ea1 56db 534d 7fbd 4e5f 53c8 4eca 98de 4e5d 5f13 9a6c 8ba1 9ce9 53ca 961d 90ae 6295 51f8 5927 5927 4e03 4e2d 4e4f 6708 4e38 56db 758b 5b50 5c71 4e1c