diff --git a/xkcd-script/generator/pt2_character_classification.py b/xkcd-script/generator/pt2_character_classification.py index 4ec478b..5c908c6 100644 --- a/xkcd-script/generator/pt2_character_classification.py +++ b/xkcd-script/generator/pt2_character_classification.py @@ -2,6 +2,8 @@ import glob import os +import base64 +import sys import matplotlib matplotlib.use('agg') @@ -111,7 +113,10 @@ def merge(img1, img1_bbox, img2, img2_bbox): for char_no, (char, bbox, img) in enumerate(line): char_repr = '-'.join(replacements.get(c, c) for c in char) hex_repr = '-'.join(str(hex(ord(c))) for c in char) - b64_repr = char.encode('base64') + try: + b64_repr = char.encode('base64') + except LookupError: + b64_repr = base64.b64encode(char.encode('utf-8')).decode('utf-8') fname = ('char_L{}_P{}_x{}_y{}_x{}_y{}_{b64_repr}.ppm' ''.format(line_no, char_no, *bbox, b64_repr=b64_repr)) diff --git a/xkcd-script/generator/pt3_ppm_to_svg.py b/xkcd-script/generator/pt3_ppm_to_svg.py index 62779f5..9bcfb86 100644 --- a/xkcd-script/generator/pt3_ppm_to_svg.py +++ b/xkcd-script/generator/pt3_ppm_to_svg.py @@ -2,7 +2,6 @@ from __future__ import division import subprocess -import fontforge def potrace(input_fname, output_fname): subprocess.check_call(['potrace', '-s', input_fname, '-o', output_fname]) diff --git a/xkcd-script/generator/pt4_svg_to_font.py b/xkcd-script/generator/pt4_svg_to_font.py index 456d909..7ac8a62 100644 --- a/xkcd-script/generator/pt4_svg_to_font.py +++ b/xkcd-script/generator/pt4_svg_to_font.py @@ -4,6 +4,22 @@ import os import glob import parse +import base64 + +def array_ucs(ustr): + work = [] + for uch in ustr: + n = ord(uch) + if n >= 0xDC00 and n <= 0xDFFF: + p = ord(work[-1]) + if p >= 0xD800 and p <= 0xDBFF: + work[-1] = (((p & 0x03FF) << 10) | (n & 0x03FF)) + 0x10000 + else: + raise Exception("surrogate nonpair") + else: + work.append(uch) + return work + fnames = sorted(glob.glob('../generated/characters/char_*.svg')) @@ -13,7 +29,10 @@ pattern = 'char_L{line:d}_P{position:d}_x{x0:d}_y{y0:d}_x{x1:d}_y{y1:d}_{b64_str}.svg' result = parse.parse(pattern, os.path.basename(fname)) - chars = tuple(result['b64_str'].decode('base64').decode('utf-8')) + try: + chars = tuple(array_ucs(result['b64_str'].decode('base64').decode('utf-8'))) + except AttributeError: + chars = tuple(array_ucs(base64.b64decode(result['b64_str'].encode()).decode('utf-8'))) bbox = (result['x0'], result['y0'], result['x1'], result['y1']) characters.append([result['line'], result['position'], bbox, fname, chars]) @@ -37,9 +56,9 @@ def basic_font(): font.descent = 256; # We create a ligature lookup table. - font.addLookup('ligatures', 'gsub_ligature', (), [[b'liga', - [[b'latn', - [b'dflt']]]]]) + font.addLookup('ligatures', 'gsub_ligature', (), [['liga', + [['latn', + ['dflt']]]]]) font.addLookupSubtable('ligatures', 'liga') return font @@ -50,6 +69,7 @@ def basic_font(): import tempfile import shutil import os +import sys @contextmanager @@ -62,7 +82,13 @@ def tmp_symlink(fname): target = tempfile.mktemp(suffix=os.path.splitext(fname)[1]) fname = os.path.normpath(os.path.abspath(fname)) try: - os.symlink(fname, target) + if os.name == 'nt': + if sys.version_info.major == 2: + shutil.copy(fname, target) + else: + os.link(fname, target) + else: + os.symlink(fname, target) yield target finally: if os.path.exists(target): @@ -116,10 +142,12 @@ def create_char(font, chars, fname): this_line.setdefault('cap-height', []).append(bbox[1]) -import numpy as np +def mean(a): + return sum(a) / len(a) + import psMat -def scale_glyph(char, char_bbox, baseline, cap_height): +def scale_glyph(c, char_bbox, baseline, cap_height): # TODO: The code in this function is convoluted - it can be hugely simplified. # Essentially, all this function does is figure out how much # space a normal glyph takes, then looks at how much space *this* glyph takes. @@ -127,8 +155,6 @@ def scale_glyph(char, char_bbox, baseline, cap_height): # takes, and scale it to the full EM. On second thoughts, this function really does # need to be convoluted, so maybe the code isn't *that* bad... - font = char.font - # Get hold of the bounding box information for the imported glyph. import_bbox = c.boundingBox() import_width, import_height = import_bbox[2] - import_bbox[0], import_bbox[3] - import_bbox[1] @@ -136,8 +162,8 @@ def scale_glyph(char, char_bbox, baseline, cap_height): # Note that timportOutlines doesn't guarantee glyphs will be put in any particular location, # so translate to the bottom and middle. - target_baseline = char.font.descent - top = char.font.ascent + target_baseline = c.font.descent + top = c.font.ascent top_ratio = top / (top + target_baseline) y_base_delta_baseline = char_bbox[3] - baseline @@ -151,15 +177,15 @@ def scale_glyph(char, char_bbox, baseline, cap_height): # A nice glyph size, in pixels. NOTE: In pixel space, cap_height is smaller than baseline, so make it positive. full_glyph_size = -(cap_height - baseline) / top_ratio - to_canvas_coord_from_px = full_glyph_size / font.em + to_canvas_coord_from_px = full_glyph_size / c.font.em anchor_ratio = (top + target_baseline) / height # pixel scale factor - px_sf = (top + target_baseline) / font.em + px_sf = (top + target_baseline) / c.font.em frac_of_full_size = (height / full_glyph_size) - import_frac_1000 = font.em / import_height + import_frac_1000 = c.font.em / import_height t = psMat.scale(frac_of_full_size * import_frac_1000) c.transform(t) @@ -214,7 +240,7 @@ def autokern(font): all_chars = caps + lower # Add a kerning lookup table. - font.addLookup('kerning', 'gpos_pair', (), [[b'kern', [[b'latn', [b'dflt']]]]]) + font.addLookup('kerning', 'gpos_pair', (), [['kern', [['latn', ['dflt']]]]]) font.addLookupSubtable('kerning', 'kern') # Everyone knows that two slashes together need kerning... (even if they didn't realise it) @@ -243,7 +269,7 @@ def autokern(font): # Special case - add a vertial pipe by re-using an I, and stretching it a bit. for line, position, bbox, fname, chars in characters: - if chars == (u'I',) and line == 4: + if chars == ('I',) and line == 4: characters.append([4, None, bbox, fname, ('|',)]) for line, position, bbox, fname, chars in characters: @@ -260,13 +286,13 @@ def autokern(font): scale_glyph( c, bbox, - baseline=np.mean(line_features['baseline']), - cap_height=np.mean(line_features['cap-height'])) + baseline=mean(line_features['baseline']), + cap_height=mean(line_features['cap-height'])) translate_glyph( c, bbox, - baseline=np.mean(line_features['baseline']), - cap_height=np.mean(line_features['cap-height'])) + baseline=mean(line_features['baseline']), + cap_height=mean(line_features['cap-height'])) # Simplify, then put the vertices on rounded coordinate positions. c.simplify() @@ -284,3 +310,4 @@ def autokern(font): os.remove(font_fname) font.generate(font_fname) +font.close() diff --git a/xkcd-script/generator/pt5_gen_reprod_font.py b/xkcd-script/generator/pt5_gen_reprod_font.py index 7ffaeee..e5f7889 100644 --- a/xkcd-script/generator/pt5_gen_reprod_font.py +++ b/xkcd-script/generator/pt5_gen_reprod_font.py @@ -5,7 +5,7 @@ import datetime import os import shutil -import time +import calendar import fontforge @@ -16,6 +16,10 @@ ttf = os.path.join(base, 'xkcd-script.ttf') woff = os.path.join(base, 'xkcd-script.woff') +then_utc = datetime.datetime(2000, 1, 1, 0, 0) +then_str = 'Sat Jan 1 00:00:00 2000' +then_unix = calendar.timegm(then_utc.timetuple()) + if True: content = [] with open(sfd, 'rb') as fh_in: @@ -27,10 +31,19 @@ continue if line.startswith('%%CreationDate'): - line = '%%CreationDate: Sat Jan 1 00:00:00 2000\n' + line = '%%CreationDate: ' + then_str + '\n' + + if line.startswith('CreationTime:'): + line = 'CreationTime: ' + str(then_unix) + '\n' + + if line.startswith('ModificationTime:'): + line = 'ModificationTime: ' + str(then_unix) + '\n' if 'Created with FontForge (http://fontforge.org)' in line: - line = '% Created with FontForge (http://fontforge.org)\n' + if line.startswith('UComments:'): + line = 'UComments: "Created with FontForge (http://fontforge.org)"\n' + else: + line = '% Created with FontForge (http://fontforge.org)\n' if 'Generated by FontForge' in line: continue @@ -44,9 +57,7 @@ os.remove(sfd) shutil.move(newsfd, sfd) -then = datetime.datetime(2000, 1, 1, 0, 0) -then = time.mktime(then.timetuple()) -os.utime(sfd, (then, then)) +os.utime(sfd, (then_unix, then_unix)) font = fontforge.open(sfd)