def mainProgram(optind): # NON-OPTIONS if len(sys.argv) - optind < 4: raise Exception('invalid number of arguments, try --help') input = sys.argv[optind] st = fnio.InputStream(input) registry = sys.argv[optind + 1] encoding = sys.argv[optind + 2] newCodes = [] if re.match('^[A-Za-z][\w.:()]*$', registry) == None or re.match( '^[\w.:()]+$', encoding) == None: raise Exception('invalid registry or encoding') # READ INPUT try: oldFont = bdf.Font.read(st) st.close() except Exception as e: raise Exception(st.location() + str(e)) # READ TABLES optind += 3 while optind < len(sys.argv): st = fnio.InputStream(sys.argv[optind]) optind += 1 def loadCode(line): newCodes.append(fnutil.parseHex('unicode', line)) try: st.readLines(loadCode) st.close() except Exception as e: raise Exception(st.location() + str(e)) if not newCodes: raise Exception('no characters in the output font') # CREATE GLYPHS newFont = bdf.Font() charMap = {char.code: char for char in oldFont.chars} index = 0 unstart = 0 if filter: unstart = 32 if registry == 'ISO10646' else 1114111 for code in newCodes: if filter and code == 0xFFFF: index += 1 continue if code in charMap: oldChar = charMap[code] uniFFFF = False else: missing = hex(code)[2:].upper() uniFFFF = True if code != 0xFFFF: raise Exception('{0} does not contain {1}'.format( input, missing)) if oldFont.defaultCode != None: oldChar = charMap[oldFont.defaultCode] elif 0xFFFD in charMap: oldChar = charMap[0xFFFD] else: raise Exception( '{0} does not contain {1}, and no stub found'.format( input, missing)) newChar = copy.copy(oldChar) newChar.props = oldChar.props.copy() newChar.code = code if index >= unstart else index index += 1 newChar.props['ENCODING'] = str(newChar.code) newFont.chars.append(newChar) if uniFFFF: newChar.props['STARTCHAR'] = 'uniFFFF' elif oldChar.code == oldFont.defaultCode or ( oldChar.code == 0xFFFD and newFont.defaultCode == None): newFont.defaultCode = newChar.code # CREATE HEADER propertyCount = None propertyDelta = 0 for name, value in oldFont.props.items(): if name == 'FONT': newFont.xlfd = oldFont.xlfd[:] newFont.xlfd[13] = registry newFont.xlfd[14] = encoding value = '-'.join(newFont.xlfd) elif name == 'STARTPROPERTIES': propertyCount = fnutil.parseDec(name, value, 1, 250) elif name == 'CHARSET_REGISTRY': value = '"' + registry + '"' elif name == 'CHARSET_ENCODING': value = '"' + encoding + '"' elif name == 'DEFAULT_CHAR': if newFont.defaultCode != None: value = str(newFont.defaultCode) else: propertyDelta = -1 continue elif name == 'ENDPROPERTIES': if propertyCount == None: raise Exception(input + ': ENDPROPERTIES without STARTPROPERTIES') if newFont.defaultCode != None and not 'DEFAULT_CHAR' in newFont.props: newFont.props['DEFAULT_CHAR'] = str(newFont.defaultCode) propertyDelta += 1 newFont.props['STARTPROPERTIES'] = str(propertyCount + propertyDelta) elif name == 'CHARS': value = str(len(newFont.chars)) newFont.props[name] = value newFont.version = oldFont.version newFont.bbx = oldFont.bbx # WRITE OUTPUT st = fnio.OutputStream(output) try: newFont.write(st) st.close() except Exception as e: st.destroy() raise Exception(st.fileName + ': ' + str(e))
def main_program(nonopt, parsed): version = parsed.version exchange = parsed.exchange bdfile = len(nonopt) > 0 and nonopt[0].lower().endswith('.bdf') ver1_unicodes = True # READ INPUT ifs = fnio.InputStream(nonopt[0] if bdfile else None) try: font = bmpf.Font.read(ifs) ifs.close() for char in font.chars: prefix = 'char %d: ' % char.code if char.width != font.bbx.width: raise Exception(prefix + 'output width not equal to maximum output width') if char.code == 65534: raise Exception(prefix + 'not a character, use 65535 for empty position') if char.code >= 65536: if version == 1: raise Exception(prefix + '-1 requires unicodes <= 65535') ver1_unicodes = False # VERSION ver1_num_chars = len(font.chars) == 256 or len(font.chars) == 512 if version == 1: if not ver1_num_chars: raise Exception('-1 requires a font with 256 or 512 characters') if font.bbx.width != 8: raise Exception('-1 requires a font with width 8') # EXCHANGE vga_num_chars = len(font.chars) >= 224 and len(font.chars) <= 512 vga_text_size = font.bbx.width == 8 and font.bbx.height in [8, 14, 16] if exchange is True: if not vga_num_chars: raise Exception('-g/--vga requires a font with 224...512 characters') if not vga_text_size: raise Exception('-g/--vga requires an 8x8, 8x14 or 8x16 font') except Exception as ex: raise Exception(ifs.location() + str(ex)) # READ EXTRAS tables = dict() def load_extra(line): nonlocal ver1_unicodes words = re.split(br'\s+', line) if len(words) < 2: raise Exception('invalid format') uni = fnutil.parse_hex('unicode', words[0]) if uni == 0xFFFE: raise Exception('FFFE is not a character') if next((char for char in font.chars if char.code == uni), None): if uni >= 0x10000: ver1_unicodes = False if uni not in tables: tables[uni] = [] table = tables[uni] for word in words[1:]: dup = fnutil.parse_hex('extra code', word) if dup == 0xFFFF: raise Exception('FFFF is not a character') if dup >= 0x10000: ver1_unicodes = False if not dup in table or 0xFFFE in table: tables[uni].append(dup) if version == 1 and not ver1_unicodes: raise Exception('-1 requires unicodes <= FFFF') for name in nonopt[int(bdfile):]: ifs = fnio.InputStream(name) try: ifs.read_lines(load_extra) ifs.close() except Exception as ex: raise Exception(ifs.location() + str(ex)) # VERSION if version == -1: version = 1 if ver1_num_chars and ver1_unicodes and font.bbx.width == 8 else 2 # EXCHANGE if exchange == -1: exchange = vga_text_size and version >= 1 and vga_num_chars and font.chars[0].code == 0x00A3 if exchange: font.chars = font.chars[192:224] + font.chars[32:192] + font.chars[0:32] + font.chars[224:] # WRITE ofs = fnio.OutputStream(parsed.output) if ofs.file.isatty(): raise Exception('binary output may not be send to a terminal, use -o or redirect/pipe it') try: # HEADER if version == 1: ofs.write8(0x36) ofs.write8(0x04) ofs.write8((len(font.chars) >> 8) + 1) ofs.write8(font.bbx.height) elif version == 2: ofs.write32(0x864AB572) ofs.write32(0x00000000) ofs.write32(0x00000020) ofs.write32(0x00000001) ofs.write32(len(font.chars)) ofs.write32(len(font.chars[0].data)) ofs.write32(font.bbx.height) ofs.write32(font.bbx.width) # GLYPHS for char in font.chars: ofs.write(char.data) # UNICODES if version > 0: def write_unicode(code): if version == 1: ofs.write16(code) elif code <= 0x7F: ofs.write8(code) elif code in [0xFFFE, 0xFFFF]: ofs.write8(code & 0xFF) else: if code <= 0x7FF: ofs.write8(0xC0 + (code >> 6)) else: if code <= 0xFFFF: ofs.write8(0xE0 + (code >> 12)) else: ofs.write8(0xF0 + (code >> 18)) ofs.write8(0x80 + ((code >> 12) & 0x3F)) ofs.write8(0x80 + ((code >> 6) & 0x3F)) ofs.write8(0x80 + (code & 0x3F)) for char in font.chars: if char.code != 0xFFFF: write_unicode(char.code) if char.code in tables: for extra in tables[char.code]: write_unicode(extra) write_unicode(0xFFFF) # FINISH ofs.close() except Exception as ex: raise Exception(ofs.location() + str(ex) + ofs.destroy())
def mainProgram(optind): WIN_FONTHEADERSIZE = 118 global charset global minChar # READ INPUT if len(sys.argv) - optind > 1: raise Exception('invalid number of arguments, try --help') st = fnio.InputStream(sys.argv[optind] if optind < len(sys.argv) else None) try: font = bdf.Font.read(st) st.close() except Exception as e: raise Exception(st.location() + str(e)) # PRE-COMPUTE if charset == None: encoding = font.xlfd[bdf.XLFD.CHARSET_ENCODING] if re.match('(cp)?125[0-8]', encoding.lower()): FNT_CHARSETS = [238, 204, 0, 161, 162, 177, 178, 186, 163] charset = FNT_CHARSETS[int(encoding[-1:])] else: charset = 255 try: # CHECK INPUT if font.bbx.yoff > 0 or font.bbx.height + font.bbx.yoff < 0: raise Exception('FONTBOUNDINGBOX yoff must be between 0 and -' + str(font.bbx.height)) for char in font.chars: prefix = 'char {0}: '.format(char.code) if char.bbx.width > font.bbx.width: raise Exception(prefix + 'BBX width exceeds FONTBOUNDINGBOX') if char.bbx.height != font.bbx.height or char.bbx.yoff != font.bbx.yoff: raise Exception( prefix + 'BBX height and yoff must be identical to FONTBOUNDINGBOX') numChars = len(font.chars) if numChars > 256: raise Exception('too many characters, the maximum is 256') if minChar == None: if numChars == 192 or numChars == 256: minChar = 256 - numChars else: minChar = font.chars[0].code maxChar = minChar + numChars - 1 if maxChar >= 256: raise Exception('the maximum char is too big, (re)specify -m') # HEADER vTell = WIN_FONTHEADERSIZE + (numChars + 1) * 4 bitsOffset = vTell cTable = [] widthBytes = 0 # CTABLE/GLYPHS for char in font.chars: rowSize = char.bbx.rowSize() cTable.append(char.bbx.width) cTable.append(vTell) vTell += rowSize * font.bbx.height widthBytes += rowSize if vTell > 0xFFFF: raise Exception('too much character data') # SENTINEL sentinel = 2 - widthBytes % 2 cTable.append(sentinel * 8) cTable.append(vTell) vTell += sentinel * font.bbx.height widthBytes += sentinel if widthBytes > 0xFFFF: raise Exception('the total character width is too big') except Exception as e: raise Exception(st.fileName + str(e)) # WRITE st = fnio.OutputStream(output) if st.file.isatty(): raise Exception( 'binary output may not be send to a terminal, use -o or redirect/pipe it' ) def IntEql(index, value): return 1 if font.xlfd[index].upper() == value else 0 try: # HEADER family = font.xlfd[bdf.XLFD.FAMILY_NAME] copyright = fnutil.unQuote( font.props['COPYRIGHT'])[:60] if 'COPYRIGHT' in font.props else '' proportional = IntEql(bdf.XLFD.SPACING, 'P') st.write16(0x0200) # font version st.write32(vTell + len(family) + 1) # total size st.writeZStr(copyright, 60 - len(copyright)) st.write16(0) # gdi, device type st.write16(round(font.bbx.height * 72 / 96)) st.write16(96) # vertical resolution st.write16(96) # horizontal resolution st.write16(font.bbx.height + font.bbx.yoff) st.write16(0) # internal leading st.write16(0) # external leading st.write8(IntEql(bdf.XLFD.SLANT, 'I')) st.write8(0) # underline st.write8(0) # strikeout st.write16(400 + 300 * IntEql(bdf.XLFD.WEIGHT_NAME, 'BOLD')) st.write8(charset) st.write16(font.bbx.width * (1 - proportional)) st.write16(font.bbx.height) st.write8((fntFamily << 4) + proportional) st.write16(font.getAvgWidth()) st.write16(font.bbx.width) # max width st.write8(minChar) st.write8(maxChar) defaultIndex = maxChar - minChar breakIndex = 0 if font.defaultCode != None: defaultIndex = next(index for index, char in enumerate(font.chars) if char.code == font.defaultCode) if minChar <= 0x20 and maxChar >= 0x20: breakIndex = 0x20 - minChar st.write8(defaultIndex) st.write8(breakIndex) st.write16(widthBytes) st.write32(0) # device name st.write32(vTell) st.write32(0) # gdi bits pointer st.write32(bitsOffset) st.write8(0) # reserved # CTABLE for value in cTable: st.write16(value) # GLYPHS data = bytearray(font.bbx.rowSize() * font.bbx.height) for char in font.chars: rowSize = char.bbx.rowSize() counter = 0 # MS coordinates for n in range(0, rowSize): for y in range(0, font.bbx.height): data[counter] = char.data[rowSize * y + n] counter += 1 st.write(data, counter) st.write(bytes(sentinel * font.bbx.height)) # FAMILY st.writeZStr(family, 1) st.close() except Exception as e: st.destroy() raise Exception(st.fileName + str(e))
def main_program(nonopt, parsed): bstr = lambda number: bytes(str(number), 'ascii') # NON-OPTIONS if len(nonopt) < 4: raise Exception('invalid number of arguments, try --help') input = nonopt[0] registry = nonopt[1] encoding = nonopt[2] new_codes = [] if not re.fullmatch(r'[A-Za-z][\w.:()]*', registry) or not re.fullmatch( r'[\w.:()]+', encoding): raise Exception('invalid registry or encoding') # READ INPUT ifs = fnio.InputStream(input) try: old_font = bdf.Font.read(ifs) ifs.close() except Exception as ex: raise Exception(ifs.location() + str(ex)) # READ TABLES def load_code(line): new_codes.append(fnutil.parse_hex('unicode', line)) for name in nonopt[3:]: ifs = fnio.InputStream(name) try: ifs.read_lines(load_code) ifs.close() except Exception as ex: raise Exception(ifs.location() + str(ex)) if not new_codes: raise Exception('no characters in the output font') # CREATE GLYPHS new_font = bdf.Font() charmap = {char.code: char for char in old_font.chars} index = 0 unstart = 0 family = parsed.family if parsed.family is not None else old_font.xlfd[ bdf.XLFD.FAMILY_NAME] if parsed.filter: unstart = 32 if registry == 'ISO10646' else bdf.CHARS_MAX for code in new_codes: if code == 0xFFFF and parsed.filter: index += 1 continue if code in charmap: old_char = charmap[code] uni_ffff = False else: uni_ffff = True if code != 0xFFFF: raise Exception('%s does not contain U+%04X' % (input, code)) if old_font.default_code != -1: old_char = charmap[old_font.default_code] elif 0xFFFD in charmap: old_char = charmap[0xFFFD] else: raise Exception( '%s does not contain U+FFFF, and no replacement found' % input) new_char = copy.copy(old_char) new_char.code = code if index >= unstart else index index += 1 new_char.props = new_char.props.clone() new_char.props.set('ENCODING', bstr(new_char.code)) new_font.chars.append(new_char) if uni_ffff: new_char.props.set('STARTCHAR', b'uniFFFF') elif old_char.code == old_font.default_code or ( old_char.code == 0xFFFD and new_font.default_code == -1): new_font.default_code = new_char.code # CREATE HEADER registry = bytes(registry, 'ascii') encoding = bytes(encoding, 'ascii') for [name, value] in old_font.props: if name == 'FONT': new_font.xlfd = old_font.xlfd[:] new_font.xlfd[bdf.XLFD.FAMILY_NAME] = family new_font.xlfd[bdf.XLFD.CHARSET_REGISTRY] = registry new_font.xlfd[bdf.XLFD.CHARSET_ENCODING] = encoding value = b'-'.join(new_font.xlfd) elif name == 'STARTPROPERTIES': num_props = fnutil.parse_dec(name, value, 1) elif name == 'FAMILY_NAME': value = fnutil.quote(family) elif name == 'CHARSET_REGISTRY': value = fnutil.quote(registry) elif name == 'CHARSET_ENCODING': value = fnutil.quote(encoding) elif name == 'DEFAULT_CHAR': if new_font.default_code != -1: value = bstr(new_font.default_code) else: num_props -= 1 continue elif name == 'ENDPROPERTIES': if new_font.default_code != -1 and new_font.props.get( 'DEFAULT_CHAR') is None: new_font.props.add('DEFAULT_CHAR', bstr(new_font.default_code)) num_props += 1 new_font.props.set('STARTPROPERTIES', bstr(num_props)) elif name == 'CHARS': value = bstr(len(new_font.chars)) new_font.props.add(name, value) # COPY FIELDS new_font.bbx = old_font.bbx new_font.finis = old_font.finis # WRITE OUTPUT ofs = fnio.OutputStream(parsed.output) try: new_font.write(ofs) ofs.close() except Exception as ex: raise Exception(ofs.location() + str(ex) + ofs.destroy())
def main_program(nonopt, parsed): if len(nonopt) > 1: raise Exception('invalid number of arguments, try --help') char_set = parsed.char_set min_char = parsed.min_char # READ INPUT ifs = fnio.InputStream(nonopt[0] if nonopt else None) try: font = bmpf.Font.read(ifs) ifs.close() except Exception as ex: raise Exception(ifs.location() + str(ex)) # COMPUTE if char_set == -1: encoding = font.xlfd[bdf.XLFD.CHARSET_ENCODING] if re.fullmatch(b'(cp)?125[0-8]', encoding.lower()): char_set = FNT_CHARSETS[int(encoding[-1:])] else: char_set = 255 try: num_chars = len(font.chars) if num_chars > 256: raise Exception('too many characters, the maximum is 256') if min_char == -1: if num_chars in [192, 256]: min_char = 256 - num_chars else: min_char = font.chars[0].code max_char = min_char + num_chars - 1 if max_char >= 256: raise Exception('the maximum character code is too big, (re)specify -m') # HEADER vtell = WIN_FONTHEADERSIZE + (num_chars + 1) * 4 bits_offset = vtell ctable = [] width_bytes = 0 # CTABLE/GLYPHS for char in font.chars: row_size = char.row_size() ctable.append(char.width) ctable.append(vtell) vtell += row_size * font.bbx.height width_bytes += row_size if vtell > 0xFFFF: raise Exception('too much character data') # SENTINEL sentinel = 2 - width_bytes % 2 ctable.append(sentinel * 8) ctable.append(vtell) vtell += sentinel * font.bbx.height width_bytes += sentinel if width_bytes > 0xFFFF: raise Exception('the total character width is too big') except Exception as ex: raise Exception(ifs.location() + str(ex)) # WRITE ofs = fnio.OutputStream(parsed.output) if ofs.file.isatty(): raise Exception('binary output may not be send to a terminal, use -o or redirect/pipe it') try: # HEADER family = font.xlfd[bdf.XLFD.FAMILY_NAME] copyright = font.props.get('COPYRIGHT') copyright = fnutil.unquote(copyright)[:60] if copyright is not None else b'' proportional = font.get_proportional() ofs.write16(0x0200) # font version ofs.write32(vtell + len(family) + 1) # total size ofs.write_zstr(copyright, 60 - len(copyright)) ofs.write16(0) # gdi, device type ofs.write16(round(font.bbx.height * 72 / 96)) ofs.write16(96) # vertical resolution ofs.write16(96) # horizontal resolution ofs.write16(font.get_ascent()) # base line ofs.write16(0) # internal leading ofs.write16(0) # external leading ofs.write8(font.get_italic()) ofs.write8(0) # underline ofs.write8(0) # strikeout ofs.write16(400 + 300 * font.get_bold()) ofs.write8(char_set) ofs.write16(0 if proportional else font.bbx.width) ofs.write16(font.bbx.height) ofs.write8((parsed.fnt_family << 4) + proportional) ofs.write16(font.avg_width) ofs.write16(font.bbx.width) ofs.write8(min_char) ofs.write8(max_char) default_index = max_char - min_char break_index = 0 if font.default_code != -1: default_index = next(index for index, char in enumerate(font.chars) if char.code == font.default_code) if min_char <= 0x20 <= max_char: break_index = 0x20 - min_char ofs.write8(default_index) ofs.write8(break_index) ofs.write16(width_bytes) ofs.write32(0) # device name ofs.write32(vtell) ofs.write32(0) # gdi bits pointer ofs.write32(bits_offset) ofs.write8(0) # reserved # CTABLE for value in ctable: ofs.write16(value) # GLYPHS data = bytearray(font.bbx.height * font.bbx.row_size()) for char in font.chars: row_size = char.row_size() counter = 0 # MS coordinates for n in range(0, row_size): for y in range(0, font.bbx.height): data[counter] = char.data[row_size * y + n] counter += 1 ofs.write(data[:counter]) ofs.write(bytes(sentinel * font.bbx.height)) # FAMILY ofs.write_zstr(family, 1) ofs.close() except Exception as ex: raise Exception(ofs.location() + str(ex) + ofs.destroy())
def mainProgram(optind): global version global exchange # READ INPUT if optind < len(sys.argv) and sys.argv[optind].lower().endswith('.bdf'): st = fnio.InputStream(sys.argv[optind]) optind += 1 else: st = fnio.InputStream(None) try: font = bdf.Font.read(st) st.close() except Exception as e: raise Exception(st.location() + str(e)) try: if font.xlfd[bdf.XLFD.SPACING] != 'C': raise Exception('SPACING "C" required') keys = ['width', 'height', 'xoff', 'yoff'] for char in font.chars: for name in keys: if getattr(char.bbx, name) != getattr(font.bbx, name): raise Exception('char {0}: BBX must be identical to FONTBOUNDINGBOX'.format(char.code)) if char.code == 65534: raise Exception('65534 is not a character, use 65535 for empty position') ver1NumChars = len(font.chars) == 256 or len(font.chars) == 512 if version == None: if ver1NumChars and font.bbx.width == 8: version = 1 else: version = 2 elif version == 1: if not ver1NumChars: raise Exception('-1 requires a font with 256 or 512 characters') if font.bbx.width != 8: raise Exception('-1 requires a font with width 8') vgaNumChars = len(font.chars) >= 224 and len(font.chars) <= 512 vgaTextSize = font.bbx.width == 8 and font.bbx.height in [8, 14, 16] if exchange == None: exchange = vgaTextSize and version >= 1 and vgaNumChars and font.chars[0].code == 0x00A3 elif exchange: if not vgaNumChars: raise Exception('-g/--vga requires a font with 224...512 characters') if not vgaTextSize: raise Exception('-g/--vga requires an 8x8, 8x14 or 8x16 font') except Exception as e: raise Exception(st.fileName + ': ' + str(e)) # READ EXTRAS tables = dict() def loadExtra(line): words = re.split('\s+', line) if len(words) < 2: raise Exception('invalid format') uni = fnutil.parseHex('unicode', words[0]) if uni == 0xFFFE: raise Exception('FFFE is not a character') if not uni in tables: tables[uni] = [] table = tables[uni] del words[0] for word in words: dup = fnutil.parseHex('extra code', word) if dup == 0xFFFF: raise Exception('FFFF is not a character') if not dup in table or 0xFFFE in table: tables[uni].append(dup) while optind < len(sys.argv): st = fnio.InputStream(sys.argv[optind]) optind += 1 try: st.readLines(loadExtra) st.close() except Exception as e: raise Exception(st.location() + str(e)) # REMAP newChars = font.chars[:] if exchange: for index in range(0, 32): newChars[index] = font.chars[0xC0 + index] newChars[0xC0 + index] = font.chars[index] # WRITE st = fnio.OutputStream(output) if st.file.isatty(): raise Exception('binary output may not be send to a terminal, use -o or redirect/pipe it') try: # HEADER if version == 1: st.write8(0x36) st.write8(0x04) st.write8((len(font.chars) >> 8) + 1) st.write8(font.bbx.height) elif version == 2: st.write32(0x864AB572) st.write32(0x00000000) st.write32(0x00000020) st.write32(0x00000001) st.write32(len(font.chars)) st.write32(len(font.chars[0].data)) st.write32(font.bbx.height) st.write32(font.bbx.width) # GLYPHS for char in newChars: st.write(char.data, None) # UNICODES if version > 0: def writeUnicode(code): if version == 1: st.write16(code) elif code <= 0x7F: st.write8(code) elif code == 0xFFFE or code == 0xFFFF: st.write8(code & 0xFF) else: if code <= 0x7FF: st.write8(0xC0 + (code >> 6)) else: if code <= 0xFFFF: st.write8(0xE0 + (code >> 12)) else: st.write8(0xF0 + (code >> 18)) st.write8(0x80 + ((code >> 12) & 0x3F)) st.write8(0x80 + ((code >> 6) & 0x3F)) st.write8(0x80 + (code & 0x3F)) for char in newChars: if char.code != 0xFFFF: writeUnicode(char.code) if char.code in tables: for extra in tables[char.code]: writeUnicode(extra) writeUnicode(0xFFFF) # FINISH st.close() except Exception as e: st.destroy() raise Exception(st.fileName + ': ' + str(e))