Fabulous

Version:0.3.0
Founder:Justine Alexandra Roberts Tunney
Copyright:Copyright 2016 The Fabulous Authors. All rights reserved.
License:Apache 2.0 / OFL
Support:Python 2.6, 2.7, 3.3, 3.4, 3.5, and pypy
Source:github.com/jart/fabulous

Fabulous is a Python library (and command line tools) designed to make the output of terminal applications look fabulous. Fabulous allows you to print colors, images, and stylized text to the console (without curses.) Fabulous also offers features to improve the usability of Python’s standard logging system.

Installation

The following prerequisites should be installed, but they are not mandatory. They help Fabulous run faster and make the full feature set available:

sudo apt-get install gcc python-imaging

Fabulous can be installed from CheeseShop:

sudo pip install fabulous

Fabulous can also be installed manually from the source archive:

wget https://github.com/jart/fabulous/releases/download/0.3.0/fabulous-0.3.0.tar.gz
tar -xvzf fabulous-0.3.0.tar.gz
cd fabulous-0.3.0
sudo python setup.py install

Once installed, run the demo:

fabulous-demo
_images/fabulous-demo.png

Examples

Colors

4-bit colors and styles are standard and work almost everywhere. They are useful in helping make your program output easier to read:

from fabulous.color import bold, magenta, highlight_red

print bold(magenta('hello world'))

print highlight_red('DANGER WILL ROBINSON!')

print bold('hello') + ' ' + magenta(' world')

assert len(bold('test')) == 4

8-bit color works in most modern terminals, such as gnome-terminal and Terminal.app:

from fabulous import fg256, bg256
print fg256('#F0F', 'hello world')
print fg256('magenta', 'hello world')

Fancy Text

This is something neat you can use when you program starts up to display its name with style:

from fabulous import text
print text.Text("Fabulous!", color='#0099ff', shadow=True, skew=5)

Images

Fabulous lets you print images, which is more fun than useful. Fabulous’ unique method of printing images really shines when used with semi-transparent PNG files. When blending backgrounds, Fabulous assumes by default that your terminal has a black background. Don’t worry if your image is huge, it’ll be resized by default to fit your terminal:

from fabulous import utils, image
print image.Image("balls.png")

# adjust for a white background
utils.term.bgcolor = 'white'
print image.Image("balls.png")

Image printing may perform slowly depending on whether or not Fabulous is able to compile ~/.xterm256.so on the fly. This is a tiny library that makes color quantization go much faster. The pure Python version of the algorithm is really slow because it’s implemented as a brute force nearest neighbor over Euclidean distance search. Although an O(1) version of this algorithm exists with slightly less correctness. Your humble author simply hasn’t had the time to implement it in this library.

If you like this image printing feature, then please check out hiptext which is a C++ program written by the same author as Fabulous. It offers a much richer version of this same functionality. It can even play videos in the terminal. Also be sure to check out rickrollrc.

Commands

fabulous-text

Usage: fabulous-text [options]

Options:
  -h, --help            show this help message and exit
  -l, --list            List available fonts
  -S SKEW, --skew=SKEW  Apply skew effect (measured in pixels) to make it look
                        extra cool.  For example, Fabulous' logo logo is
                        skewed by 5 pixels.  Default: none
  -C COLOR, --color=COLOR
                        Color of your text.  This can be specified as you
                        would using HTML/CSS.  Default: #0099ff
  -B TERM_COLOR, --term-color=TERM_COLOR
                        If you terminal background isn't black, please change
                        this value to the proper background so semi-
                        transparent pixels will blend properly.
  -F FONT, --font=FONT  Name of font file, or absolute path to one. Use the
                        --list flag to see what fonts are available. Fabulous
                        bundles the NotoSans-Bold and NotoEmoji-Regular fonts,
                        which are guaranteed to work. Default: NotoSans-Bold
  -Z FSIZE, --size=FSIZE
                        Size of font in points.  Default: 23
  -s, --shadow          Size of font in points.  Default: False

fabulous-image

Usage: fabulous-image [options]

Options:
  -h, --help            show this help message and exit
  -w WIDTH, --width=WIDTH
                        Width of printed image in characters.  Default: none

fabulous-demo

Displays a demo showing what Fabulous can do.

fabulous-gotham

The fabulous-gotham command is a gothic poetry generator. It is a gimmick feature that uses a simple mad lib algorithm. It has no concept of meter or rhyme. Users wanting a proper poetry generator should consider poemy2 which uses markov chains and isledict. It’s also written by the same author as Fabulous.

fabulous-rotatingcube

The fabulous-rotatingcube command is another gimmick feature that animates a wireframe rotating cube in the terminal. It runs until you hit Ctrl+C.

Library

fabulous.color

The color module provides an object-oriented abstraction for stylized text inside the terminal. This includes things like bold text, blinking text, 4-bit ANSI colors and 8-bit xterm256 colors.

class fabulous.color.ColorString(*items)

Abstract base class for stylized string-like objects.

Subclasses make it possible to compose stylized text:

>>> str(red("hello"))
'\x1b[31mhello\x1b[39m'
>>> str(bold(red("hello")))
'\x1b[1m\x1b[31mhello\x1b[39m\x1b[22m'
>>> str(plain("hello ", bold("world")))
'hello \x1b[1mworld\x1b[22m'

These objects also provide string length without taking into consideration the ANSI escape codes:

>>> len(red("hello"))
5
>>> len(str(red("hello")))
15
>>> len(bold(red("hello")))
5
>>> len(bold("hello ", red("world")))
11
as_utf8

A more readable way to say unicode(color).encode('utf8')

class fabulous.color.ColorString256(color, *items)

Base class for 256-color stylized string-like objects.

See the fg256, bg256, highlight256, and complement256 classes for more information.

class fabulous.color.bg256(color, *items)

xterm256 background color wrapper

This class creates a string-like object that has an xterm256 color. The color is specified as a CSS color code, which is automatically quantized to the available set of xterm colors.

These colors are more dependable than the 4-bit colors, because 8-bit colors don’t get changed by the terminal theme. They will consistently be the requested color.

However it is worth noting that in Terminal.app on Mac OS, 8-bit background colors are ever so slightly different than their foreground equivalent. Therefore Terminal.app has effectively 512 colors.

Example usage:

from fabulous import bg256, plain
print bg256('#F00', 'i have a red background!')
print bg256('#FF0000', 'i have a red background!')
print bg256('magenta', 'i have a', ' magenta background!')
print plain('hello ', bg256('magenta', 'world'))

The ANSI escape codes look as follows:

>>> str(bg256('red', 'hello'))
'\x1b[48;5;196mhello\x1b[49m'
class fabulous.color.black(*items)

Black foreground text wrapper

This class creates a string-like object containing text with a black foreground.

Example usage:

from fabulous.color import black
print black('i am black!')
print plain('hello ', black('world'))

Text can be made dark grey by using bold:

from fabulous.color import bold, black
print bold(black('i am dark grey!'))

The ANSI escape codes are as follows:

>>> str(black("hello"))
'\x1b[30mhello\x1b[39m'
class fabulous.color.black_bg(*items)

Black background text wrapper

This class creates a string-like object containing text with a black background. On properly configured terminals, this will do nothing.

Example usage:

from fabulous.color import black_bg
print black_bg('i have a black background!')
print plain('hello ', black_bg('world'))

The ANSI escape codes are as follows:

>>> str(black_bg("hello"))
'\x1b[40mhello\x1b[49m'

Blinking text wrapper

This class creates a string-like object containing blinking text. This is supported by SOME terminals, as documented in the terminal support section.

Example usage:

from fabulous.color import blink
print blink('i am underlined!')
print plain('hello ', blink('world'))

The ANSI escape codes are as follows:

>>> str(blink("hello"))
'\x1b[5mhello\x1b[25m'
class fabulous.color.blue(*items)

Blue foreground text wrapper

This class creates a string-like object containing text with a blue foreground.

Example usage:

from fabulous.color import blue
print blue('i am dark blue!')
print plain('hello ', blue('world'))

Text can be made sky blue by using bold:

from fabulous.color import bold, blue
print bold(blue('i am sky blue!'))

The ANSI escape codes are as follows:

>>> str(blue("hello"))
'\x1b[34mhello\x1b[39m'
class fabulous.color.blue_bg(*items)

Blue background text wrapper

This class creates a string-like object containing text with a blue background.

Example usage:

from fabulous.color import blue_bg
print blue_bg('i have a blue background!')
print plain('hello ', blue_bg('world'))

The ANSI escape codes are as follows:

>>> str(blue_bg("hello"))
'\x1b[44mhello\x1b[49m'
class fabulous.color.bold(*items)

Bold text wrapper

This class creates a string-like object containing bold or bright text. It also brightens the foreground and background colors. This is supported by all terminals that support ANSI color codes.

Example usage:

from fabulous.color import bold
print bold('i am bold!')
print plain('hello ', bold('world'))

The ANSI escape codes are as follows:

>>> str(bold("hello"))
'\x1b[1mhello\x1b[22m'
fabulous.color.complement(color)

Calculates polar opposite of color

This isn’t guaranteed to look good >_> (especially with brighter, higher intensity colors.) This will be replaced with a formula that produces better looking colors in the future.

>>> complement('red')
(0, 255, 76)
>>> complement((0, 100, 175))
(175, 101, 0)
class fabulous.color.complement256(color, *items)

Highlighted 8-bit color text

This class composes bold, flip, and bg256. Then it invokes complement() to supply the polar opposite fg256 color.

This looks kind of hideous at the moment. We’re planning on finding a better formula for complementary colors in the future.

class fabulous.color.cyan(*items)

Cyan foreground text wrapper

This class creates a string-like object containing text with a cyan foreground.

Example usage:

from fabulous.color import cyan
print cyan('i am cyan!')
print plain('hello ', cyan('world'))

Text can be made bright cyan by using bold:

from fabulous.color import bold, cyan
print bold(cyan('i am bright cyan!'))

The ANSI escape codes are as follows:

>>> str(cyan("hello"))
'\x1b[36mhello\x1b[39m'
class fabulous.color.cyan_bg(*items)

Cyan background text wrapper

This class creates a string-like object containing text with a cyan background.

Example usage:

from fabulous.color import cyan_bg
print cyan_bg('i have a cyan background!')
print plain('hello ', cyan_bg('world'))

The ANSI escape codes are as follows:

>>> str(cyan_bg("hello"))
'\x1b[46mhello\x1b[49m'
fabulous.color.esc(*codes)

Produces an ANSI escape code string from a list of integers

This is a low level function that is abstracted by the other functions and classes in this module.

class fabulous.color.fg256(color, *items)

xterm256 foreground color wrapper

This class creates a string-like object that has an xterm256 color. The color is specified as a CSS color code, which is automatically quantized to the available set of xterm colors.

These colors are more dependable than the 4-bit colors, because 8-bit colors don’t get changed by the terminal theme. They will consistently be the requested color, which is calculated using a simple math formula.

However it is worth noting that in Terminal.app on Mac OS, 8-bit colors appear to be designed rather than formulaic, so they look much nicer.

Example usage:

from fabulous import fg256, plain
print fg256('#F00', 'i am red!')
print fg256('#FF0000', 'i am red!')
print fg256('magenta', 'i am', ' magenta!')
print plain('hello ', fg256('magenta', 'world'))

The ANSI escape codes look as follows:

>>> str(fg256('red', 'hello'))
'\x1b[38;5;196mhello\x1b[39m'
class fabulous.color.flip(*items)

Flips background and foreground colors

For example:

from fabulous.color import flip, red
print flip(red('hello'))

Is equivalent to the following on a black terminal:

from fabulous.color import black, red_bg
print red_bg(black('hello'))

The ANSI escape codes are as follows:

>>> str(flip("hello"))
'\x1b[7mhello\x1b[27m'
class fabulous.color.green(*items)

Green foreground text wrapper

This class creates a string-like object containing text with a green foreground.

Example usage:

from fabulous.color import green
print green('i am green!')
print plain('hello ', green('world'))

Text can be made bright green by using bold:

from fabulous.color import bold, green
print bold(green('i am bright green!'))

The ANSI escape codes are as follows:

>>> str(green("hello"))
'\x1b[32mhello\x1b[39m'
class fabulous.color.green_bg(*items)

Green background text wrapper

This class creates a string-like object containing text with a green background.

Example usage:

from fabulous.color import green_bg
print green_bg('i have a green background!')
print plain('hello ', green_bg('world'))

The ANSI escape codes are as follows:

>>> str(green_bg("hello"))
'\x1b[42mhello\x1b[49m'
fabulous.color.h1(title, line=u'\u203e')

Prints bold text with line beneath it spanning width of terminal

class fabulous.color.highlight256(color, *items)

Highlighted 8-bit color text

This is equivalent to composing bold, flip, and fg256.

class fabulous.color.highlight_black(*items)

Dark grey highlight text wrapper

This is equivalent to composing bold, flip, and black.

class fabulous.color.highlight_blue(*items)

Blue highlight text wrapper

This is equivalent to composing bold, flip, and blue.

class fabulous.color.highlight_cyan(*items)

Cyan highlight text wrapper

This is equivalent to composing bold, flip, and cyan.

class fabulous.color.highlight_green(*items)

Green highlight text wrapper

This is equivalent to composing bold, flip, and green.

class fabulous.color.highlight_magenta(*items)

Hot pink highlight text wrapper

This is equivalent to composing bold, flip, and magenta.

class fabulous.color.highlight_red(*items)

Red highlight text wrapper

This is equivalent to composing bold, flip, and red.

class fabulous.color.highlight_white(*items)

White highlight text wrapper

This is equivalent to composing bold, flip, and yellow.

class fabulous.color.highlight_yellow(*items)

Yellow highlight text wrapper

This is equivalent to composing bold, flip, and yellow.

class fabulous.color.italic(*items)

Italic text wrapper

This class creates a string-like object containing italic text, which is supported by almost no terminals.

The ANSI escape codes are as follows:

>>> str(italic("hello"))
'\x1b[3mhello\x1b[23m'
class fabulous.color.magenta(*items)

Purple/magenta foreground text wrapper

This class creates a string-like object containing text with a magenta foreground. Although in many terminals, it’s going to look more purple.

Example usage:

from fabulous.color import magenta
print magenta('i am magenta purplish!')
print plain('hello ', magenta('world'))

Text can be made hot pink by using bold:

from fabulous.color import bold, magenta
print bold(magenta('i am hot pink!'))

The ANSI escape codes are as follows:

>>> str(magenta("hello"))
'\x1b[35mhello\x1b[39m'
class fabulous.color.magenta_bg(*items)

Magenta background text wrapper

This class creates a string-like object containing text with a magenta background.

Example usage:

from fabulous.color import magenta_bg
print magenta_bg('i have a magenta background!')
print plain('hello ', magenta_bg('world'))

The ANSI escape codes are as follows:

>>> str(magenta_bg("hello"))
'\x1b[45mhello\x1b[49m'
fabulous.color.parse_color(color)

Turns a color into an (r, g, b) tuple

>>> parse_color('white')
(255, 255, 255)
>>> parse_color('#ff0000')
(255, 0, 0)
>>> parse_color('#f00')
(255, 0, 0)
>>> parse_color((255, 0, 0))
(255, 0, 0)
>>> from fabulous import grapefruit
>>> parse_color(grapefruit.Color((0.0, 1.0, 0.0)))
(0, 255, 0)
class fabulous.color.plain(*items)

Plain text wrapper

This class is useful for concatenating plain strings with ColorString objects. For example:

from fabulous.color import plain
>>> len(plain("hello ", bold("kitty")))
11
class fabulous.color.red(*items)

Red foreground text wrapper

This class creates a string-like object containing text with a red foreground.

Example usage:

from fabulous.color import red
print red('i am red!')
print plain('hello ', red('world'))

Text can be made bright red by using bold:

from fabulous.color import bold, red
print bold(red('i am bright red!'))

The ANSI escape codes are as follows:

>>> str(red("hello"))
'\x1b[31mhello\x1b[39m'
class fabulous.color.red_bg(*items)

Red background text wrapper

This class creates a string-like object containing text with a red background.

Example usage:

from fabulous.color import red_bg
print red_bg('i have a red background!')
print plain('hello ', red_bg('world'))

The ANSI escape codes are as follows:

>>> str(red_bg("hello"))
'\x1b[41mhello\x1b[49m'
fabulous.color.section(title, bar=u'\u203e', strm=<open file '<stdout>', mode 'w' at 0x7f507d148150>)

Helper function for testing demo routines

class fabulous.color.strike(*items)

Strike-through text wrapper

This class creates a string-like object containing strike-through text, which is supported by very few terminals.

Example usage:

from fabulous.color import strike
print strike('i am stricken!')
print plain('hello ', strike('world'))

The ANSI escape codes are as follows:

>>> str(strike("hello"))
'\x1b[9mhello\x1b[29m'
class fabulous.color.underline(*items)

Underline text wrapper

This class creates a string-like object containing underline text. This is supported by SOME terminals, as documented in the terminal support section.

Example usage:

from fabulous.color import underline
print underline('i am underlined!')
print plain('hello ', underline('world'))

The ANSI escape codes are as follows:

>>> str(underline("hello"))
'\x1b[4mhello\x1b[24m'
class fabulous.color.underline2(*items)

Alternative underline text wrapper

See also: underline.

The ANSI escape codes are as follows:

>>> str(underline2("hello"))
'\x1b[21mhello\x1b[24m'
class fabulous.color.white(*items)

White foreground text wrapper

This class creates a string-like object containing text with a light grey foreground.

Example usage:

from fabulous.color import white
print white('i am light grey!')
print plain('hello ', white('world'))

Text can be made true white by using bold:

from fabulous.color import bold, white
print bold(white('i am bold white!'))

The ANSI escape codes are as follows:

>>> str(white("hello"))
'\x1b[37mhello\x1b[39m'
class fabulous.color.white_bg(*items)

White background text wrapper

This class creates a string-like object containing text with a white background.

Example usage:

from fabulous.color import white_bg
print white_bg('i have a white background!')
print plain('hello ', white_bg('world'))

The ANSI escape codes are as follows:

>>> str(white_bg("hello"))
'\x1b[47mhello\x1b[49m'
class fabulous.color.yellow(*items)

Yellow foreground text wrapper

This class creates a string-like object containing text with a “yellow” foreground, which in many terminals is actually going to look more brownish.

Example usage:

from fabulous.color import yellow
print yellow('i am yellow brownish!')
print plain('hello ', yellow('world'))

Text can be made true bright yellow by using bold:

from fabulous.color import bold, yellow
print bold(yellow('i am bright yellow!'))

The ANSI escape codes are as follows:

>>> str(yellow("hello"))
'\x1b[33mhello\x1b[39m'
class fabulous.color.yellow_bg(*items)

Yellow background text wrapper

This class creates a string-like object containing text with a yellow background.

Example usage:

from fabulous.color import yellow_bg
print yellow_bg('i have a yellow background!')
print plain('hello ', yellow_bg('world'))

The ANSI escape codes are as follows:

>>> str(yellow_bg("hello"))
'\x1b[43mhello\x1b[49m'

fabulous.xterm256

The xterm256 module provides support for the 256 colors supported by xterm as well as quantizing 24-bit RGB color to xterm color ids.

Color quantization may perform slowly depending on whether or not Fabulous is able to compile ~/.xterm256.so on the fly. This is a tiny library that makes color quantization go much faster. The pure Python version of the algorithm is really slow because it’s implemented as a brute force nearest neighbor over Euclidean distance search. Although an O(1) version of this algorithm exists with slightly less correctness. Your humble author simply hasn’t had the time to implement it in this library.

fabulous.xterm256.compile_speedup()

Tries to compile/link the C version of this module

Like it really makes a huge difference. With a little bit of luck this should just work for you.

You need:

  • Python >= 2.5 for ctypes library
  • gcc (sudo apt-get install gcc)

fabulous.text

The text module makes it possible to print TrueType text to the terminal. This functionality is available on the command line:

jart@compy:~$ fabulous-text --help
jart@compy:~$ fabulous-text --skew=5 --shadow 'Fabulous!'
jart@compy:~$ python -m fabulous.text --help

Or as a Python library:

from fabulous import text
print text.Text("Fabulous!", color='#0099ff', shadow=True, skew=5)

To make things simple, Fabulous bundles the following Google Noto Fonts which look good and are guaranteed to work no matter what:

  • NotoSans-Bold
  • NotoEmoji-Regular

For other fonts, Fabulous will do its best to figure out where they are stored. If Fabulous has trouble finding your font, try using an absolute path with the extension. It’s also possible to put the font in the ~/.fonts directory and then running fc-cache -fv ~/.fonts.

You can run fabulous-text --list to see what fonts are available.

exception fabulous.text.FontNotFound

I get raised when the font-searching hueristics fail

This class extends the standard ValueError exception so you don’t have to import me if you don’t want to.

class fabulous.text.Text(text, fsize=23, color='#0099ff', shadow=False, skew=None, font='NotoSans-Bold')

Renders TrueType Text to Terminal

I’m a sub-class of fabulous.image.Image. My job is limited to simply getting things ready. I do this by:

  • Turning your text into an RGB-Alpha bitmap image using PIL
  • Applying way cool effects (if you choose to enable them)

For example:

>>> assert Text("Fabulous", shadow=True, skew=5)

>>> txt = Text("lorem ipsum", font="NotoSans-Bold")
>>> len(str(txt)) > 0
True
>>> txt = Text(u"😃", font="NotoSans-Bold")
>>> len(str(txt)) > 0
True
Parameters:
  • text – The text you want to display as a string.
  • fsize – The font size in points. This obviously end up looking much larger because in fabulous a single character is treated as one horizontal pixel and two vertical pixels.
  • color – The color (specified as you would in HTML/CSS) of your text. For example Red could be specified as follows: red, #00F or #0000FF.
  • shadow – If true, render a simple drop-shadow beneath text. The Fabulous logo uses this feature.
  • skew – Skew size in pixels. This applies an affine transform to shift the top-most pixels to the right. The Fabulous logo uses a five pixel skew.
  • font – The TrueType font you want. If this is not an absolute path, Fabulous will search for your font by globbing the specified name in various directories.
fabulous.text.get_font_files()

Returns a list of all font files we could find

Returned as a list of dir/files tuples:

get_font_files() -> {'FontName': '/abs/FontName.ttf', ...]

For example:

>>> fonts = get_font_files()
>>> 'NotoSans-Bold' in fonts
True
>>> fonts['NotoSans-Bold'].endswith('/NotoSans-Bold.ttf')
True
fabulous.text.main()

Main function for fabulous-text.

fabulous.text.resolve_font(name)

Turns font names into absolute filenames

This is case sensitive. The extension should be omitted.

For example:

>>> path = resolve_font('NotoSans-Bold')

>>> fontdir = os.path.join(os.path.dirname(__file__), 'fonts')
>>> noto_path = os.path.join(fontdir, 'NotoSans-Bold.ttf')
>>> noto_path = os.path.abspath(noto_path)
>>> assert path == noto_path

Absolute paths are allowed:

>>> resolve_font(noto_path) == noto_path
True

Raises FontNotFound on failure:

>>> try:
...     resolve_font('blahahaha')
...     assert False
... except FontNotFound:
...     pass

fabulous.image

The image module makes it possible to print images to the terminal.

This module is available as a command line tool:

jart@compy:~$ fabulous-image foo.png
jart@compy:~$ python -m fabulous.image foo.png
class fabulous.image.Image(path, width=None)

Printing image files to a terminal

I use PIL to turn your image file into a bitmap, resize it so it’ll fit inside your terminal, and implement methods so I can behave like a string or iterable.

When resizing, I’ll assume that a single character on the terminal display is one pixel wide and two pixels tall. For most fonts this is the best way to preserve the aspect ratio of your image.

All colors are are quantized by fabulous.xterm256 to the 256 colors supported by modern terminals. When quantizing semi-transparant pixels (common in text or PNG files) I’ll ask TerminalInfo for the background color I should use to solidify the color. Fully transparent pixels will be rendered as a blank space without color so we don’t need to mix in a background color.

I also put a lot of work into optimizing the output line-by-line so it needs as few ANSI escape sequences as possible. If your terminal is kinda slow, you’re gonna want to buy me a drink ;) You can use DebugImage to visualize these optimizations.

The generated output will only include spaces with different background colors. In the future routines will be provided to overlay text on top of these images.

convert()

Yields xterm color codes for each pixel in image

reduce(colors)

Converts color codes into optimized text

This optimizer works by merging adjacent colors so we don’t have to repeat the same escape codes for each pixel. There is no loss of information.

Parameters:colors – Iterable yielding an xterm color code for each pixel, None to indicate a transparent pixel, or 'EOL' to indicate th end of a line.
Returns:Yields lines of optimized text.
resize(width=None)

Resizes image to fit inside terminal

Called by the constructor automatically.

size

Returns size of image

fabulous.image.main()

Main function for fabulous-image.

fabulous.logs

Utilities for transient logging.

This is very useful tool for monitoring what your Python scripts are doing. It allows you to have full verbosity without drowning out important error messages:

import time, logging
from fabulous import logs
logs.basicConfig(level='WARNING')

for n in range(20):
    logging.debug("verbose stuff you don't care about")
    time.sleep(0.1)
logging.warning("something bad happened!")
for n in range(20):
    logging.debug("verbose stuff you don't care about")
    time.sleep(0.1)
class fabulous.logs.TransientStreamHandler(strm=<open file '<stderr>', mode 'w' at 0x7f507d1481e0>, level=30)

Standard Python logging Handler for Transient Console Logging

Logging transiently means that verbose logging messages like DEBUG will only appear on the last line of your terminal for a short period of time and important messages like WARNING will scroll like normal text.

This allows you to log lots of messages without the important stuff getting drowned out.

This module integrates with the standard Python logging module.

fabulous.logs.basicConfig(level=30, transient_level=0)

Shortcut for setting up transient logging

I am a replica of logging.basicConfig which installs a transient logging handler to stderr.

fabulous.widget

Widget library using terminate.

class fabulous.widget.ProgressBar(title=None)
A 3-line progress bar, which looks like::
title
39% [================>—————————-]
message

p = ProgressBar(‘spam’) # create bar p.update(0, ‘starting spam’) # start printing it out p.update(50, ‘spam almost ready’) # progress p.update(100, ‘spam complete’)

clear()
get_bar(percent)
get_message()

returns None or string

get_title()
set_message(message=None)
set_title(title=None)
update(percent, message=None, test=False)
class fabulous.widget.TimedProgressBar(title=None)
A 3-line progress bar, which looks like::
title
39% [================>—————————-] ETA mm:ss
message

p = ProgressBar(‘spam’) # create bar p.update(0, ‘starting spam’) # start printing it out p.update(50, ‘spam almost ready’) # progress p.update(100, ‘spam complete’)

BAR_FORMAT = {'padding': 2, 'text': ' %3d%% [%s\x1b[2m%s\x1b[0m]', 'length': 13}

ETA 12:23

fabulous.term

Terminal abstraction layer.

Provides standard capabilites to a variety of terminals. Support information is being worked on.

import os
os.stdout.write('spam' +
                display('bright','yellow','white') +
                'eggs' +
                display('default') + os.linesep)

Warning: on IPython setting sys.stdout to stdout will break readline

Caveat: Failure to flush after ouput can cause weird ordering behaviour when writing to stdout and stderr simutaniously. This should fix the worst of it, but application developers should be warned not to rely on the state of things between call between one method call and another

fabulous.term.display(codes=, []fg=None, bg=None)

Returns an ANSI display code. This is useful when writing to an Term

codes
A list containing strings. The strings should one of the keys in Magic.DISPLAY. It can also be just a single string.
fg, bg
A string. Explicitly for setting the foreground or background. Use one of the keys in Magic.COLORS.
# give bright blue foreground and white background with underline
display(('bright','underline'),'blue','white')
# gives a blue foreground
display(fg='blue')
# resets the color to the default.
display('default')

Avoid using black or white. Depending on the situation the default background/foreground is normally black or white, but it’s hard to tell which. Bare terminals are normally white on black, but virtual terminals run from X or another GUI system are often black on white. This can lead to unpredicatble results. If you want reversed colours, use the ‘reverse’ code, and if you want to set the colors back to their original colors, use the ‘default’ code.

Also, be prudent with your use of ‘hidden’ and ‘blink’. Several terminals do not support them (and for good reason too), they can be really annoying and make reading difficult.

class fabulous.term.Term(stream)

A file-like object which also supports terminal features.

This is a base class for dumb terminals. It supports almost nothing.

bell()

Causes the computer to beep

Use sparingly, it is mainly to alert the user if something potentialy bad may be happening.

clear(scope='screen')

clears part of the screen

The valid values for scope are:

right
Clears a single space directly to the right.
left
Clears a single space directly to the left.
line
Clears the current line.
screen
Clears the whole screen.
beginning of line
Clears from the current position to the beginning of the line
end of line
Clears from the current position to the end of the line
end of screen
Clears from the current position to the end of the screen

N.b. this is not the same as deleting. After a place is cleared it should still be there, but with nothing in it. Also, this should not change the position of the cursor.

display(codes=, []fg=None, bg=None)

Not for public consumption (yet)

Just use display() and stdout.write() for now.

run this at the beginning:

(codes, fg, bg) = Magic.displayformat(codes, fg, bg)
fileno()

Returns the stream’s file descriptor as an integer

flush()

Ensure the text is ouput to the screen.

The write() method will do this automatically, so only use this when using self.stream.write().

get_size()

Get the width and height of the terminal.

Returns either a tuple of two integers or None. If two integers are returned, the first one is the number of columns (or width) and the second value is the number of lines (or height). If None is returned, then the terminal does not support this feature. If you still need to have a value to fall back on (75, 25) is a fairly descent fallback.

getch()

Don’t use this yet

It doesn’t belong here but I haven’t yet thought about a proper way to implement this feature and the features that will depend on it.

isatty()

Returns True if the terminal is a terminal

This should always be True. If it’s not somebody is being rather nauty.

move(place, distance=1)

Move cursor position

The valid values for place are:

up
Move up a line.
down
Move to the next line. This also puts you at the beginning of the line.
left
Move one place to the left.
right
Move one place to the right.
beginning of line
Move to the beginning of the current line.
beginning of screen
Move to the beginning of the screen.
raw_input(prompt)

Don’t use this yet

It doesn’t belong here but I haven’t yet thought about a proper way to implement this feature and the features that will depend on it.

set_title(name)

Sets the title of the terminal

write(text)

Parses text and prints proper output to the terminal

This method will extract escape codes from the text and handle them as well as possible for whichever platform is being used. At the moment only the display escape codes are supported.

writelines(sequence_of_strings)

Write out a sequence of strings

Note that newlines are not added. The sequence may be any iterable object producing strings. This is equivalent to calling write() for each string.

class fabulous.term.WinTerm(stream)

Windows version of terminal control

This class should not be used by itself, use either Win32Terminal or WinCTypesTerminal classes that subclasses of this class.

This class makes extensive use of the Windows API

The official documentation for the API is on MSDN (look for ‘console functions’)

clear(scope='screen')

see doc in Term class

According to http://support.microsoft.com/kb/99261 the best way to clear the console is to write out empty spaces

display(codes=, []fg=None, bg=None)

Displays codes using Windows kernel calls

get_size()

see doc in Term class

getch()

Don’t use this yet

It doesn’t belong here but I haven’t yet thought about a proper way to implement this feature and the features that will depend on it.

move(place, distance=1)

see doc in Term class

class fabulous.term.Win32Term(stream)

PyWin32 version of Windows terminal control.

Uses the PyWin32 Libraries <http://sourceforge.net/projects/pywin32/>.

ActiveState has good documentation for them:

Main page: http://aspn.activestate.com/ASPN/docs/ActivePython/2.4/pywin32/PyWin32.html Console related objects and methods: http://aspn.activestate.com/ASPN/docs/ActivePython/2.4/pywin32/PyConsoleScreenBuffer.html

class fabulous.term.WinCTypesTerm(stream)

CTypes version of Windows terminal control.

It requires the CTypes libraries <http://sourceforge.net/projects/ctypes/>

As of Python 2.5, CTypes is included in Python by default. User’s of previous version of Python will have to install it if they what to use this.

fabulous.rlcomplete

Readline related stuff.

class fabulous.rlcomplete.Completer

A base class for completers.

Child classes should implement the completelist method.

complete(text, state)

The actual completion method

This method is not meant to be overridden. Override the completelist method instead. It will make your life much easier.

For more detail see documentation for readline.set_completer

completelist(text)

Returns a list.

The list contains a series of strings which are the suggestions for the given string text. It is valid to have no suggestions (empty list returned).

class fabulous.rlcomplete.ListCompleter(words, ignorecase)

A class that does completion based on a predefined list.

class fabulous.rlcomplete.PathCompleter

Does completion based on file paths.

completelist(text)

Return a list of potential matches for completion

n.b. you want to complete to a file in the current working directory that starts with a ~, use ./~ when typing in. Paths that start with ~ are magical and specify users’ home paths

static matchuserhome(prefix)

To find matches that start with prefix.

For example, if prefix = ‘~user’ this returns list of possible matches in form of [‘~userspam’,’~usereggs’] etc.

matchuserdir(‘~’) returns all users

fabulous.gotham

This is a gimmick feature that generates silly gothic poetry.

This uses a simple mad lib algorithm. It has no concept of meter or rhyme. If you want a proper poetry generator, check out poemy2 which uses markov chains and isledict. It’s written by the same author as Fabulous.

This module can be run as a command line tool:

jart@compy:~$ fabulous-gotham
jart@compy:~$ python -m fabulous.gotham
fabulous.gotham.lorem_gotham()

Cheesy Gothic Poetry Generator

Uses Python generators to yield eternal angst.

When you need to generate random verbiage to test your code or typographic design, let’s face it... Lorem Ipsum and “the quick brown fox” are old and boring!

What you need is something with flavor, the kind of thing a depressed teenager with a lot of black makeup would write.

fabulous.gotham.lorem_gotham_title()

Names your poem

fabulous.gotham.main()

I provide a command-line interface for this module

fabulous.rotating_cube

Command for animating a wireframe rotating cube in the terminal.

class fabulous.rotating_cube.Frame

Canvas object for drawing a frame to be printed

line(x0, y0, x1, y1, c='*')

Draws a line

Who would have thought this would be so complicated? Thanks again Wikipedia <3

fabulous.rotating_cube.rotating_cube(degree_change=3, frame_rate=3)

Rotating cube program

How it works:

  1. Create two imaginary ellipses
  2. Sized to fit in the top third and bottom third of screen
  3. Create four imaginary points on each ellipse
  4. Make those points the top and bottom corners of your cube
  5. Connect the lines and render
  6. Rotate the points on the ellipses and repeat

fabulous.debug

The debug module provides the ability to print images as ASCII. It isn’t a good ASCII representation like cacalib. This module is mostly intended for debugging purposes (hence the name.)

class fabulous.debug.DebugImage(path, width=None)

Visualize optimization techniques used by Image

fabulous.debug.main()

I provide a command-line interface for this module

fabulous.utils

Miscellaneous utilities for Fabulous.

class fabulous.utils.TerminalInfo(bgcolor='black')

Quick and easy access to some terminal information

I’ll tell you the terminal width/height and it’s background color.

You don’t need to use me directly. Just access the global term instance:

>>> assert term.width > 0
>>> assert term.height > 0

It’s important to know the background color when rendering PNG images with semi-transparency. Because there’s no way to detect this, black will be the default:

>>> term.bgcolor
(0.0, 0.0, 0.0, 1.0)
>>> from fabulous import grapefruit
>>> isinstance(term.bgcolor, grapefruit.Color)
True

If you use a white terminal, you’ll need to manually change this:

>>> term.bgcolor = 'white'
>>> term.bgcolor
(1.0, 1.0, 1.0, 1.0)
>>> term.bgcolor = grapefruit.Color.NewFromRgb(0.0, 0.0, 0.0, 1.0)
>>> term.bgcolor
(0.0, 0.0, 0.0, 1.0)
dimensions

Returns terminal dimensions

Don’t save this information for long periods of time because the user might resize their terminal.

Returns:Returns (width, height). If there’s no terminal to be found, we’ll just return (79, 40).
height

Returns height of terminal in lines

termfd

Returns file descriptor number of terminal

This will look at all three standard i/o file descriptors and return whichever one is actually a TTY in case you’re redirecting i/o through pipes.

width

Returns width of terminal in characters

fabulous.utils.memoize(function)

A very simple memoize decorator to optimize pure-ish functions

Don’t use this unless you’ve examined the code and see the potential risks.

fabulous.utils.pil_check()

Check for PIL library, printing friendly error if not found

We need PIL for the fabulous.text and fabulous.image modules to work. Because PIL can be very tricky to install, it’s not listed in the setup.py requirements list.

Terminal Support

Supported Terminals

Terminal default bright dim underline blink reverse hidden
xterm yes yes yes yes yes yes yes
linux yes yes yes bright yes yes no
rxvt yes yes no yes bright yes no
Windows [0] yes yes yes no no yes yes
PuTTY [1] yes yes no yes [2] yes no
Cygwin SSH [3] yes yes no [4] [4] [2] yes

Currently unsupported, but should support

Terminal default bright dim underline blink reverse hidden
dtterm yes yes yes yes reverse yes yes
teraterm yes reverse no yes rev/red yes no
aixterm kinda normal no yes no yes yes
Mac Terminal yes yes no yes yes yes yes

Unsupported and will not support

Windows Telnet
It thinks it supports ANSI control, but it’s so horribly buggy its best to ignore it all together. (TERM = ansi)
[0]The default windows terminal, cmd.exe does not set the TERM variable, so detection is done by checking if the string 'win32' is in sys.platform. This This method has some limitations, particularly with remote terminal. But if you’re allowing remote access to a Windows computer you probably have bigger problems.
[1]Putty has the TERM variable set to xterm by default
[2](1, 2) Makes background bright
[3]Cygwin’s SSH support’s ANSI, but the regular terminal does not, check for win32 first, then check for cygwin. That should give us the cases when cygwin is used through SSH or telnet or something. (TERM = cygwin)
[4](1, 2) Sets foreground color to cyan

Alternatives

Here’s how Fabulous compares to other similar libraries:

  • fabulous: Licensed Apache 2.0. Focuses on delivering useful features in the simplest, most user-friendly way possible (without a repulsive name.) Written in pure-python but will attempt to auto-magically compile/link a speedup library. ~5,000 lines of code.
  • libcaca: WTFPL. This is the established and respected standard for doing totally insane things with ascii art (ever wanted to watch a movie on the command line?) Weighing in at ~72k lines of C, this project is a monster. It uses an older, more complex text/dithering-based rendering method. Compared to fabulous, some images look better, some worse. I found the docs somewhat difficult to follow and couldn’t find support for transparency or 256-colors.
  • asciiporn: GPL. Similar to libcaca but has an interesting feature for drawing math graphs to the terminal... Needs to compile C code, requires numpy/python2.6, and I couldn’t get the darn thing to work. Aprox 17k lines of code.
  • pygments: BSD. Has excellent support for terminal syntax highlighting.
  • termcolor: GPL. Only supports 4-bit ANSI colors.

License

Fabulous code and documentation are licensed Apache 2.0:

                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

The bundled Google Noto Fonts are licensed under the SIL Open Font License, Version 1.1:

This Font Software is licensed under the SIL Open Font License,
Version 1.1.

This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL

-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------

PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font
creation efforts of academic and linguistic communities, and to
provide a free and open framework in which fonts may be shared and
improved in partnership with others.

The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply to
any document created using the fonts or their derivatives.

DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.

"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).

"Original Version" refers to the collection of Font Software
components as distributed by the Copyright Holder(s).

"Modified Version" refers to any derivative made by adding to,
deleting, or substituting -- in part or in whole -- any of the
components of the Original Version, by changing formats or by porting
the Font Software to a new environment.

"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.

PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed,
modify, redistribute, and sell modified and unmodified copies of the
Font Software, subject to the following conditions:

1) Neither the Font Software nor any of its individual components, in
Original or Modified Versions, may be sold by itself.

2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.

3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the
corresponding Copyright Holder. This restriction only applies to the
primary font name as presented to the users.

4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.

5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created using
the Font Software.

TERMINATION
This license becomes null and void if any of the above conditions are
not met.

DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.