parser.add_argument('--infile', type=str, default='') parser.add_argument('--outfile', type=str, default='') # find out which operation we're asked to perform args, unknown = parser.parse_known_args() # get arguments for this operation operation = monobit.OPERATIONS[args.operation[0]] for arg, _type in operation.script_args.items(): if _type == bool: parser.add_argument('--' + arg.strip('_'), dest=arg, action='store_true') else: parser.add_argument('--' + arg.strip('_'), dest=arg, type=CONVERTERS[_type]) args = parser.parse_args() # convert arguments to type accepted by operation fargs = { _name: _arg for _name, _arg in args.__dict__.items() if _arg is not None and _name in operation.script_args } # load, modify, save try: font = monobit.load(args.infile) font = operation(font, **fargs) font.save(args.outfile) except ValueError as exc: logging.error(exc)
#!/usr/bin/env python3 import os import string import tarfile from io import TextIOWrapper import monobit bdf_name = 'uni_vga/u_vga16.bdf' unizip = 'uni-vga.tgz' header = 'header.txt' hex_name = 'univga_16.hex' with tarfile.open(unizip, 'r:gz').extractfile(bdf_name) as infile: uni_vga = monobit.load(TextIOWrapper(infile), format='bdf') with open(hex_name, 'wb') as outfile: with open(header, 'rb') as h: for line in h: outfile.write(line) outfile.write(b'\n') uni_vga.save(outfile, format='hex')
_name: _arg for _name, _arg in vars(args).items() if _arg is not None and _name in loader.script_args } save_args = { _name: _arg for _name, _arg in vars(args).items() if _arg is not None and _name in saver.script_args } if args.debug: loglevel = logging.DEBUG else: loglevel = logging.INFO logging.basicConfig(level=loglevel, format='%(levelname)s: %(message)s') try: font = monobit.load(args.infile, format=args.from_, **load_args) if args.codepage: font = font.set_encoding(args.codepage) if args.comments: with open(args.comments) as f: font = font.add_comments(f.read()) monobit.save(font, args.outfile, format=args.to_, **save_args) except Exception as exc: logging.error(exc) if args.debug: raise
def main(): # register custom FreeDOS codepages for filename in os.listdir(CODEPAGE_DIR): cp_name, ext = os.path.splitext(os.path.basename(filename)) if ext == '.ucp': monobit.Codepage.override(f'cp{cp_name}', f'{os.getcwd()}/{CODEPAGE_DIR}/{filename}') try: os.mkdir('work') except OSError: pass try: os.mkdir('work/yaff') except OSError: pass # read header logging.info('Processing header') with open(HEADER, 'r') as header: comments = tuple(_line[2:].rstrip() for _line in header) # create empty fonts with header final_font = { size: monobit.font.Font(comments=comments).set_encoding('unicode') for size in SIZES } # obtain original source files os.chdir('work') logging.info('Downloading originals.') for source in (CPIDOS_URL, UNIVGA_URL, UNIFONT_URL): target = source.split('/')[-1] if not os.path.isfile(target): logging.info(f'Downloading {source}.') request.urlretrieve(source, target) # process unifont package with tarfile.open(UNIFONT_ZIP, 'r:gz') as unizip: for name in UNIFONT_NAMES: unizip.extract(UNIFONT_DIR + name) # process univga package with tarfile.open(UNIVGA_ZIP, 'r:gz') as univga: univga.extract(UNIVGA_BDF) # process CPIDOS package # unpack zipfile pack = zipfile.ZipFile(CPIDOS_ZIP, 'r') # extract cpi files from compressed cpx files for name in CPI_NAMES: pack.extract(CPI_DIR + name) subprocess.call(['upx', '-d', CPI_DIR + name]) os.chdir('..') # load CPIs and add to dictionary freedos_fonts = {_size: {} for _size in SIZES} for cpi_name in CPI_NAMES: logging.info(f'Reading {cpi_name}') cpi = monobit.load(f'work/{CPI_DIR}{cpi_name}', format='cpi') for font in cpi: codepage = font.encoding # always starts with `cp` height = font.bounding_box[1] # save intermediate file monobit.save( font.add_glyph_names(), f'work/yaff/{cpi_name}_{codepage}_{font.pixel_size:02d}.yaff' ) freedos_fonts[font.pixel_size][(cpi_name, codepage)] = font.set_encoding('unicode') # retrieve preferred picks from choices file logging.info('Processing choices') choices = defaultdict(list) with open(CHOICES, 'r') as f: for line in f: if line and line[0] in ('#', '\n'): continue codepoint, codepagestr = line.strip('\n').split(':', 1) codepage_info = codepagestr.split(':') # e.g. 852:ega.cpx if len(codepage_info) > 1: codepage, cpi_name = codepage_info[:2] else: codepage, cpi_name = codepage_info[0], None choices[(cpi_name, f'cp{codepage}')].append(chr(int(codepoint, 16))) # merge locally drawn glyphs for size in SIZES: for yaff in COMPONENTS[size]: logging.info(f'Merging {yaff}.') final_font[size] = final_font[size].merged_with(monobit.load(yaff)) # merge preferred picks from FreeDOS fonts logging.info('Add freedos preferred glyphs') for size, fontdict in freedos_fonts.items(): for (cpi_name_0, codepage_0), labels in choices.items(): for (cpi_name_1, codepage_1), font in fontdict.items(): if ( (codepage_0 == codepage_1) and (cpi_name_0 is None or cpi_name_0 == cpi_name_1) ): final_font[size] = final_font[size].merged_with(font.subset(labels)) # merge other fonts logging.info('Add remaining freedos glyphs') for size, fontdict in freedos_fonts.items(): for font in fontdict.values(): final_font[size] = final_font[size].merged_with(font) # assign length-1 equivalents logging.info('Assign canonical equivalents.') for size in final_font.keys(): final_font[size] = precompose(final_font[size], max_glyphs=1) # drop glyphs with better alternatives in uni-vga final_font[16] = final_font[16].without(FREEDOS_DROP) # copy glyphs (canonical equivalents have been covered before) for size in final_font.keys(): for copy, orig in FREEDOS_COPY.items(): try: final_font[size] = final_font[size].with_glyph( final_font[size].get_glyph(orig).set_annotations(char=copy) ) except KeyError as e: logging.warning(e) for copy, orig in FREEDOS_MIRROR.items(): try: orig = final_font[size].get_glyph(orig) except KeyError as e: logging.warning(e) else: offsets = orig.ink_offsets mirrored = orig.crop(*offsets).mirror().expand(*offsets) final_font[size] = final_font[size].with_glyph(mirrored.set_annotations(char=copy)) for copy, orig in FREEDOS_FLIP.items(): try: orig = final_font[size].get_glyph(orig) except KeyError as e: logging.warning(e) else: offsets = orig.ink_offsets flipped = orig.crop(*offsets).flip().expand(*offsets) final_font[size] = final_font[size].with_glyph(flipped.set_annotations(char=copy)) for copy, orig in FREEDOS_TURN.items(): try: orig = final_font[size].get_glyph(orig) except KeyError as e: logging.warning(e) else: offsets = orig.ink_offsets flipped = orig.crop(*offsets).flip().mirror().expand(*offsets) final_font[size] = final_font[size].with_glyph(flipped.set_annotations(char=copy)) # read univga univga_orig = monobit.load(f'work/{UNIVGA_BDF}') # replace code points where necessary univga = univga_orig.without(UNIVGA_REPLACE.keys()) for orig, repl in UNIVGA_REPLACE.items(): univga = univga.with_glyph(univga_orig.get_glyph(orig).set_annotations(char=repl)) # drop labels to avoid retaing chars on merge univga = monobit.Font(_g.set_annotations(labels=()) for _g in univga.glyphs) logging.info('Add uni-vga box-drawing glyphs.') box_glyphs = univga.subset(chr(_code) for _code in UNIVGA_UNSHIFTED) final_font[16] = final_font[16].merged_with(box_glyphs) # shift uni-vga baseline down by one logging.info('Add remaining uni-vga glyphs after rebaselining.') univga_rebaselined = univga.without(chr(_code) for _code in UNIVGA_NONPRINTING) univga_rebaselined = univga_rebaselined.expand(top=1).crop(bottom=1) final_font[16] = final_font[16].merged_with(univga_rebaselined) # copy glyphs from uni-vga for copy, orig in UNIVGA_COPY.items(): final_font[16] = final_font[16].with_glyph( final_font[16].get_glyph(orig).set_annotations(char=copy) ) # read unifont logging.info('Add glyphs from unifont.') unifont = monobit.Font().set_encoding('unicode') for name in UNIFONT_NAMES: unifont = unifont.merged_with(monobit.load(f'work/{UNIFONT_DIR}/{name}')) unifont_glyphs = unifont.subset(chr(_code) for _code in UNIFONT_RANGES) final_font[16] = final_font[16].merged_with(unifont_glyphs) # exclude personal use area code points logging.info('Removing private use area') pua_keys = set(chr(_code) for _code in range(0xe000, 0xf900)) pua_font = {_size: _font.subset(pua_keys) for _size, _font in final_font.items()} for size, font in pua_font.items(): monobit.save(font, f'work/pua_{size:02d}.hex', format='hext') final_font = {_size: _font.without(pua_keys) for _size, _font in final_font.items()} logging.info('Sorting glyphs') for size in final_font.keys(): # first take the 437 subset # note this'll be the Freedos 437 as we overrode it keys437 = list(monobit.Codepage('cp437')._mapping.values()) font437 = final_font[size].subset(keys437) sortedfont = monobit.font.Font(sorted( (_glyph for _glyph in final_font[size].glyphs), key=lambda _g: (_g.char or '') )) final_font[size] = font437.merged_with(sortedfont) # output logging.info('Writing output') for size, font in final_font.items(): monobit.save(font.drop_comments(), f'{TARGET_DIR}/default_{size:02d}.hex', format='hext') #composed = { # size: precompose(font, max_glyphs=4) # .without(font.get_chars()).drop_comments().add_glyph_names() # for size, font in final_font.items() #} #for size, font in composed.items(): # monobit.save(font, f'autocomposed_{size:02d}.yaff') for size, font in final_font.items(): wrong_size = [f'{ord(g.char):04x}' for g in font.glyphs if g.height != size or g.width not in (8, 16)] if wrong_size: logging.warning(f'Wrong-size glyphs in {size}px font: {wrong_size}')