1#!/usr/bin/env python 2from __future__ import print_function, division, absolute_import 3 4import argparse 5import logging 6import os 7import sys 8 9from cu2qu.pens import Cu2QuPen 10from fontTools import configLogger 11from fontTools.misc.cliTools import makeOutputFileName 12from fontTools.pens.ttGlyphPen import TTGlyphPen 13from fontTools.ttLib import TTFont, newTable 14 15 16log = logging.getLogger() 17 18# default approximation error, measured in UPEM 19MAX_ERR = 1.0 20 21# default 'post' table format 22POST_FORMAT = 2.0 23 24# assuming the input contours' direction is correctly set (counter-clockwise), 25# we just flip it to clockwise 26REVERSE_DIRECTION = True 27 28 29def glyphs_to_quadratic( 30 glyphs, max_err=MAX_ERR, reverse_direction=REVERSE_DIRECTION): 31 quadGlyphs = {} 32 for gname in glyphs.keys(): 33 glyph = glyphs[gname] 34 ttPen = TTGlyphPen(glyphs) 35 cu2quPen = Cu2QuPen(ttPen, max_err, 36 reverse_direction=reverse_direction) 37 glyph.draw(cu2quPen) 38 quadGlyphs[gname] = ttPen.glyph() 39 return quadGlyphs 40 41 42def otf_to_ttf(ttFont, post_format=POST_FORMAT, **kwargs): 43 assert ttFont.sfntVersion == "OTTO" 44 assert "CFF " in ttFont 45 46 glyphOrder = ttFont.getGlyphOrder() 47 48 ttFont["loca"] = newTable("loca") 49 ttFont["glyf"] = glyf = newTable("glyf") 50 glyf.glyphOrder = glyphOrder 51 glyf.glyphs = glyphs_to_quadratic(ttFont.getGlyphSet(), **kwargs) 52 del ttFont["CFF "] 53 glyf.compile(ttFont) 54 55 ttFont["maxp"] = maxp = newTable("maxp") 56 maxp.tableVersion = 0x00010000 57 maxp.maxZones = 1 58 maxp.maxTwilightPoints = 0 59 maxp.maxStorage = 0 60 maxp.maxFunctionDefs = 0 61 maxp.maxInstructionDefs = 0 62 maxp.maxStackElements = 0 63 maxp.maxSizeOfInstructions = 0 64 maxp.maxComponentElements = max( 65 len(g.components if hasattr(g, 'components') else []) 66 for g in glyf.glyphs.values()) 67 maxp.compile(ttFont) 68 69 post = ttFont["post"] 70 post.formatType = post_format 71 post.extraNames = [] 72 post.mapping = {} 73 post.glyphOrder = glyphOrder 74 try: 75 post.compile(ttFont) 76 except OverflowError: 77 post.formatType = 3 78 log.warning("Dropping glyph names, they do not fit in 'post' table.") 79 80 ttFont.sfntVersion = "\000\001\000\000" 81 82 83def main(args=None): 84 configLogger(logger=log) 85 86 parser = argparse.ArgumentParser() 87 parser.add_argument("input", nargs='+', metavar="INPUT") 88 parser.add_argument("-o", "--output") 89 parser.add_argument("-e", "--max-error", type=float, default=MAX_ERR) 90 parser.add_argument("--post-format", type=float, default=POST_FORMAT) 91 parser.add_argument( 92 "--keep-direction", dest='reverse_direction', action='store_false') 93 parser.add_argument("--face-index", type=int, default=0) 94 parser.add_argument("--overwrite", action='store_true') 95 options = parser.parse_args(args) 96 97 if options.output and len(options.input) > 1: 98 if not os.path.isdir(options.output): 99 parser.error("-o/--output option must be a directory when " 100 "processing multiple fonts") 101 102 for path in options.input: 103 if options.output and not os.path.isdir(options.output): 104 output = options.output 105 else: 106 output = makeOutputFileName(path, outputDir=options.output, 107 extension='.ttf', 108 overWrite=options.overwrite) 109 110 font = TTFont(path, fontNumber=options.face_index) 111 otf_to_ttf(font, 112 post_format=options.post_format, 113 max_err=options.max_error, 114 reverse_direction=options.reverse_direction) 115 font.save(output) 116 117 118if __name__ == "__main__": 119 sys.exit(main()) 120