Created
January 30, 2021 04:00
-
-
Save celestialphineas/4d20fae0d54c2ebbbb7f98a127cee7b9 to your computer and use it in GitHub Desktop.
Generate otfcc font dump out of a folder of images
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"head": { | |
"version": 1.0, | |
"fontRevision": 1.0, | |
"flags": { | |
"baselineAtY_0": true, | |
"alwaysUseIntegerSize": true | |
}, | |
"unitsPerEm": 1000, | |
"macStyle": {}, | |
"lowestRecPPEM": 8, | |
"indexToLocFormat": 0, | |
"glyphDataFormat": 0 | |
}, | |
"hhea": { | |
"version": 1.0, | |
"ascender": 850, | |
"descender": -150, | |
"lineGap": 0, | |
"caretSlopeRise": 1, | |
"caretSlopeRun": 0, | |
"caretOffset": 0 | |
}, | |
"vhea": { | |
"version": 1.0, | |
"ascent": 500, | |
"descent": -500, | |
"lineGap": 0, | |
"caretSlopeRise": 0, | |
"caretSlopeRun": 1, | |
"caretOffset": 0 | |
}, | |
"post": { | |
"version": 3.0, | |
"italicAngle": 0, | |
"underlinePosition": -100, | |
"underlineThickness": 50, | |
"isFixedPitch": false | |
}, | |
"OS_2": { | |
"version": 3, | |
"usWeightClass": 400, | |
"usWidthClass": 5, | |
"fsType": {}, | |
"ySubscriptXSize": 650, | |
"ySubscriptYSize": 600, | |
"ySubscriptXOffset": 0, | |
"ySubscriptYOffset": 75, | |
"ySupscriptXSize": 650, | |
"ySupscriptYSize": 600, | |
"ySupscriptXOffset": 0, | |
"ySupscriptYOffset": 350, | |
"yStrikeoutSize": 45, | |
"yStrikeoutPosition": 250, | |
"sFamilyClass": 0, | |
"panose": [ | |
2, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0, | |
0 | |
], | |
"ulUnicodeRange2": { | |
"CJK_Compatibility": true, | |
"CJK_Unified_Ideographs": true | |
}, | |
"achVendID": "TEST", | |
"fsSelection": { | |
"regular": true | |
}, | |
"usFirstCharIndex": 32, | |
"usLastCharIndex": 65509, | |
"sTypoAscender": 850, | |
"sTypoDescender": -150, | |
"sTypoLineGap": 1000, | |
"usWinAscent": 861, | |
"usWinDescent": 220, | |
"ulCodePageRange1": { | |
"latin1": true, | |
"latin2": true, | |
"gbk": true | |
}, | |
"ulCodePageRange2": {}, | |
"sxHeight": 480, | |
"sCapHeight": 700, | |
"usDefaultChar": 0, | |
"usBreakChar": 32, | |
"usMaxContext": 1, | |
"usLowerOpticalPointSize": 0, | |
"usUpperOpticalPointSize": 0 | |
}, | |
"name": [ | |
{ | |
"platformID": 1, | |
"encodingID": 0, | |
"languageID": 0, | |
"nameID": 0, | |
"nameString": "${COPYRIGHT}" | |
}, | |
{ | |
"platformID": 1, | |
"encodingID": 0, | |
"languageID": 0, | |
"nameID": 1, | |
"nameString": "${FONTNAME}" | |
}, | |
{ | |
"platformID": 1, | |
"encodingID": 0, | |
"languageID": 0, | |
"nameID": 2, | |
"nameString": "Regular" | |
}, | |
{ | |
"platformID": 1, | |
"encodingID": 0, | |
"languageID": 0, | |
"nameID": 3, | |
"nameString": "${FONTIDENTIFIER}" | |
}, | |
{ | |
"platformID": 1, | |
"encodingID": 0, | |
"languageID": 0, | |
"nameID": 4, | |
"nameString": "${FONTNAME}" | |
}, | |
{ | |
"platformID": 1, | |
"encodingID": 0, | |
"languageID": 0, | |
"nameID": 5, | |
"nameString": "Version 1.00" | |
}, | |
{ | |
"platformID": 1, | |
"encodingID": 0, | |
"languageID": 0, | |
"nameID": 6, | |
"nameString": "${FONTIDENTIFIER}" | |
}, | |
{ | |
"platformID": 3, | |
"encodingID": 1, | |
"languageID": 1033, | |
"nameID": 0, | |
"nameString": "${COPYRIGHT}" | |
}, | |
{ | |
"platformID": 3, | |
"encodingID": 1, | |
"languageID": 1033, | |
"nameID": 1, | |
"nameString": "${FONTNAME}" | |
}, | |
{ | |
"platformID": 3, | |
"encodingID": 1, | |
"languageID": 1033, | |
"nameID": 2, | |
"nameString": "Regular" | |
}, | |
{ | |
"platformID": 3, | |
"encodingID": 1, | |
"languageID": 1033, | |
"nameID": 3, | |
"nameString": "${FONTIDENTIFIER}" | |
}, | |
{ | |
"platformID": 3, | |
"encodingID": 1, | |
"languageID": 1033, | |
"nameID": 4, | |
"nameString": "${FONTNAME}" | |
}, | |
{ | |
"platformID": 3, | |
"encodingID": 1, | |
"languageID": 1033, | |
"nameID": 5, | |
"nameString": "Version 1.00" | |
}, | |
{ | |
"platformID": 3, | |
"encodingID": 1, | |
"languageID": 1033, | |
"nameID": 6, | |
"nameString": "${FONTIDENTIFIER}" | |
}, | |
{ | |
"platformID": 3, | |
"encodingID": 1, | |
"languageID": 2052, | |
"nameID": 0, | |
"nameString": "${COPYRIGHT}" | |
}, | |
{ | |
"platformID": 3, | |
"encodingID": 1, | |
"languageID": 2052, | |
"nameID": 1, | |
"nameString": "${FONTNAME_ZH}" | |
}, | |
{ | |
"platformID": 3, | |
"encodingID": 1, | |
"languageID": 2052, | |
"nameID": 2, | |
"nameString": "Regular" | |
}, | |
{ | |
"platformID": 3, | |
"encodingID": 1, | |
"languageID": 2052, | |
"nameID": 4, | |
"nameString": "${FONTNAME_ZH}" | |
} | |
], | |
"cmap": { | |
"U+0020": "space" | |
}, | |
"CFF_": { | |
"isCID": true, | |
"${FONTNAME}": "${FONTIDENTIFIER}", | |
"fullName": "${FONTIDENTIFIER}", | |
"cidRegistry": "Adobe", | |
"cidOrdering": "Identity", | |
"cidSupplement": 0, | |
"fdArray": { | |
"${FONTIDENTIFIER}": { | |
"privates": { | |
"defaultWidthX": 1000.0, | |
"nominalWidthX": 853 | |
} | |
} | |
} | |
}, | |
"glyf": { | |
".notdef": { | |
"advanceWidth": 1000, | |
"advanceHeight": 1000, | |
"verticalOrigin": 850, | |
"CFF_fdSelect": "${FONTIDENTIFIER}", | |
"CFF_CID": 0 | |
}, | |
"space": { | |
"advanceWidth": 300 | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import json | |
import potrace | |
import numpy as np | |
from skimage import io, transform | |
def trace(filename, baseline=150, upm=1000): | |
img = io.imread(filename) | |
if len(img.shape) > 2: img = img[:,:,0] | |
h, w = img.shape | |
resized = transform.resize(img, (int(upm), int(upm/h*w))) < 0.5 | |
bmp = potrace.Bitmap(resized) | |
path = bmp.trace() | |
contours = [] | |
for curve in path: | |
contour = [] | |
for segment in curve: | |
if not segment.is_corner: | |
contour.insert(0, { | |
'x': int(segment.c1[0] + 0.5), | |
'y': int(upm - segment.c1[1] - int(baseline) + 0.5), | |
'on': False | |
}) | |
contour.insert(0, { | |
'x': int(segment.c2[0] + 0.5), | |
'y': int(upm - segment.c2[1] - int(baseline) + 0.5), | |
'on': False | |
}) | |
contour.insert(0, { | |
'x': int(segment.end_point[0] + 0.5), | |
'y': int(upm - segment.end_point[1] - int(baseline) + 0.5), | |
'on': True | |
}) | |
contours.append(contour) | |
return contours | |
def build_font(glyph_dict, copyright='Copyright 2021 Next Lab', | |
fontname='Test font', fontname_zh='测试字体', font_identifier='TESTFONT'): | |
font_base = open('base.json').read() | |
font_base = font_base.replace('${COPYRIGHT}', copyright) | |
font_base = font_base.replace('${FONTNAME}', fontname) | |
font_base = font_base.replace('${FONTNAME_ZH}', fontname_zh) | |
font_base = font_base.replace('${FONTIDENTIFIER}', font_identifier) | |
font_obj = json.loads(font_base) | |
for unicode in glyph_dict: | |
upper = unicode.upper() | |
contours = glyph_dict[unicode] | |
font_obj['cmap']['U+'+ upper] = 'uni' + upper | |
font_obj['glyf']['uni' + upper] = { | |
'advanceWidth': 1000, | |
'advanceHeight': 1000, | |
'verticalOrigin': 850, | |
'CFF_fdSelect': font_identifier, | |
'CFF_CID': 0, | |
'contours': contours | |
} | |
return font_obj | |
import os | |
import multiprocessing as mp | |
files = os.listdir('yan') | |
if __name__ == '__main__': | |
manager = mp.Manager() | |
glyph_dict = manager.dict() | |
def process(filename): | |
char = filename.split('_')[1][0] | |
unicode = '%X' % ord(char) | |
glyph_dict[unicode] = trace('yan/' + filename) | |
with mp.Pool(processes=8) as p: | |
p.map(process, files) | |
out_file = open('export.json', 'w') | |
json.dump( | |
build_font(glyph_dict.copy(), fontname='Next Lab Yan', font_identifier='NextLabYan', fontname_zh='Next Lab 颜体'), | |
out_file | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment