#!/usr/bin/python # # Urwid BigText fonts # Copyright (C) 2004-2006 Ian Ward # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Urwid web site: https://urwid.org/ from __future__ import annotations import typing import warnings from pprint import pformat from urwid.canvas import CanvasError, TextCanvas from urwid.escape import SAFE_ASCII_DEC_SPECIAL_RE from urwid.util import apply_target_encoding, str_util if typing.TYPE_CHECKING: from collections.abc import Iterator, Sequence from typing_extensions import Literal def separate_glyphs(gdata: str, height: int) -> tuple[dict[str, tuple[int, list[str]]], bool]: """return (dictionary of glyphs, utf8 required)""" gl: list[str] = gdata.split("\n")[1:-1] if any("\t" in elem for elem in gl): raise ValueError(f"Incorrect glyphs data:\n{gdata!r}") if len(gl) != height + 1: raise ValueError(f"Incorrect glyphs height (expected: {height}):\n{gdata}") key_line: str = gl[0] character: str | None = None # current character key_index = 0 # index into character key line end_col = 0 # column position at end of glyph start_col = 0 # column position at start of glyph jl: list[int] = [0] * height # indexes into lines of gdata (gl) result: dict[str, tuple[int, list[str]]] = {} utf8_required = False while True: if character is None: if key_index >= len(key_line): break character = key_line[key_index] if key_index < len(key_line) and key_line[key_index] == character: end_col += str_util.get_width(ord(character)) key_index += 1 continue out: list[str] = [] y = 0 fill = 0 for k, line in enumerate(gl[1:]): j: int = jl[k] y = 0 fill = 0 while y < end_col - start_col: if j >= len(line): fill = end_col - start_col - y break y += str_util.get_width(ord(line[j])) j += 1 if y + fill != end_col - start_col: raise ValueError(repr((y, fill, end_col))) segment = line[jl[k] : j] if not SAFE_ASCII_DEC_SPECIAL_RE.match(segment): utf8_required = True out.append(segment + " " * fill) jl[k] = j start_col = end_col result[character] = (y + fill, out) character = None return result, utf8_required def add_font(name: str, cls: FontRegistry) -> None: warnings.warn( "`add_font` is deprecated, please set 'name' attribute to the font class," " use metaclass keyword argument 'font_name'" " or use `Font.register()`", DeprecationWarning, stacklevel=2, ) cls.register(name) class FontRegistryWarning(UserWarning): """FontRegistry warning.""" class FontRegistry(type): """Font registry. Store all registered fonts, register during class creation if possible. """ __slots__ = () __registered: typing.ClassVar[dict[str, FontRegistry]] = {} def __iter__(self) -> Iterator[str]: """Iterate over registered font names.""" return iter(self.__registered) def __getitem__(self, item: str) -> FontRegistry | None: """Get font by name if registered.""" return self.__registered.get(item) def __class_getitem__(cls, item: str) -> FontRegistry | None: """Get font by name if registered. This method is needed to get access to font from registry class. >>> repr(FontRegistry["a"]) 'None' >>> font = FontRegistry["Thin 3x3"]() >>> font.height 3 >>> canvas: TextCanvas = font.render("+") >>> b'\\n'.join(canvas.text).decode('utf-8') == " \\n ┼\\n " True """ return cls.__registered.get(item) @property def registered(cls) -> Sequence[str]: """Registered font names in alphabetical order.""" return tuple(sorted(cls.__registered)) @classmethod def as_list(mcs) -> list[tuple[str, FontRegistry]]: """List of (font name, font class) tuples.""" return list(mcs.__registered.items()) def __new__( # noqa: PYI034 # new can not return Self cls: type[FontRegistry], name: str, bases: tuple[type, ...], namespace: dict[str, typing.Any], **kwds: typing.Any, ) -> FontRegistry: font_name: str = namespace.setdefault("name", kwds.get("font_name", "")) font_class = super().__new__(cls, name, bases, namespace) if font_name: if font_name not in cls.__registered: cls.__registered[font_name] = font_class if cls.__registered[font_name] != font_class: warnings.warn( f"{font_name!r} is already registered, please override explicit if required or change name", FontRegistryWarning, stacklevel=2, ) return font_class def register(cls, font_name: str) -> None: """Register font explicit. :param font_name: Font name to use in registration. """ if not font_name: raise ValueError('"font_name" is not set.') cls.__registered[font_name] = cls get_all_fonts = FontRegistry.as_list class Font(metaclass=FontRegistry): """Font base class.""" __slots__ = ("char", "canvas", "utf8_required") height: int data: Sequence[str] name: str def __init__(self) -> None: if not self.height: raise ValueError(f'"height" is invalid: {self.height!r}') if not self.data: raise ValueError(f'"data" is empty: {self.data!r}') self.char: dict[str, tuple[int, list[str]]] = {} self.canvas: dict[str, TextCanvas] = {} self.utf8_required = False data: list[str] = [self._to_text(block) for block in self.data] for gdata in data: self.add_glyphs(gdata) def __repr__(self) -> str: return f"{self.__class__.__name__}()" def __str__(self) -> str: """Font description.""" return f"{self.__class__.__name__}():\n {self.height!r}\n {pformat(self.data, indent=4)}" @staticmethod def _to_text( obj: str | bytes, encoding: str = "utf-8", errors: Literal["strict", "ignore", "replace"] = "strict", ) -> str: if isinstance(obj, str): return obj if isinstance(obj, bytes): warnings.warn( "Bytes based fonts are deprecated, please switch to the text one", DeprecationWarning, stacklevel=3, ) return obj.decode(encoding, errors) raise TypeError(f"{obj!r} is not str|bytes") def add_glyphs(self, gdata: str) -> None: d, utf8_required = separate_glyphs(gdata, self.height) self.char.update(d) self.utf8_required |= utf8_required def characters(self) -> str: return "".join(sorted(self.char)) def char_width(self, character: str) -> int: if character in self.char: return self.char[character][0] return 0 def char_data(self, character: str) -> list[str]: return self.char[character][1] def render(self, character: str) -> TextCanvas: if character in self.canvas: return self.canvas[character] width, line = self.char[character] byte_lines = [] character_set_lines = [] for d in line: t, cs = apply_target_encoding(d) byte_lines.append(t) character_set_lines.append(cs) try: canv = TextCanvas(byte_lines, None, character_set_lines, maxcol=width, check_width=False) except CanvasError as exc: raise CanvasError(f"Failed render of {character!r} from line {line!r}:\n{self}\n:{exc}").with_traceback( exc.__traceback__ ) from exc self.canvas[character] = canv return canv # safe_palette = u"┘┐┌└┼─├┤┴┬│" # more_palette = u"═║╒╓╔╕╖╗╘╙╚╛╜╝╞╟╠╡╢╣╤╥╦╧╨╩╪╫╬○" # block_palette = u"▄#█#▀#▌#▐#▖#▗#▘#▙#▚#▛#▜#▝#▞#▟" class Thin3x3Font(Font): name = "Thin 3x3" height = 3 data = ( """ 000111222333444555666777888999 ! ┌─┐ ┐ ┌─┐┌─┐ ┐┌─ ┌─ ┌─┐┌─┐┌─┐ │ │ │ │ ┌─┘ ─┤└─┼└─┐├─┐ ┼├─┤└─┤ │ └─┘ ┴ └─ └─┘ ┴ ─┘└─┘ ┴└─┘ ─┘ . """, r""" "###$$$%%%'*++,--.///:;==???[[\\\]]^__` " ┼┼┌┼┐O /' /.. _┌─┐┌ \ ┐^ ` ┼┼└┼┐ / * ┼ ─ / ., _ ┌┘│ \ │ └┼┘/ O , ./ . └ \ ┘ ── """, ) class Thin4x3Font(Font): name = "Thin 4x3" height = 3 data = ( *Thin3x3Font.data, """ 0000111122223333444455556666777788889999 ####$$$$ ┌──┐ ┐ ┌──┐┌──┐ ┐┌── ┌── ┌──┐┌──┐┌──┐ ┼─┼┌┼┼┐ │ │ │ ┌──┘ ─┤└──┼└──┐├──┐ ┼├──┤└──┤ ┼─┼└┼┼┐ └──┘ ┴ └── └──┘ ┴ ──┘└──┘ ┴└──┘ ──┘ └┼┼┘ """, ) class Sextant3x3Font(Font): name = "Sextant 3x3" height = 3 data = ( """ !!!###$$$%%%&&&'''((()))***+++,,,---.../// ▐ 🬞🬲🬲🬞🬍🬋🬉🬄🬖🬦🬧 🬉 🬞🬅 🬁🬢 🬞🬦🬞 🬦 🬖 🬉 🬇🬛🬛🬞🬰🬗🬞🬅🬭🬦🬈🬖 🬉🬏 🬘 🬇🬨🬈🬁🬨🬂 🬭 🬁🬂🬂 🬭 🬞🬅 🬁 🬀🬀 🬁 🬂 🬂🬁 🬁 🬁 🬅 🬂 """, """ 000111222333444555666777888999 🬦🬂🬧🬞🬫 🬇🬂🬧🬁🬂🬧 🬞🬫▐🬂🬂🬞🬅🬀🬁🬂🬙🬦🬂🬧🬦🬂🬧 ▐🬁▐ ▐ 🬞🬅🬀 🬂🬧🬇🬌🬫🬁🬂🬧▐🬂🬧 🬔 🬦🬂🬧 🬂🬙 🬂🬀 🬁 🬁🬂🬂🬁🬂🬀 🬁🬁🬂🬀 🬂🬀 🬀 🬂🬀 🬂 """, """ \"\"\" 🬄🬄 """, """ :::;;;<<<===>>>???@@@ 🬭 🬭 🬖🬀 🬁🬢 🬇🬂🬧🬦🬂🬧 🬰 🬰 🬁🬢 🬠🬰🬰 🬖🬀 🬇🬀▐🬉🬅 🬂 🬅 🬀 🬁 🬁 🬂🬀 """, """ AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQ 🬞🬅🬢▐🬂🬧🬦🬂🬈▐🬂🬧▐🬂🬂▐🬂🬂🬦🬂🬈▐ ▐ 🬨🬀 ▐▐🬞🬅▐ ▐🬢🬫▐🬢▐🬦🬂🬧▐🬂🬧🬦🬂🬧 ▐🬋🬫▐🬂🬧▐ 🬞▐ ▐▐🬂 ▐🬂 ▐ 🬨▐🬂🬨 ▐ 🬞 ▐▐🬈🬏▐ ▐🬁▐▐ 🬨▐ ▐▐🬂🬀▐🬇🬘 🬁 🬁🬁🬂🬀 🬂🬀🬁🬂🬀🬁🬂🬂🬁 🬂🬂🬁 🬁 🬂🬀 🬂🬀🬁 🬁🬁🬂🬂🬁 🬁🬁 🬁 🬂🬀🬁 🬂🬁 """, """ RRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[]]]^^^___``` ▐🬂🬧🬦🬂🬈🬁🬨🬂▐ ▐▐ ▐▐ ▐🬉🬏🬘▐ ▐🬁🬂🬙 🬕🬀 🬂▌🬞🬅🬢 🬈🬏 ▐🬊🬐🬞🬂🬧 ▐ ▐ ▐🬉🬏🬘▐🬖🬷🬞🬅🬢 🬧🬀🬞🬅 ▌ ▌ 🬁 🬁 🬂🬀 🬁 🬂🬀 🬁 🬁 🬁🬁 🬁 🬁 🬁🬂🬂 🬂🬀 🬂🬀 🬂🬂🬂 """, """ \\\\\\ 🬇🬏 🬁🬢 """, ) class Sextant2x2Font(Font): name = "Sextant 2x2" height = 2 data = """ ..,,%%00112233445566778899 🬁🬖▐🬨🬇▌🬁🬗🬠🬸🬦▐▐🬒▐🬭🬁🬙▐🬸▐🬸 🬇 🬇🬀🬁🬇🬉🬍 🬄🬉🬋🬇🬍🬁🬊🬇🬅🬉🬍 🬄🬉🬍 🬉 """ class HalfBlock5x4Font(Font): name = "Half Block 5x4" height = 4 data = ( """ 00000111112222233333444445555566666777778888899999 !! ▄▀▀▄ ▄█ ▄▀▀▄ ▄▀▀▄ ▄ █ █▀▀▀ ▄▀▀ ▀▀▀█ ▄▀▀▄ ▄▀▀▄ █ █ █ █ ▄▀ ▄▀ █▄▄█ █▄▄ █▄▄ ▐▌ ▀▄▄▀ ▀▄▄█ █ █ █ █ ▄▀ ▄ █ █ █ █ █ █ █ █ █ ▀ ▀▀ ▀▀▀ ▀▀▀▀ ▀▀ ▀ ▀▀▀ ▀▀ ▀ ▀▀ ▀▀ ▀ """, ''' """######$$$$$$%%%%%&&&&&((()))******++++++,,,-----..////::;;; █▐▌ █ █ ▄▀█▀▄ ▐▌▐▌ ▄▀▄ █ █ ▄ ▄ ▄ ▐▌ ▀█▀█▀ ▀▄█▄ █ ▀▄▀ ▐▌ ▐▌ ▄▄█▄▄ ▄▄█▄▄ ▄▄▄▄ █ ▀ ▀ ▀█▀█▀ ▄ █ █ ▐▌▄ █ ▀▄▌▐▌ ▐▌ ▄▀▄ █ ▐▌ ▀ ▄▀ ▀ ▀ ▀▀▀ ▀ ▀ ▀▀ ▀ ▀ ▄▀ ▀ ▀ ''', r""" <<<<<=====>>>>>?????@@@@@@[[[[\\\\]]]]^^^^____```{{{{||}}}}~~~~''``` ▄▀ ▀▄ ▄▀▀▄ ▄▀▀▀▄ █▀▀ ▐▌ ▀▀█ ▄▀▄ ▀▄ ▄▀ █ ▀▄ ▄ █ ▄▀ ▄▀ ▀▀▀▀ ▀▄ ▄▀ █ █▀█ █ █ █ ▄▀ █ ▀▄ ▐▐▌▌ ▀▄ ▀▀▀▀ ▄▀ ▀ █ ▀▀▀ █ ▐▌ █ █ █ █ ▀ ▀ ▀ ▀ ▀▀▀ ▀▀▀ ▀ ▀▀▀ ▀▀▀▀ ▀ ▀ ▀ """, """ AAAAABBBBBCCCCCDDDDDEEEEEFFFFFGGGGGHHHHHIIJJJJJKKKKK ▄▀▀▄ █▀▀▄ ▄▀▀▄ █▀▀▄ █▀▀▀ █▀▀▀ ▄▀▀▄ █ █ █ █ █ █ █▄▄█ █▄▄▀ █ █ █ █▄▄ █▄▄ █ █▄▄█ █ █ █▄▀ █ █ █ █ █ ▄ █ █ █ █ █ ▀█ █ █ █ ▄ █ █ ▀▄ ▀ ▀ ▀▀▀ ▀▀ ▀▀▀ ▀▀▀▀ ▀ ▀▀ ▀ ▀ ▀ ▀▀ ▀ ▀ """, """ LLLLLMMMMMMNNNNNOOOOOPPPPPQQQQQRRRRRSSSSSTTTTT █ █▄ ▄█ ██ █ ▄▀▀▄ █▀▀▄ ▄▀▀▄ █▀▀▄ ▄▀▀▄ ▀▀█▀▀ █ █ ▀ █ █▐▌█ █ █ █▄▄▀ █ █ █▄▄▀ ▀▄▄ █ █ █ █ █ ██ █ █ █ █ ▌█ █ █ ▄ █ █ ▀▀▀▀ ▀ ▀ ▀ ▀ ▀▀ ▀ ▀▀▌ ▀ ▀ ▀▀ ▀ """, """ UUUUUVVVVVVWWWWWWXXXXXXYYYYYYZZZZZ █ █ █ █ █ █ █ █ █ █ ▀▀▀█ █ █ ▐▌ ▐▌ █ ▄ █ ▀▄▀ ▀▄▀ ▄▀ █ █ █ █ ▐▌█▐▌ ▄▀ ▀▄ █ █ ▀▀ ▀ ▀ ▀ ▀ ▀ ▀ ▀▀▀▀ """, """ aaaaabbbbbcccccdddddeeeeeffffggggghhhhhiijjjjkkkkk █ █ ▄▀▀ █ ▄ ▄ █ ▀▀▄ █▀▀▄ ▄▀▀▄ ▄▀▀█ ▄▀▀▄ ▀█▀ ▄▀▀▄ █▀▀▄ ▄ ▄ █ ▄▀ ▄▀▀█ █ █ █ ▄ █ █ █▀▀ █ ▀▄▄█ █ █ █ █ █▀▄ ▀▀▀ ▀▀▀ ▀▀ ▀▀▀ ▀▀ ▀ ▄▄▀ ▀ ▀ ▀ ▄▄▀ ▀ ▀ """, """ llmmmmmmnnnnnooooopppppqqqqqrrrrssssstttt █ █ █ █▀▄▀▄ █▀▀▄ ▄▀▀▄ █▀▀▄ ▄▀▀█ █▀▀ ▄▀▀▀ ▀█▀ █ █ █ █ █ █ █ █ █ █ █ █ █ ▀▀▄ █ ▀ ▀ ▀ ▀ ▀ ▀▀ █▀▀ ▀▀█ ▀ ▀▀▀ ▀ """, """ uuuuuvvvvvwwwwwwxxxxxxyyyyyzzzzz █ █ █ █ █ ▄ █ ▀▄ ▄▀ █ █ ▀▀█▀ █ █ ▐▌▐▌ ▐▌█▐▌ ▄▀▄ ▀▄▄█ ▄▀ ▀▀ ▀▀ ▀ ▀ ▀ ▀ ▄▄▀ ▀▀▀▀ """, ) class HalfBlock6x5Font(Font): name = "Half Block 6x5" height = 5 data = """ 000000111111222222333333444444555555666666777777888888999999 ..:://// ▄▀▀▀▄ ▄█ ▄▀▀▀▄ ▄▀▀▀▄ ▄ █ █▀▀▀▀ ▄▀▀▀ ▀▀▀▀█ ▄▀▀▀▄ ▄▀▀▀▄ █ █ █ █ █ █ █ █ █ █ ▐▌ █ █ █ █ ▀ ▐▌ █ █ █ ▄▀ ▀▀▄ ▀▀▀█▀ ▀▀▀▀▄ █▀▀▀▄ █ ▄▀▀▀▄ ▀▀▀█ ▄ █ █ █ █ ▄▀ ▄ █ █ █ █ █ ▐▌ █ █ █ ▐▌ ▀▀▀ ▀▀▀ ▀▀▀▀▀ ▀▀▀ ▀ ▀▀▀▀ ▀▀▀ ▀ ▀▀▀ ▀▀▀ ▀ ▀ """ class HalfBlockHeavy6x5Font(Font): name = "Half Block Heavy 6x5" height = 5 data = """ 000000111111222222333333444444555555666666777777888888999999 ..:://// ▄███▄ ▐█▌ ▄███▄ ▄███▄ █▌ █████ ▄███▄ █████ ▄███▄ ▄███▄ █▌ █▌ ▐█ ▀█▌ ▀ ▐█ ▀ ▐█ █▌ █▌ █▌ █▌ █▌ █▌ ▐█ █▌ ▐█ █▌ ▐█ █▌ ▐█ █▌ ▄█▀ ██▌ █████ ████▄ ████▄ ▐█ ▐███▌ ▀████ █▌ █▌ ▐█ █▌ ▄█▀ ▄ ▐█ █▌ ▐█ █▌ ▐█ █▌ █▌ ▐█ ▐█ █▌▐█ ▀███▀ ███▌ █████ ▀███▀ █▌ ████▀ ▀███▀ ▐█ ▀███▀ ▀███▀ █▌ █▌ """ class Thin6x6Font(Font): name = "Thin 6x6" height = 6 data = ( """ 000000111111222222333333444444555555666666777777888888999999'' ┌───┐ ┐ ┌───┐ ┌───┐ ┐ ┌─── ┌─── ┌───┐ ┌───┐ ┌───┐ │ │ │ │ │ │ ┌ │ │ │ │ │ │ │ │ │ / │ │ ┌───┘ ─┤ └──┼─ └───┐ ├───┐ ┼ ├───┤ └───┤ │ │ │ │ │ │ │ │ │ │ │ │ │ └───┘ ┴ └─── └───┘ ┴ ───┘ └───┘ ┴ └───┘ ───┘ """, r''' !! """######$$$$$$%%%%%%&&&&&&((()))******++++++ │ ││ ┌ ┌ ┌─┼─┐ ┌┐ / ┌─┐ / \ │ ─┼─┼─ │ │ └┘ / │ │ │ │ \ / │ │ │ │ └─┼─┐ / ┌─\┘ │ │ ──X── ──┼── │ ─┼─┼─ │ │ / ┌┐ │ \, │ │ / \ │ . ┘ ┘ └─┼─┘ / └┘ └───\ \ / ''', r""" ,,-----..//////::;;<<<<=====>>>>??????@@@@@@ / ┌───┐ ┌───┐ / . . / ──── \ │ │┌──┤ ──── / / \ ┌─┘ ││ │ / . , \ ──── / │ │└──┘ , . / \ / . └───┘ """, r""" [[\\\\\\]]^^^____``{{||}}~~~~~~ ┌ \ ┐ /\ \ ┌ │ ┐ │ \ │ │ │ │ ┌─┐ │ \ │ ┤ │ ├ └─┘ │ \ │ │ │ │ └ \ ┘ ──── └ │ ┘ """, """ AAAAAABBBBBBCCCCCCDDDDDDEEEEEEFFFFFFGGGGGGHHHHHHIIJJJJJJ ┌───┐ ┬───┐ ┌───┐ ┬───┐ ┬───┐ ┬───┐ ┌───┐ ┬ ┬ ┬ ┬ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├───┤ ├───┤ │ │ │ ├── ├── │ ──┬ ├───┤ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ┬ │ ┴ ┴ ┴───┘ └───┘ ┴───┘ ┴───┘ ┴ └───┘ ┴ ┴ ┴ └───┘ """, """ KKKKKKLLLLLLMMMMMMNNNNNNOOOOOOPPPPPPQQQQQQRRRRRRSSSSSS ┬ ┬ ┬ ┌─┬─┐ ┬─┐ ┬ ┌───┐ ┬───┐ ┌───┐ ┬───┐ ┌───┐ │ ┌─┘ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├─┴┐ │ │ │ │ │ │ │ │ │ ├───┘ │ │ ├─┬─┘ └───┐ │ └┐ │ │ │ │ │ │ │ │ │ │ ┐│ │ └─┐ │ ┴ ┴ ┴───┘ ┴ ┴ ┴ └─┴ └───┘ ┴ └──┼┘ ┴ ┴ └───┘ └ """, """ TTTTTTUUUUUUVVVVVVWWWWWWXXXXXXYYYYYYZZZZZZ ┌─┬─┐ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┌───┐ │ │ │ │ │ │ │ └┐ ┌┘ │ │ ┌─┘ │ │ │ │ │ │ │ │ ├─┤ └─┬─┘ ┌┘ │ │ │ └┐ ┌┘ │ │ │ ┌┘ └┐ │ ┌┘ ┴ └───┘ └─┘ └─┴─┘ ┴ ┴ ┴ └───┘ """, """ aaaaaabbbbbbccccccddddddeeeeeefffgggggghhhhhhiijjj ┌─┐ │ │ │ │ . . ┌───┐ ├───┐ ┌───┐ ┌───┤ ┌───┐ ┼ ┌───┐ ├───┐ ┐ ┐ ┌───┤ │ │ │ │ │ ├───┘ │ │ │ │ │ │ │ └───┴ └───┘ └───┘ └───┘ └───┘ ┴ └───┤ ┴ ┴ ┴ │ └───┘ ─┘ """, """ kkkkkkllmmmmmmnnnnnnooooooppppppqqqqqqrrrrrssssss │ │ │ ┌─ │ ┬─┬─┐ ┬───┐ ┌───┐ ┌───┐ ┌───┐ ┬──┐ ┌───┐ ├─┴┐ │ │ │ │ │ │ │ │ │ │ │ │ │ └───┐ ┴ └─ └ ┴ ┴ ┴ ┴ └───┘ ├───┘ └───┤ ┴ └───┘ │ │ """, """ ttttuuuuuuvvvvvvwwwwwwxxxxxxyyyyyyzzzzzz │ ─┼─ ┬ ┬ ┬ ┬ ┬ ┬ ─┐ ┌─ ┬ ┬ ────┬ │ │ │ └┐ ┌┘ │ │ │ ├─┤ │ │ ┌───┘ └─ └───┴ └─┘ └─┴─┘ ─┘ └─ └───┤ ┴──── └───┘ """, ) class HalfBlock7x7Font(Font): name = "Half Block 7x7" height = 7 data = ( """ 0000000111111122222223333333444444455555556666666777777788888889999999''' ▄███▄ ▐█▌ ▄███▄ ▄███▄ █▌ ▐█████▌ ▄███▄ ▐█████▌ ▄███▄ ▄███▄ ▐█ ▐█ █▌ ▀█▌ ▐█ █▌▐█ █▌▐█ █▌ ▐█ ▐█ ▐█ ▐█ █▌▐█ █▌▐█ ▐█ ▐ █▌ █▌ █▌ ▐██ ▐█████▌▐████▄ ▐████▄ █▌ █████ ▀████▌ ▐█ ▌ █▌ █▌ ▄█▀ █▌ █▌ █▌▐█ █▌ ▐█ ▐█ █▌ █▌ ▐█ █▌ █▌ ▄█▀ ▐█ █▌ █▌ █▌▐█ █▌ █▌ ▐█ █▌ █▌ ▀███▀ ███▌ ▐█████▌ ▀███▀ █▌ ▐████▀ ▀███▀ ▐█ ▀███▀ ▀███▀ """, ''' !!! """""#######$$$$$$$%%%%%%%&&&&&&&(((())))*******++++++ ▐█ ▐█ █▌ ▐█ █▌ █ ▄ █▌ ▄█▄ █▌▐█ ▄▄ ▄▄ ▐█ ▐█ █▌▐█████▌ ▄███▄ ▐█▌▐█ ▐█ █▌ ▐█ █▌ ▀█▄█▀ ▐█ ▐█ ▐█ █▌ ▐█▄█▄▄ ▀ █▌ ███ █▌ ▐█ ▐█████▌ ████▌ ▐█ ▐█████▌ ▀▀█▀█▌ ▐█ ▄ ███▌▄ █▌ ▐█ ▄█▀█▄ ▐█ ▐█ █▌ ▀███▀ █▌▐█▌▐█ █▌ ▐█ █▌ ▀▀ ▀▀ ▐█ █ ▐█ ▀ ▀██▀█▌ █▌▐█ ''', """ ,,,------.../////:::;;;<<<<<<<======>>>>>>>???????@@@@@@@ █▌ ▄█▌ ▐█▄ ▄███▄ ▄███▄ ▐█ ▐█ ▐█ ▄█▀ ▐████▌ ▀█▄ ▐█ █▌▐█ ▄▄█▌ ▐████▌ █▌ ▐██ ██▌ █▌ ▐█▐█▀█▌ ▐█ ▐█ ▐█ ▀█▄ ▐████▌ ▄█▀ █▌ ▐█▐█▄█▌ █▌ ▀ ▀█▌ ▐█▀ ▐█ ▀▀▀ ▐█ ▐█ ▐█ █▌ ▀███▀ ▀ """, r""" [[[[\\\\\]]]]^^^^^^^_____```{{{{{|||}}}}}~~~~~~~``` ▐██▌▐█ ▐██▌ ▐█▌ ▐█ █▌▐█ ▐█ █▌ ▐█ █▌ █▌ ▐█ █▌ █▌ █▌ ▐█ ▐█ ▄▄ ▐█ ▐█ ▐█ █▌▐█ █▌ ▄█▌ ▐█ ▐█▄ ▐▀▀█▄▄▌ ▐█ █▌ █▌ ▀█▌ ▐█ ▐█▀ ▀▀ ▐█ ▐█ █▌ █▌ ▐█ ▐█ ▐██▌ █▌▐██▌ █████ █▌▐█ ▐█ """, """ AAAAAAABBBBBBBCCCCCCCDDDDDDDEEEEEEEFFFFFFFGGGGGGGHHHHHHHIIIIJJJJJJJ ▄███▄ ▐████▄ ▄███▄ ▐████▄ ▐█████▌▐█████▌ ▄███▄ ▐█ █▌ ██▌ █▌ ▐█ █▌▐█ █▌▐█ ▐█ █▌▐█ ▐█ ▐█ ▐█ █▌ ▐█ █▌ ▐█████▌▐█████ ▐█ ▐█ █▌▐████ ▐████ ▐█ ▐█████▌ ▐█ █▌ ▐█ █▌▐█ █▌▐█ ▐█ █▌▐█ ▐█ ▐█ ██▌▐█ █▌ ▐█ █▌ ▐█ █▌▐█ █▌▐█ ▐█ █▌▐█ ▐█ ▐█ █▌▐█ █▌ ▐█ ▐█ █▌ ▐█ █▌▐████▀ ▀███▀ ▐████▀ ▐█████▌▐█ ▀███▀ ▐█ █▌ ██▌ ▀███▀ """, """ KKKKKKKLLLLLLLMMMMMMMMNNNNNNNOOOOOOOPPPPPPPQQQQQQQRRRRRRRSSSSSSS ▐█ █▌▐█ ▄█▌▐█▄ ▐██ █▌ ▄███▄ ▐████▄ ▄███▄ ▐████▄ ▄███▄ ▐█ █▌ ▐█ ▐█ ▐▌ █▌▐██▌ █▌▐█ █▌▐█ █▌▐█ █▌▐█ █▌▐█ ▐█▄█▌ ▐█ ▐█ ▐▌ █▌▐█▐█ █▌▐█ █▌▐████▀ ▐█ █▌▐█████ ▀███▄ ▐█▀█▌ ▐█ ▐█ █▌▐█ █▌█▌▐█ █▌▐█ ▐█ █▌▐█ █▌ █▌ ▐█ █▌ ▐█ ▐█ █▌▐█ ▐██▌▐█ █▌▐█ ▐█ █▌█▌▐█ █▌ █▌ ▐█ █▌▐█████▌▐█ █▌▐█ ██▌ ▀███▀ ▐█ ▀███▀ ▐█ █▌ ▀███▀ ▀▀ """, """ TTTTTTTUUUUUUUVVVVVVVWWWWWWWWXXXXXXXYYYYYYYZZZZZZZ █████▌▐█ █▌▐█ █▌▐█ █▌▐█ █▌ █▌ █▌▐█████▌ █▌ ▐█ █▌ █▌ ▐█ ▐█ █▌ ▐█ █▌ ▐█ ▐█ █▌ █▌ ▐█ █▌ ▐█ █▌ ▐█ █▌ ▐█▌ ▐██ █▌ █▌ ▐█ █▌ ███ ▐█ ▐▌ █▌ ███ █▌ █▌ █▌ ▐█ █▌ ▐█▌ ▐█ ▐▌ █▌ █▌ ▐█ █▌ █▌ █▌ ▀███▀ █ ▀█▌▐█▀ ▐█ █▌ █▌ ▐█████▌ """, """ aaaaaaabbbbbbbcccccccdddddddeeeeeeefffffggggggghhhhhhhiiijjjj ▐█ █▌ ▄█▌ ▐█ █▌ █▌ ▐█ █▌ ▐█ ▐█ ▄███▄ ▐████▄ ▄███▄ ▄████▌ ▄███▄ ▐███ ▄███▄ ▐████▄ ▐█▌ ▐█▌ ▄▄▄█▌▐█ █▌▐█ ▐█ █▌▐█▄▄▄█▌ ▐█ ▐█ █▌▐█ █▌ █▌ █▌ ▐█▀▀▀█▌▐█ █▌▐█ ▐█ █▌▐█▀▀▀ ▐█ ▐█▄▄▄█▌▐█ █▌ █▌ █▌ ▀████▌▐████▀ ▀███▀ ▀████▌ ▀███▀ ▐█ ▀▀▀█▌▐█ █▌ █▌ █▌ ▀███▀ ▐██ """, """ kkkkkkkllllmmmmmmmmnnnnnnnooooooopppppppqqqqqqqrrrrrrsssssss ▐█ ██ ▐█ ▐█ ▐█ ▄█▌ ▐█ ▄█▌▐█▄ ▐████▄ ▄███▄ ▐████▄ ▄████▌ ▄███▌ ▄███▄ ▐█▄█▀ ▐█ ▐█ ▐▌ █▌▐█ █▌▐█ █▌▐█ █▌▐█ █▌▐█ ▐█▄▄▄ ▐█▀▀█▄ ▐█ ▐█ ▐▌ █▌▐█ █▌▐█ █▌▐█ █▌▐█ █▌▐█ ▀▀▀█▌ ▐█ █▌ ▐█▌▐█ █▌▐█ █▌ ▀███▀ ▐████▀ ▀████▌▐█ ▀███▀ ▐█ █▌ """, """ tttttuuuuuuuvvvvvvvwwwwwwwwxxxxxxxyyyyyyyzzzzzzz █▌ █▌ ███▌▐█ █▌▐█ █▌▐█ █▌▐█ █▌▐█ █▌▐█████▌ █▌ ▐█ █▌ █▌ ▐█ ▐█ █▌ ▀█▄█▀ ▐█ █▌ ▄█▀ █▌ ▐█ █▌ ███ ▐█ ▐▌ █▌ ▄█▀█▄ ▐█▄▄▄█▌ ▄█▀ █▌ ▀███▀ ▐█▌ ▀█▌▐█▀ ▐█ █▌ ▀▀▀█▌▐█████▌ ▀███▀ """, ) if __name__ == "__main__": all_ascii = frozenset(chr(x) for x in range(32, 127)) print("Available Fonts: (U) = UTF-8 required") print("----------------") for n, font_cls in get_all_fonts(): f = font_cls() u = "" if f.utf8_required: u = "(U)" print(f"{n:<20} {u:>3} ", end=" ") chars = f.characters() font_chars = frozenset(chars) if font_chars == all_ascii: print("Full ASCII") elif font_chars & all_ascii == all_ascii: print(f"Full ASCII + {''.join(font_chars^all_ascii)!r}") else: print(f"Characters: {chars!r}")