Banner - Noah.org

Banner

From Noah.org

Jump to: navigation, search

Contents

Banner

This is my version of the classic `banner` program. This is written in Python. It supports font rotation; XPM Pixmap output; two font sizes. It can read text from stdin or from the command-line.

MOTD banner

I like to have a nice big banner display the machine name when I login. I use the following script to create a banner that I put in /etc/motd.

# banner $HOSTNAME > /etc/motd

Simple XPM pixmaps

This script can also create text icons and labels for X-Windows. Using the -x option will tell the script to output in xpm format. Put the output in a file in /usr/share/pixmaps/ and you can use the icon anywhere in X-Windows.

# banner -x -g 32x32 www noah org > /usr/share/pixmaps/www.noah.org.xpm

Install

I put this script in my ~/bin directory. This is part of my standard Dotfiles distribution.

Download banner

#!/usr/bin/env python
"""
 ####    #   #   # #   # ##### ####
 #   #  # #  ##  # ##  # #     #   #
 ####  #   # # # # # # # ####  ####
 #   # ##### #  ## #  ## #     #  #
 ####  #   # #   # #   # ##### #   #
 
SYNOPSIS
 
    banner [-x] [-5] [-6] [-r] [-g NxN] [-j] [STRING]...
 
DESCRIPTION
 
    Banner is a python script that prints a banner in a 6x6 or 5x5 pixel font.
    It can also create an XPM pixmap of the banner. This is useful for creating
    icons and labels.
 
OPTIONS
 
    -h : display help text
 
    -x : output banner in XPM format. This is handy for making icons.
 
    -5 : use 5x5 font. This is the default. Upper and lower cases are the same.
 
    -6 : use 6x6 font
 
    -r : rotate banner 90 degrees clockwise.
        This options can be used multiple times to rotate 180 and 270 degrees.
 
    -g : set geometry to a given WIDTHxHEIGHT. Most useful with -x to
        create icons. For example:
        banner -x -g 32x32 hello world
 
    -j : join all words on one line
        By default, spaces between words are treated as new lines.
        You can also escape or quote individual spaces
        if you want then on the same line.
 
EXAMPLES
 
    banner hello world
 
    #   # ##### #     #      ###
    #   # #     #     #     #   #
    ##### ####  #     #     #   #
    #   # #     #     #     #   #
    #   # ##### ##### #####  ###
 
    #   #  ###  ####  #     ####
    #   # #   # #   # #      #  #
    # # # #   # ####  #      #  #
    ## ## #   # # #   #      #  #
    #   #  ###  #  #  ##### ####
 
    Note that spaces between words become new lines.
    You can prevent this by escaping the space:
        banner hello\\ world
    or by quoting the string:
        banner "hello world"
    or by using the -j option:
        banner -j hello word
 
    #   # ##### #     #      ###        #   #  ###  ####  #     ####
    #   # #     #     #     #   #       #   # #   # #   # #      #  #
    ##### ####  #     #     #   #       # # # #   # ####  #      #  #
    #   # #     #     #     #   #       ## ## #   # # #   #      #  #
    #   # ##### ##### #####  ###        #   #  ###  #  #  ##### ####
 
 
EXIT STATUS
 
    Returns a zero exist status on success.
    Returns non zero in case of failure.
 
AUTHOR
 
    Noah Spurrier <noah@noah.org>
 
LICENSE
 
    2007 Free from copyrights or restrictions. Public domain.
"""
 
import sys, os, string, getopt, traceback
 
def exit_with_usage(exit_code=1):
    print globals()['__doc__']
    sys.exit(exit_code)
 
# The font storage format is not very efficient, but
# it is easy to edit with a text editor.
 
font_six= """
|       |  ###  |### ###|  # #  |   #   |###   #|  ##   |  ###  |
|       |  ###  |### ###|  # #  |  #### |# #  # | #  #  |  ###  |
|       |  ###  | #   # |#######| # #   |### #  |  ##   |   #   |
|       |   #   | #   # |  # #  | ##### |   #   | ###   |   #   |
|       |       |       |#######|   #  #|  # ###|#   # #|       |
|       |  ###  |       |  # #  | ##### | #  # #|#    # |       |
|       |  ###  |       |  # #  |   #   |#   ###| #### #|       |
-----------------------------------------------------------------
|   #   |   #   |       |       |       |       |       |      #|
|  #    |    #  | #   # |   #   |       |       |       |     # |
| #     |     # |  # #  |   #   |       |       |       |    #  |
| #     |     # |#######| ##### |  ###  | ##### |       |   #   |
| #     |     # |  # #  |   #   |  ###  |       |  ###  |  #    |
|  #    |    #  | #   # |   #   |   #   |       |  ###  | #     |
|   #   |   #   |       |       |  #    |       |  ###  |#      |
-----------------------------------------------------------------
|       |       |       |       |       |       |       |       |
|   ##  |   #   |  #### | ##### |    ## | ######|  #### | ######|
|  #  # | ###   | #    #|      #|   # # | #     | #     |     # |
| #  # #|   #   |    ## |  #### |  #  # | ##### | # ### |    #  |
| # #  #|   #   |  ##   |      #| ######|      #| ##   #|   #   |
|  #  # |   #   | #     |      #|     # | #    #| #    #|  #    |
|   ##  | ##### | ######| ##### |     # |  #### |  #### |  #    |
-----------------------------------------------------------------
|       |       |   #   |  ###  |    #  |       |  #    |       |
|  #### |  #### |  ###  |  ###  |   #   |       |   #   |  #### |
| #    #| #    #|   #   |       |  #    | ##### |    #  | #    #|
|  #### | #    #|       |  ###  | #     |       |     # |    ## |
| #    #|  #####|   #   |  ###  |  #    | ##### |    #  |   #   |
| #    #|      #|  ###  |   #   |   #   |       |   #   |       |
|  #### |  #### |   #   |  #    |    #  |       |  #    |   #   |
-----------------------------------------------------------------
|       |       |       |       |       |       |       |       |
|  #### |   ##  | ##### |  #### | ##### | ######| ######|  #### |
| #    #|  #  # | #    #| #    #| #    #| #     | #     | #    #|
| # ## #| #    #| ##### | #     | #    #| ##### | ##### | #     |
| # ####| ######| #    #| #     | #    #| #     | #     | #  ###|
| #     | #    #| #    #| #    #| #    #| #     | #     | #    #|
|  #### | #    #| ##### |  #### | ##### | ######| #     |  #### |
-----------------------------------------------------------------
|       |       |       |       |       |       |       |       |
| #    #|    #  |      #| #    #| #     | #    #| #    #|  #### |
| #    #|    #  |      #| #   # | #     | ##  ##| ##   #| #    #|
| #    #|    #  |      #| ####  | #     | # ## #| # #  #| #    #|
| ######|    #  |      #| #  #  | #     | #    #| #  # #| #    #|
| #    #|    #  | #    #| #   # | #     | #    #| #   ##| #    #|
| #    #|    #  |  #### | #    #| ######| #    #| #    #|  #### |
-----------------------------------------------------------------
|       |       |       |       |       |       |       |       |
| ##### |  #### | ##### |  #### |  #####| #    #| #    #| #    #|
| #    #| #    #| #    #| #     |    #  | #    #| #    #| #    #|
| #    #| #    #| #    #|  #### |    #  | #    #| #    #| #    #|
| ##### | #  # #| ##### |      #|    #  | #    #| #    #| # ## #|
| #     | #   # | #   # | #    #|    #  | #    #|  #  # | ##  ##|
| #     |  ### #| #    #|  #### |    #  |  #### |   ##  | #    #|
-----------------------------------------------------------------
|       |       |       | ##### |#      | ##### |   #   |       |
| #    #|  #   #| ######| #     | #     |     # |  # #  |       |
|  #  # |   # # |     # | #     |  #    |     # | #   # |       |
|   ##  |    #  |    #  | #     |   #   |     # |       |       |
|   ##  |    #  |   #   | #     |    #  |     # |       |       |
|  #  # |    #  |  #    | #     |     # |     # |       |       |
| #    #|    #  | ######| ##### |      #| ##### |       |#######|
-----------------------------------------------------------------
|  ###  |       |       |       |       |       |       |       |
|  ###  |   ##  | ##### |  #### | ##### | ######| ######|  #### |
|   #   |  #  # | #    #| #    #| #    #| #     | #     | #    #|
|    #  | #    #| ##### | #     | #    #| ##### | ##### | #     |
|       | ######| #    #| #     | #    #| #     | #     | #  ###|
|       | #    #| #    #| #    #| #    #| #     | #     | #    #|
|       | #    #| ##### |  #### | ##### | ######| #     |  #### |
-----------------------------------------------------------------
|       |       |       |       |       |       |       |       |
| #    #|    #  |      #| #    #| #     | #    #| #    #|  #### |
| #    #|    #  |      #| #   # | #     | ##  ##| ##   #| #    #|
| #    #|    #  |      #| ####  | #     | # ## #| # #  #| #    #|
| ######|    #  |      #| #  #  | #     | #    #| #  # #| #    #|
| #    #|    #  | #    #| #   # | #     | #    #| #   ##| #    #|
| #    #|    #  |  #### | #    #| ######| #    #| #    #|  #### |
-----------------------------------------------------------------
|       |       |       |       |       |       |       |       |
| ##### |  #### | ##### |  #### |  #####| #    #| #    #| #    #|
| #    #| #    #| #    #| #     |    #  | #    #| #    #| #    #|
| #    #| #    #| #    #|  #### |    #  | #    #| #    #| #    #|
| ##### | #  # #| ##### |      #|    #  | #    #| #    #| # ## #|
| #     | #   # | #   # | #    #|    #  | #    #|  #  # | ##  ##|
| #     |  ### #| #    #|  #### |    #  |  #### |   ##  | #    #|
-----------------------------------------------------------------
|       |       |       |  ###  |   #   |  ###  | ##    |#######|
| #    #|  #   #| ######| #     |   #   |     # |#  #  #|#######|
|  #  # |   # # |     # | #     |   #   |     # |    ## |#######|
|   ##  |    #  |    #  |##     |   #   |     ##|       |#######|
|   ##  |    #  |   #   | #     |   #   |     # |       |#######|
|  #  # |    #  |  #    | #     |   #   |     # |       |#######|
| #    #|    #  | ######|  ###  |   #   |  ###  |       |#######|
"""
 
font_five="""
|      |      |      |      |   #  |###  #|      |      |
|      |  ##  | ## ##|  # # |  ####|# # # |  ##  |  ##  |
|      |  ##  | ## ##| #####| # #  |####  | #  # |  ##  |
|      |  ##  | ## ##|  # # |  ### |  ####| ###  | #    |
|      |      |      | #####|   # #| # # #|#   ##|      |
|      |  ##  |      |  # # | #### |#  ###| ### #|      |
---------------------------------------------------------
|  #   |   #  |      |      |      |      |      |      |
| #    |    # | #   #|   #  |      |      |      |    # |
| #    |    # |  # # |   #  |      |      |      |   #  |
| #    |    # | #####| #####|  ##  | #### |      |  #   |
| #    |    # |  # # |   #  |  ##  |      |  ##  | #    |
|  #   |   #  | #   #|   #  |   #  |      |  ##  |#     |
---------------------------------------------------------
|      |      |      |      |      |      |      |      |
|  ### |   #  |  ### | #### |    # | #####|  ### | #####|
| #  ##| ###  | #   #|     #|   ## | #    | #    |     #|
| # # #|   #  |   ## |  ### |  # # | #### | #### |    # |
| ##  #|   #  |  #   |     #| #####|     #| #   #|   #  |
|  ### | #####| #####| #### |    # | #### |  ### |  #   |
---------------------------------------------------------
|      |      |      |   ## |    # |      |  #   | #### |
|  ### |  ### |   ## |   ## |   #  | #### |   #  |     #|
| #   #| #   #|   ## |      |  #   |      |    # |   ## |
|  ### |  ####|      |   ## |   #  | #### |   #  |  #   |
| #   #|     #|   ## |   ## |    # |      |  #   |      |
|  ### |  ### |   ## |    # |      |      |      |  #   |
---------------------------------------------------------
| #### |      |      |      |      |      |      |      |
|#    #|   #  | #### |  ####| #### | #####| #####|  ####|
|# ## #|  # # | #   #| #    |  #  #| #    | #    | #    |
|# ####| #   #| #### | #    |  #  #| #### | #### | # ###|
|#     | #####| #   #| #    |  #  #| #    | #    | #   #|
| #### | #   #| #### |  ####| #### | #####| #    |  ### |
---------------------------------------------------------
|      |      |      |      |      |      |      |      |
| #   #|   #  |     #| #  # | #    | #   #| #   #|  ### |
| #   #|   #  |     #| # #  | #    | ## ##| ##  #| #   #|
| #####|   #  |     #| ##   | #    | # # #| # # #| #   #|
| #   #|   #  | #   #| # #  | #    | #   #| #  ##| #   #|
| #   #|   #  |  ### | #  # | #####| #   #| #   #|  ### |
---------------------------------------------------------
|      |      |      |      |      |      |      |      |
| #### |  ### | #### |  ####| #####| #   #| #   #| #   #|
| #   #| #   #| #   #| #    |   #  | #   #| #   #| #   #|
| #### | # # #| #### |  ### |   #  | #   #| #   #| # # #|
| #    | #  # | # #  |     #|   #  | #   #|  # # | ## ##|
| #    |  ## #| #  # | #### |   #  |  ### |   #  | #   #|
---------------------------------------------------------
|      |      |      | #### |#     | #### |   #  |      |
| #   #| #   #| #####| #    | #    |    # |  # # |      |
|  # # |  # # |    # | #    |  #   |    # | #   #|      |
|   #  |   #  |   #  | #    |   #  |    # |      |      |
|  # # |   #  |  #   | #    |    # |    # |      |      |
| #   #|   #  | #####| #### |     #| #### |      |######|
---------------------------------------------------------
|  ##  |      |      |      |      |      |      |      |
|  ##  |   #  | #### |  ####| #### | #####| #####|  ####|
|   #  |  # # | #   #| #    |  #  #| #    | #    | #    |
|    # | #   #| #### | #    |  #  #| #### | #### | # ###|
|      | #####| #   #| #    |  #  #| #    | #    | #   #|
|      | #   #| #### |  ####| #### | #####| #    |  ### |
---------------------------------------------------------
|      |      |      |      |      |      |      |      |
| #   #|   #  |     #| #  # | #    | #   #| #   #|  ### |
| #   #|   #  |     #| # #  | #    | ## ##| ##  #| #   #|
| #####|   #  |     #| ##   | #    | # # #| # # #| #   #|
| #   #|   #  | #   #| # #  | #    | #   #| #  ##| #   #|
| #   #|   #  |  ### | #  # | #####| #   #| #   #|  ### |
---------------------------------------------------------
|      |      |      |      |      |      |      |      |
| #### |  ### | #### |  ####| #####| #   #| #   #| #   #|
| #   #| #   #| #   #| #    |   #  | #   #| #   #| #   #|
| #### | # # #| #### |  ### |   #  | #   #| #   #| # # #|
| #    | #  # | # #  |     #|   #  | #   #|  # # | ## ##|
| #    |  ## #| #  # | #### |   #  |  ### |   #  | #   #|
---------------------------------------------------------
|      |      |      |  ### |   #  | ###  | ##   |######|
| #   #| #   #| #####| #    |   #  |    # |#  # #|######|
|  # # |  # # |    # |##    |   #  |    ##|    # |######|
|   #  |   #  |   #  |##    |   #  |    ##|      |######|
|  # # |   #  |  #   | #    |   #  |    # |      |######|
| #   #|   #  | #####|  ### |   #  | ###  |      |######|
"""
# HACK: trim the leading linefeed off the fonts.
font_six = font_six[1:]
font_five= font_five[1:]
 
def dump_xpm (page, geometry=(32,32)):
    """This dumps the given page to stdout in XPM pixmap format."""
    if geometry is not None:
        page = crop (page, geometry)
    pixel_width = len(page[0])
    pixel_height= len(page)
    xpm_header = """/* XPM */
    static char * banner_xpm[] = {
    "%(pixel_width)d %(pixel_height)d 3 1",
    "   c None",
    ".  c #FFFFFF",
    "#  c #000000",
    """ % locals()
    print xpm_header
    for l in page:
        print '    "' + l + '",'
    print "};"
 
def dump_page (page):
    """This dumps the given page to stdout."""
    for l in page:
        print l
 
def rotate (page):
    """This returns the give page rotated clockwise by 90 degrees."""
    new_page = []
    for vl in zip(*page):
        ll = list(vl)
        ll.reverse()
        new_page.append(string.join(ll,sep=''))
        #print string.join(ll,sep='')
    return new_page
 
def hcenter (page, pixel_width):
    """This centers the "page" horizontally in the given width."""
    page_pixel_width = len(page[0])
    pad = pixel_width - page_pixel_width
    left_pad = pad/2
    right_pad = pad/2
    if left_pad+right_pad < pad:
        left_pad = left_pad + 1
    new_page = []
    for r in page:
        r = ' '*left_pad + r + ' '*right_pad
        new_page.append(r)
    return new_page
 
def vcenter (page, pixel_height):
    """This centers the "page" vertically in the given height."""
    page_pixel_height = len(page)
    pad = pixel_height - page_pixel_height
    top_pad = pad/2
    bottom_pad = pad/2
    if top_pad+bottom_pad < pad:
        bottom_pad = bottom_pad + 1
    new_page = []
    for r in range(top_pad):
        new_page.append(' '*len(page[0]))
    for r in page:
        new_page.append(r)
    for r in range(bottom_pad):
        new_page.append(' '*len(page[0]))
    return new_page
 
def c_to_coord(c):
    """This takes an ascii character and returns the
    coordinates in the 8x11 array of character raster data.
    The 8x11 arrays of character raster data are arranged like this:
 
     !"#$%&'
    ()*+,-./
    01234567
    89:;<=>?
    @ABCDEFG
    HIJKLMNO
    PQRSTUVW
    XYZ[\]^_
    `abcdefg
    hijklmno
    pqrstuvw
    xyz{|}~
    """
    i = ord(c)-32
    y = i/8
    x = i%8
    if x < 0: x = 7;y = 11
    if x > 7: x = 7;y = 11
    if y < 0: x = 7;y = 11
    if y > 11: x = 7;y = 11
    return (x,y)
 
def get_raster_data_by_char_five (c=None):
    """This returns the raster data for the given character. """
    if c is None:
        return ['','','','','','']
    return get_raster_data_five(*c_to_coord(c))
 
def get_raster_data_by_char_six (c=None):
    """This returns the raster data for the given character. """
    if c is None:
        return ['','','','','','','']
    return get_raster_data_six(*c_to_coord(c))
 
def get_raster_data_five (x,y):
    """This returns the raster data for the given coordinate. """
    start = (58*(7*y))+(7*x)+1
    letter = []
    letter.append(font_five[start:start+6])
    start = start + 58
    letter.append(font_five[start:start+6])
    start = start + 58
    letter.append(font_five[start:start+6])
    start = start + 58
    letter.append(font_five[start:start+6])
    start = start + 58
    letter.append(font_five[start:start+6])
    start = start + 58
    letter.append(font_five[start:start+6])
    return letter
 
def get_raster_data_six (x,y):
    """This returns the raster data for the given coordinate. """
    start = (66*(8*y))+(8*x)+1
    letter = []
    letter.append(font_six[start:start+7])
    start = start + 66
    letter.append(font_six[start:start+7])
    start = start + 66
    letter.append(font_six[start:start+7])
    start = start + 66
    letter.append(font_six[start:start+7])
    start = start + 66
    letter.append(font_six[start:start+7])
    start = start + 66
    letter.append(font_six[start:start+7])
    start = start + 66
    letter.append(font_six[start:start+7])
    return letter
 
def append_raster_data (left_data, right_data):
    """This appends the given raster data in right_data to the right side of
    left_data. Note that weird things will happen if left_data and right_data
    are different heights.
    """
    return [z[0]+z[1] for z in zip(left_data,right_data)]
 
def vappend_raster_data (top_data, bottom_data):
    """This appends the given raster data in bottom_data to the bottom of the
    top_data. Note that the final width will grow to fit the widest band of
    data. The smaller band will be centered.
    """
    width_top_data = len(top_data[0])
    width_bottom_data = len(bottom_data[0])
    max_width = max(width_top_data,width_bottom_data)
    top_data = hcenter(top_data, max_width)
    bottom_data = hcenter(bottom_data, max_width)
    new_word = []
    for l in top_data:
        new_word.append(l)
    for l in bottom_data:
        new_word.append(l)
    return new_word
 
def append_letter_five (w1, c):
    letter = get_raster_data_by_char_five(c)
    return append_raster_data(w1, letter)
 
def append_string_five (word, s):
    if word is None:
        word = get_raster_data_by_char_five(None)
    for c in s:
        word = append_letter_five(word, c)
    return word
 
def append_letter_six (w1, c):
    letter = get_raster_data_by_char_six(c)
    return append_raster_data(w1, letter)
 
def append_string_six (word, s):
    if word is None:
        word = get_raster_data_by_char_six(None)
    for c in s:
        word = append_letter_six(word, c)
    return word
 
def crop (page, geometry):
    """This crops the raster data to the given geometry.
    It also centers and grows the raster data to fit the given geometry.
    """
    if len(page) > geometry[1]:
        page = page [0:geometry[1]]
    else:
        page = vcenter (page, geometry[1])
    if len(page[0]) > geometry[0]:
        new_page = []
        for p in page:
            new_page.append(p[0:geometry[0]])
        page = new_page
    else:
        page = hcenter(page, geometry[0])
    return page
 
def main ():
    try:
        optlist, args = getopt.getopt(sys.argv[1:], 'h?56xrjg:', ['help','h','?'])
        options = dict(optlist)
        # There are a million ways to cry for help. These are but a few of them.
        if [elem for elem in options if elem in ['-h','--h','-?','--?','--help']]:
            exit_with_usage(0)
        #if len(options)==0 and len(args)==0:
        #    exit_with_usage(0)
    except Exception, e:
        print str(e)
        exit_with_usage()
 
    if '-j' in options:
        args = [string.join(args)]
 
    if len(args) == 0:
        args = sys.stdin.read()
        args = args.split()
 
    #
    # Start building the banner
    #
 
    # build up an array of "bands". Each band is the raster data
    # for one line of the string.
    # This handle either 6 pixel or 5 pixel raster data.
    raster_bands = []
    if '-6' in options:
        for m in args:
            raster_bands.append(append_string_six(None, m))
    else:
        for m in args:
            raster_bands.append(append_string_five(None, m))
 
    # stack raster bands on top of each other to form one "page".
    # grow to fit widest line.
    page = raster_bands[0]
    for w in raster_bands[1:]:
        page = vappend_raster_data(page, w)
 
    # This rotates 90 degrees clockwise
    # once for each -v option on the command line.
    for vees in optlist:
        if vees[0]=='-r':
            page = rotate(page)
 
    # This crops the raster data to the given geometry.
    # It also centers and grows the raster data to fit
    # the given geometry.
    g = None
    if '-g' in options:
        g = options['-g'].split('x')
        g[0] = int(g[0])
        g[1] = int(g[1])
        if g[0]<1 or g[1]<1:
            print ("ERROR: geometry is impossibly small.")
            exit_with_usage(4)
        crop(page, g)
 
    # This handles the output.
    if '-x' in options:
        dump_xpm(page,g)
    else:
        dump_page (page)
 
if __name__ == "__main__":
    try:
#        start_time = time.time()
#        print time.asctime()
        main()
#        print time.asctime()
#        print "TOTAL TIME IN MINUTES:",
#        print (time.time() - start_time) / 60.0
    except Exception, e:
        tb_dump = traceback.format_exc()
        print "==============================================================="
        print "ERROR -- Unexpected exception in script."
        print str(e)
        print str(tb_dump)
        print "==============================================================="
        exit_with_usage(3)
-->