craftbeerpi4-pione/venv/lib/python3.8/site-packages/pyfiglet/__init__.py

957 lines
31 KiB
Python
Raw Normal View History

#!/usr/bin/env python
#-*- encoding: utf-8 -*-
"""
Python FIGlet adaption
"""
from __future__ import print_function, unicode_literals
import os
import pkg_resources
import re
import shutil
import sys
import zipfile
from optparse import OptionParser
from .version import __version__
__author__ = 'Peter Waller <p@pwaller.net>'
__copyright__ = """
The MIT License (MIT)
Copyright © 2007-2018
Christopher Jones <cjones@insub.org>
Stefano Rivera <stefano@rivera.za.net>
Peter Waller <p@pwaller.net>
And various contributors (see git history).
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the Software), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
"""
DEFAULT_FONT = 'standard'
COLOR_CODES = {'BLACK': 30, 'RED': 31, 'GREEN': 32, 'YELLOW': 33, 'BLUE': 34, 'MAGENTA': 35, 'CYAN': 36, 'LIGHT_GRAY': 37,
'DEFAULT': 39, 'DARK_GRAY': 90, 'LIGHT_RED': 91, 'LIGHT_GREEN': 92, 'LIGHT_YELLOW': 93, 'LIGHT_BLUE': 94,
'LIGHT_MAGENTA': 95, 'LIGHT_CYAN': 96, 'WHITE': 97, 'RESET': 0
}
RESET_COLORS = b'\033[0m'
if sys.platform == 'win32':
SHARED_DIRECTORY = os.path.join(os.environ["APPDATA"], "pyfiglet")
else:
SHARED_DIRECTORY = '/usr/local/share/pyfiglet/'
def figlet_format(text, font=DEFAULT_FONT, **kwargs):
fig = Figlet(font, **kwargs)
return fig.renderText(text)
def print_figlet(text, font=DEFAULT_FONT, colors=":", **kwargs):
ansiColors = parse_color(colors)
if ansiColors:
sys.stdout.write(ansiColors)
print(figlet_format(text, font, **kwargs))
if ansiColors:
sys.stdout.write(RESET_COLORS.decode('UTF-8', 'replace'))
sys.stdout.flush()
class FigletError(Exception):
def __init__(self, error):
self.error = error
def __str__(self):
return self.error
class CharNotPrinted(FigletError):
"""
Raised when the width is not sufficient to print a character
"""
class FontNotFound(FigletError):
"""
Raised when a font can't be located
"""
class FontError(FigletError):
"""
Raised when there is a problem parsing a font file
"""
class InvalidColor(FigletError):
"""
Raised when the color passed is invalid
"""
class FigletFont(object):
"""
This class represents the currently loaded font, including
meta-data about how it should be displayed by default
"""
reMagicNumber = re.compile(r'^[tf]lf2.')
reEndMarker = re.compile(r'(.)\s*$')
def __init__(self, font=DEFAULT_FONT):
self.font = font
self.comment = ''
self.chars = {}
self.width = {}
self.data = self.preloadFont(font)
self.loadFont()
@classmethod
def preloadFont(cls, font):
"""
Load font data if exist
"""
for extension in ('tlf', 'flf'):
fn = '%s.%s' % (font, extension)
if pkg_resources.resource_exists('pyfiglet.fonts', fn):
data = pkg_resources.resource_string('pyfiglet.fonts', fn)
data = data.decode('UTF-8', 'replace')
return data
else:
for location in ("./", SHARED_DIRECTORY):
full_name = os.path.join(location, fn)
if os.path.isfile(full_name):
with open(full_name, 'rb') as f:
return f.read().decode('UTF-8', 'replace')
else:
raise FontNotFound(font)
@classmethod
def isValidFont(cls, font):
if not font.endswith(('.flf', '.tlf')):
return False
f = None
full_file = os.path.join(SHARED_DIRECTORY, font)
if os.path.isfile(font):
f = open(font, 'rb')
elif os.path.isfile(full_file):
f = open(full_file, 'rb')
else:
f = pkg_resources.resource_stream('pyfiglet.fonts', font)
header = f.readline().decode('UTF-8', 'replace')
f.close()
return cls.reMagicNumber.search(header)
@classmethod
def getFonts(cls):
all_files = pkg_resources.resource_listdir('pyfiglet', 'fonts')
if os.path.isdir(SHARED_DIRECTORY):
all_files += os.listdir(SHARED_DIRECTORY)
return [font.rsplit('.', 2)[0] for font
in all_files
if cls.isValidFont(font)]
@classmethod
def infoFont(cls, font, short=False):
"""
Get informations of font
"""
data = FigletFont.preloadFont(font)
infos = []
reStartMarker = re.compile(r"""
^(FONT|COMMENT|FONTNAME_REGISTRY|FAMILY_NAME|FOUNDRY|WEIGHT_NAME|
SETWIDTH_NAME|SLANT|ADD_STYLE_NAME|PIXEL_SIZE|POINT_SIZE|
RESOLUTION_X|RESOLUTION_Y|SPACING|AVERAGE_WIDTH|COMMENT|
FONT_DESCENT|FONT_ASCENT|CAP_HEIGHT|X_HEIGHT|FACE_NAME|FULL_NAME|
COPYRIGHT|_DEC_|DEFAULT_CHAR|NOTICE|RELATIVE_).*""", re.VERBOSE)
reEndMarker = re.compile(r'^.*[@#$]$')
for line in data.splitlines()[0:100]:
if (cls.reMagicNumber.search(line) is None
and reStartMarker.search(line) is None
and reEndMarker.search(line) is None):
infos.append(line)
return '\n'.join(infos) if not short else infos[0]
@staticmethod
def installFonts(file_name):
"""
Install the specified font file to this system.
"""
if isinstance(pkg_resources.get_provider('pyfiglet'), pkg_resources.ZipProvider):
# Figlet is installed using a zipped resource - don't try to upload to it.
location = SHARED_DIRECTORY
else:
# Figlet looks like a standard directory - so lets use that to install new fonts.
location = pkg_resources.resource_filename('pyfiglet', 'fonts')
print("Installing {} to {}".format(file_name, location))
# Make sure the required destination directory exists
if not os.path.exists(location):
os.makedirs(location)
# Copy the font definitions - unpacking any zip files as needed.
if os.path.splitext(file_name)[1].lower() == ".zip":
# Ignore any structure inside the ZIP file.
with zipfile.ZipFile(file_name) as zip_file:
for font in zip_file.namelist():
font_file = os.path.basename(font)
if not font_file:
continue
with zip_file.open(font) as src:
with open(os.path.join(location, font_file), "wb") as dest:
shutil.copyfileobj(src, dest)
else:
shutil.copy(file_name, location)
def loadFont(self):
"""
Parse loaded font data for the rendering engine to consume
"""
try:
# Parse first line of file, the header
data = self.data.splitlines()
header = data.pop(0)
if self.reMagicNumber.search(header) is None:
raise FontError('%s is not a valid figlet font' % self.font)
header = self.reMagicNumber.sub('', header)
header = header.split()
if len(header) < 6:
raise FontError('malformed header for %s' % self.font)
hardBlank = header[0]
height, baseLine, maxLength, oldLayout, commentLines = map(
int, header[1:6])
printDirection = fullLayout = None
# these are all optional for backwards compat
if len(header) > 6:
printDirection = int(header[6])
if len(header) > 7:
fullLayout = int(header[7])
# if the new layout style isn't available,
# convert old layout style. backwards compatability
if fullLayout is None:
if oldLayout == 0:
fullLayout = 64
elif oldLayout < 0:
fullLayout = 0
else:
fullLayout = (oldLayout & 31) | 128
# Some header information is stored for later, the rendering
# engine needs to know this stuff.
self.height = height
self.hardBlank = hardBlank
self.printDirection = printDirection
self.smushMode = fullLayout
# Strip out comment lines
for i in range(0, commentLines):
self.comment += data.pop(0)
def __char(data):
"""
Function loads one character in the internal array from font
file content
"""
end = None
width = 0
chars = []
for j in range(0, height):
line = data.pop(0)
if end is None:
end = self.reEndMarker.search(line).group(1)
end = re.compile(re.escape(end) + r'{1,2}$')
line = end.sub('', line)
if len(line) > width:
width = len(line)
chars.append(line)
return width, chars
# Load ASCII standard character set (32 - 127)
for i in range(32, 127):
width, letter = __char(data)
if ''.join(letter) != '':
self.chars[i] = letter
self.width[i] = width
# Load ASCII extended character set
while data:
line = data.pop(0).strip()
i = line.split(' ', 1)[0]
if (i == ''):
continue
hex_match = re.search('^0x', i, re.IGNORECASE)
if hex_match is not None:
i = int(i, 16)
width, letter = __char(data)
if ''.join(letter) != '':
self.chars[i] = letter
self.width[i] = width
except Exception as e:
raise FontError('problem parsing %s font: %s' % (self.font, e))
def __str__(self):
return '<FigletFont object: %s>' % self.font
unicode_string = type(''.encode('ascii').decode('ascii'))
class FigletString(unicode_string):
"""
Rendered figlet font
"""
# translation map for reversing ascii art / -> \, etc.
__reverse_map__ = (
'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f'
'\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f'
' !"#$%&\')(*+,-.\\'
'0123456789:;>=<?'
'@ABCDEFGHIJKLMNO'
'PQRSTUVWXYZ]/[^_'
'`abcdefghijklmno'
'pqrstuvwxyz}|{~\x7f'
'\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f'
'\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f'
'\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf'
'\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf'
'\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf'
'\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf'
'\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef'
'\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff')
# translation map for flipping ascii art ^ -> v, etc.
__flip_map__ = (
'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f'
'\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f'
' !"#$%&\'()*+,-.\\'
'0123456789:;<=>?'
'@VBCDEFGHIJKLWNO'
'bQbSTUAMXYZ[/]v-'
'`aPcdefghijklwno'
'pqrstu^mxyz{|}~\x7f'
'\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f'
'\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f'
'\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf'
'\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf'
'\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf'
'\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf'
'\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef'
'\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff')
def reverse(self):
out = []
for row in self.splitlines():
out.append(row.translate(self.__reverse_map__)[::-1])
return self.newFromList(out)
def flip(self):
out = []
for row in self.splitlines()[::-1]:
out.append(row.translate(self.__flip_map__))
return self.newFromList(out)
def newFromList(self, list):
return FigletString('\n'.join(list) + '\n')
class FigletRenderingEngine(object):
"""
This class handles the rendering of a FigletFont,
including smushing/kerning/justification/direction
"""
def __init__(self, base=None):
self.base = base
def render(self, text):
"""
Render an ASCII text string in figlet
"""
builder = FigletBuilder(text,
self.base.Font,
self.base.direction,
self.base.width,
self.base.justify)
while builder.isNotFinished():
builder.addCharToProduct()
builder.goToNextChar()
return builder.returnProduct()
class FigletProduct(object):
"""
This class stores the internal build part of
the ascii output string
"""
def __init__(self):
self.queue = list()
self.buffer_string = ""
def append(self, buffer):
self.queue.append(buffer)
def getString(self):
return FigletString(self.buffer_string)
class FigletBuilder(object):
"""
Represent the internals of the build process
"""
def __init__(self, text, font, direction, width, justify):
self.text = list(map(ord, list(text)))
self.direction = direction
self.width = width
self.font = font
self.justify = justify
self.iterator = 0
self.maxSmush = 0
self.newBlankRegistered = False
self.curCharWidth = 0
self.prevCharWidth = 0
self.currentTotalWidth = 0
self.blankMarkers = list()
self.product = FigletProduct()
self.buffer = ['' for i in range(self.font.height)]
# constants.. lifted from figlet222
self.SM_EQUAL = 1 # smush equal chars (not hardblanks)
self.SM_LOWLINE = 2 # smush _ with any char in hierarchy
self.SM_HIERARCHY = 4 # hierarchy: |, /\, [], {}, (), <>
self.SM_PAIR = 8 # hierarchy: [ + ] -> |, { + } -> |, ( + ) -> |
self.SM_BIGX = 16 # / + \ -> X, > + < -> X
self.SM_HARDBLANK = 32 # hardblank + hardblank -> hardblank
self.SM_KERN = 64
self.SM_SMUSH = 128
# builder interface
def addCharToProduct(self):
curChar = self.getCurChar()
# if the character is a newline, we flush the buffer
if self.text[self.iterator] == ord("\n"):
self.blankMarkers.append(([row for row in self.buffer], self.iterator))
self.handleNewLine()
return None
if curChar is None:
return
if self.width < self.getCurWidth():
raise CharNotPrinted("Width is not enough to print this character")
self.curCharWidth = self.getCurWidth()
self.maxSmush = self.currentSmushAmount(curChar)
self.currentTotalWidth = len(self.buffer[0]) + self.curCharWidth - self.maxSmush
if self.text[self.iterator] == ord(' '):
self.blankMarkers.append(([row for row in self.buffer], self.iterator))
if self.text[self.iterator] == ord('\n'):
self.blankMarkers.append(([row for row in self.buffer], self.iterator))
self.handleNewLine()
if (self.currentTotalWidth >= self.width):
self.handleNewLine()
else:
for row in range(0, self.font.height):
self.addCurCharRowToBufferRow(curChar, row)
self.prevCharWidth = self.curCharWidth
def goToNextChar(self):
self.iterator += 1
def returnProduct(self):
"""
Returns the output string created by formatProduct
"""
if self.buffer[0] != '':
self.flushLastBuffer()
self.formatProduct()
return self.product.getString()
def isNotFinished(self):
ret = self.iterator < len(self.text)
return ret
# private
def flushLastBuffer(self):
self.product.append(self.buffer)
def formatProduct(self):
"""
This create the output string representation from
the internal representation of the product
"""
string_acc = ''
for buffer in self.product.queue:
buffer = self.justifyString(self.justify, buffer)
string_acc += self.replaceHardblanks(buffer)
self.product.buffer_string = string_acc
def getCharAt(self, i):
if i < 0 or i >= len(list(self.text)):
return None
c = self.text[i]
if c not in self.font.chars:
return None
else:
return self.font.chars[c]
def getCharWidthAt(self, i):
if i < 0 or i >= len(self.text):
return None
c = self.text[i]
if c not in self.font.chars:
return None
else:
return self.font.width[c]
def getCurChar(self):
return self.getCharAt(self.iterator)
def getCurWidth(self):
return self.getCharWidthAt(self.iterator)
def getLeftSmushedChar(self, i, addLeft):
idx = len(addLeft) - self.maxSmush + i
if idx >= 0 and idx < len(addLeft):
left = addLeft[idx]
else:
left = ''
return left, idx
def currentSmushAmount(self, curChar):
return self.smushAmount(self.buffer, curChar)
def updateSmushedCharInLeftBuffer(self, addLeft, idx, smushed):
l = list(addLeft)
if idx < 0 or idx > len(l):
return addLeft
l[idx] = smushed
addLeft = ''.join(l)
return addLeft
def smushRow(self, curChar, row):
addLeft = self.buffer[row]
addRight = curChar[row]
if self.direction == 'right-to-left':
addLeft, addRight = addRight, addLeft
for i in range(0, self.maxSmush):
left, idx = self.getLeftSmushedChar(i, addLeft)
right = addRight[i]
smushed = self.smushChars(left=left, right=right)
addLeft = self.updateSmushedCharInLeftBuffer(addLeft, idx, smushed)
return addLeft, addRight
def addCurCharRowToBufferRow(self, curChar, row):
addLeft, addRight = self.smushRow(curChar, row)
self.buffer[row] = addLeft + addRight[self.maxSmush:]
def cutBufferCommon(self):
self.currentTotalWidth = len(self.buffer[0])
self.buffer = ['' for i in range(self.font.height)]
self.blankMarkers = list()
self.prevCharWidth = 0
curChar = self.getCurChar()
if curChar is None:
return
self.maxSmush = self.currentSmushAmount(curChar)
def cutBufferAtLastBlank(self, saved_buffer, saved_iterator):
self.product.append(saved_buffer)
self.iterator = saved_iterator
self.cutBufferCommon()
def cutBufferAtLastChar(self):
self.product.append(self.buffer)
self.iterator -= 1
self.cutBufferCommon()
def blankExist(self, last_blank):
return last_blank != -1
def getLastBlank(self):
try:
saved_buffer, saved_iterator = self.blankMarkers.pop()
except IndexError:
return -1,-1
return (saved_buffer, saved_iterator)
def handleNewLine(self):
saved_buffer, saved_iterator = self.getLastBlank()
if self.blankExist(saved_iterator):
self.cutBufferAtLastBlank(saved_buffer, saved_iterator)
else:
self.cutBufferAtLastChar()
def justifyString(self, justify, buffer):
if justify == 'right':
for row in range(0, self.font.height):
buffer[row] = (
' ' * (self.width - len(buffer[row]) - 1)
) + buffer[row]
elif justify == 'center':
for row in range(0, self.font.height):
buffer[row] = (
' ' * int((self.width - len(buffer[row])) / 2)
) + buffer[row]
return buffer
def replaceHardblanks(self, buffer):
string = '\n'.join(buffer) + '\n'
string = string.replace(self.font.hardBlank, ' ')
return string
def smushAmount(self, buffer=[], curChar=[]):
"""
Calculate the amount of smushing we can do between this char and the
last If this is the first char it will throw a series of exceptions
which are caught and cause appropriate values to be set for later.
This differs from C figlet which will just get bogus values from
memory and then discard them after.
"""
if (self.font.smushMode & (self.SM_SMUSH | self.SM_KERN)) == 0:
return 0
maxSmush = self.curCharWidth
for row in range(0, self.font.height):
lineLeft = buffer[row]
lineRight = curChar[row]
if self.direction == 'right-to-left':
lineLeft, lineRight = lineRight, lineLeft
linebd = len(lineLeft.rstrip()) - 1
if linebd < 0:
linebd = 0
if linebd < len(lineLeft):
ch1 = lineLeft[linebd]
else:
linebd = 0
ch1 = ''
charbd = len(lineRight) - len(lineRight.lstrip())
if charbd < len(lineRight):
ch2 = lineRight[charbd]
else:
charbd = len(lineRight)
ch2 = ''
amt = charbd + len(lineLeft) - 1 - linebd
if ch1 == '' or ch1 == ' ':
amt += 1
elif (ch2 != ''
and self.smushChars(left=ch1, right=ch2) is not None):
amt += 1
if amt < maxSmush:
maxSmush = amt
return maxSmush
def smushChars(self, left='', right=''):
"""
Given 2 characters which represent the edges rendered figlet
fonts where they would touch, see if they can be smushed together.
Returns None if this cannot or should not be done.
"""
if left.isspace() is True:
return right
if right.isspace() is True:
return left
# Disallows overlapping if previous or current char has a width of 1 or
# zero
if (self.prevCharWidth < 2) or (self.curCharWidth < 2):
return
# kerning only
if (self.font.smushMode & self.SM_SMUSH) == 0:
return
# smushing by universal overlapping
if (self.font.smushMode & 63) == 0:
# Ensure preference to visiable characters.
if left == self.font.hardBlank:
return right
if right == self.font.hardBlank:
return left
# Ensures that the dominant (foreground)
# fig-character for overlapping is the latter in the
# user's text, not necessarily the rightmost character.
if self.direction == 'right-to-left':
return left
else:
return right
if self.font.smushMode & self.SM_HARDBLANK:
if (left == self.font.hardBlank
and right == self.font.hardBlank):
return left
if (left == self.font.hardBlank
or right == self.font.hardBlank):
return
if self.font.smushMode & self.SM_EQUAL:
if left == right:
return left
smushes = ()
if self.font.smushMode & self.SM_LOWLINE:
smushes += (('_', r'|/\[]{}()<>'),)
if self.font.smushMode & self.SM_HIERARCHY:
smushes += (
('|', r'|/\[]{}()<>'),
(r'\/', '[]{}()<>'),
('[]', '{}()<>'),
('{}', '()<>'),
('()', '<>'),
)
for a, b in smushes:
if left in a and right in b:
return right
if right in a and left in b:
return left
if self.font.smushMode & self.SM_PAIR:
for pair in [left+right, right+left]:
if pair in ['[]', '{}', '()']:
return '|'
if self.font.smushMode & self.SM_BIGX:
if (left == '/') and (right == '\\'):
return '|'
if (right == '/') and (left == '\\'):
return 'Y'
if (left == '>') and (right == '<'):
return 'X'
return
class Figlet(object):
"""
Main figlet class.
"""
def __init__(self, font=DEFAULT_FONT, direction='auto', justify='auto',
width=80):
self.font = font
self._direction = direction
self._justify = justify
self.width = width
self.setFont()
self.engine = FigletRenderingEngine(base=self)
def setFont(self, **kwargs):
if 'font' in kwargs:
self.font = kwargs['font']
self.Font = FigletFont(font=self.font)
def getDirection(self):
if self._direction == 'auto':
direction = self.Font.printDirection
if direction == 0:
return 'left-to-right'
elif direction == 1:
return 'right-to-left'
else:
return 'left-to-right'
else:
return self._direction
direction = property(getDirection)
def getJustify(self):
if self._justify == 'auto':
if self.direction == 'left-to-right':
return 'left'
elif self.direction == 'right-to-left':
return 'right'
else:
return self._justify
justify = property(getJustify)
def renderText(self, text):
# wrapper method to engine
return self.engine.render(text)
def getFonts(self):
return self.Font.getFonts()
def color_to_ansi(color, isBackground):
if not color:
return ''
if color.count(';') > 0 and color.count(';') != 2:
raise InvalidColor('Specified color \'{}\' not a valid color in R;G;B format')
elif color.count(';') == 0 and color not in COLOR_CODES:
raise InvalidColor('Specified color \'{}\' not found in ANSI COLOR_CODES list'.format(color))
if color in COLOR_CODES:
ansiCode = COLOR_CODES[color]
if isBackground:
ansiCode += 10
else:
ansiCode = 48 if isBackground else 38
ansiCode = '{};2;{}'.format(ansiCode, color)
return '\033[{}m'.format(ansiCode)
def parse_color(color):
foreground, _, background = color.partition(":")
ansiForeground = color_to_ansi(foreground, isBackground=False)
ansiBackground = color_to_ansi(background, isBackground=True)
return ansiForeground + ansiBackground
def main():
parser = OptionParser(version=__version__,
usage='%prog [options] [text..]')
parser.add_option('-f', '--font', default=DEFAULT_FONT,
help='font to render with (default: %default)',
metavar='FONT')
parser.add_option('-D', '--direction', type='choice',
choices=('auto', 'left-to-right', 'right-to-left'),
default='auto', metavar='DIRECTION',
help='set direction text will be formatted in '
'(default: %default)')
parser.add_option('-j', '--justify', type='choice',
choices=('auto', 'left', 'center', 'right'),
default='auto', metavar='SIDE',
help='set justification, defaults to print direction')
parser.add_option('-w', '--width', type='int', default=80, metavar='COLS',
help='set terminal width for wrapping/justification '
'(default: %default)')
parser.add_option('-r', '--reverse', action='store_true', default=False,
help='shows mirror image of output text')
parser.add_option('-F', '--flip', action='store_true', default=False,
help='flips rendered output text over')
parser.add_option('-l', '--list_fonts', action='store_true', default=False,
help='show installed fonts list')
parser.add_option('-i', '--info_font', action='store_true', default=False,
help='show font\'s information, use with -f FONT')
parser.add_option('-L', '--load', default=None,
help='load and install the specified font definition')
parser.add_option('-c', '--color', default=':',
help='''prints text with passed foreground color,
--color=foreground:background
--color=:background\t\t\t # only background
--color=foreground | foreground:\t # only foreground
--color=list\t\t\t # list all colors
COLOR = list[COLOR] | [0-255];[0-255];[0-255] (RGB)''')
opts, args = parser.parse_args()
if opts.list_fonts:
print('\n'.join(sorted(FigletFont.getFonts())))
exit(0)
if opts.color == 'list':
print('[0-255];[0-255];[0-255] # RGB\n' + '\n'.join((sorted(COLOR_CODES.keys()))))
exit(0)
if opts.info_font:
print(FigletFont.infoFont(opts.font))
exit(0)
if opts.load:
FigletFont.installFonts(opts.load)
exit(0)
if len(args) == 0:
parser.print_help()
return 1
if sys.version_info < (3,):
args = [arg.decode('UTF-8') for arg in args]
text = ' '.join(args)
f = Figlet(
font=opts.font, direction=opts.direction,
justify=opts.justify, width=opts.width,
)
r = f.renderText(text)
if opts.reverse:
r = r.reverse()
if opts.flip:
r = r.flip()
if sys.version_info > (3,):
# Set stdout to binary mode
sys.stdout = sys.stdout.detach()
ansiColors = parse_color(opts.color)
if ansiColors:
sys.stdout.write(ansiColors.encode('UTF-8'))
sys.stdout.write(r.encode('UTF-8'))
sys.stdout.write(b'\n')
if ansiColors:
sys.stdout.write(RESET_COLORS)
return 0
if __name__ == '__main__':
sys.exit(main())