#!/usr/bin/env python
# Script to render text as a PNG image.

# texttopng

# Example (all ASCII glyphs):
#
# printf $(printf '\\%s' $(seq 40 176 | grep -v '[89]')) |
#   fold -w 32 |
#   ./texttopng > ascii.png

import itertools


def usage(fil):
    fil.write("texttopng [-h|--help] text\n")


font = {
    32: '0000000000000000',
    33: '0010101010001000',
    34: '0028280000000000',
    35: '0000287c287c2800',
    36: '00103c5038147810',
    37: '0000644810244c00',
    38: '0020502054483400',
    39: '0010100000000000',
    40: '0008101010101008',
    41: '0020101010101020',
    42: '0010543838541000',
    43: '000010107c101000',
    44: '0000000000301020',
    45: '000000007c000000',
    46: '0000000000303000',
    47: '0000040810204000',
    48: '0038445454443800',
    49: '0008180808080800',
    50: '0038043840407c00',
    51: '003c041804043800',
    52: '00081828487c0800',
    53: '0078407804047800',
    54: '0038407844443800',
    55: '007c040810101000',
    56: '0038443844443800',
    57: '0038443c04040400',
    58: '0000303000303000',
    59: '0000303000301020',
    60: '0004081020100804',
    61: '0000007c007c0000',
    62: '0040201008102040',
    63: '0038440810001000',
    64: '00384c545c403800',
    65: '0038447c44444400',
    66: '0078447844447800',
    67: '0038444040443800',
    68: '0070484444487000',
    69: '007c407840407c00',
    70: '007c407840404000',
    71: '003844405c443c00',
    72: '0044447c44444400',
    73: '0038101010103800',
    74: '003c040404443800',
    75: '0044487048444400',
    76: '0040404040407c00',
    77: '006c545444444400',
    78: '004464544c444400',
    79: '0038444444443800',
    80: '0078447840404000',
    81: '0038444444443c02',
    82: '0078447844444400',
    83: '0038403804047800',
    84: '007c101010101000',
    85: '0044444444443c00',
    86: '0044444444281000',
    87: '0044445454543800',
    88: '0042241818244200',
    89: '0044443810101000',
    90: '007c081020407c00',
    91: '0038202020202038',
    92: '0000402010080400',
    93: '0038080808080838',
    94: '0010284400000000',
    95: '000000000000fe00',
    96: '0040200000000000',
    97: '000038043c443c00',
    98: '0040784444447800',
    99: '0000384040403800',
    100: '00043c4444443c00',
    101: '000038447c403c00',
    102: '0018203820202000',
    103: '00003c44443c0438',
    104: '0040784444444400',
    105: '0010003010101000',
    106: '0010003010101020',
    107: '0040404870484400',
    108: '0030101010101000',
    109: '0000385454444400',
    110: '0000784444444400',
    111: '0000384444443800',
    112: '0000784444784040',
    113: '00003c44443c0406',
    114: '00001c2020202000',
    115: '00003c4038047800',
    116: '0020203820201800',
    117: '0000444444443c00',
    118: '0000444444281000',
    119: '0000444454543800',
    120: '0000442810284400',
    121: '00004444443c0438',
    122: '00007c0810207c00',
    123: '0018202060202018',
    124: '0010101000101010',
    125: '003008080c080830',
    126: '0020540800000000',
}


def char(i):
    """Get image data for the character `i` (a one character string).
    Returned as a list of rows.  Each row is a tuple containing the
    packed pixels.
    """

    i = ord(i)
    if i not in font:
        return [(0,)] * 8
    return [(ord(row),) for row in font[i].decode('hex')]


def texttoraster(m):
    """Convert the string *m* to a raster image. Any newlines
    in *m* will cause more than one line of output. The
    resulting raster will be taller. Prior to rendering each
    line, it is padded on the right with enough spaces to make
    all lines the same length.
    """

    lines = m.split('\n')
    maxlen = max(len(line) for line in lines)
    justified = [line.ljust(maxlen) for line in lines]
    rasters = [linetoraster(line) for line in justified]
    x, = set(r[0] for r in rasters)
    y = sum(r[1] for r in rasters)
    raster = itertools.chain(*(r[2] for r in rasters))
    return x, y, raster


def linetoraster(m):
    """Convert a single line of text *m* to a raster image,
    by rendering it using the font in *font*.

    A triple of (*width*, *height*, *pixels*) is returned;
    *pixels* is in boxed row packed pixel format.
    """

    # Assumes monospaced font.
    x = 8 * len(m)
    y = 8
    return x, y, [itertools.chain(*row) for row in zip(*map(char, m))]


def render(message, out):
    import png

    x, y, pixels = texttoraster(message)
    w = png.Writer(x, y, greyscale=True, bitdepth=1)
    w.write_packed(out, pixels)
    out.flush()


def main(argv=None):
    import sys

    if argv is None:
        argv = sys.argv
    for a in argv:
        if a.startswith('-'):
            if a in ('-h', '--help'):
                usage(sys.stdout)
                sys.exit(0)
            else:
                sys.stderr.write("Unknown option: %s\n" % a)
                usage(sys.stderr)
                sys.exit(4)
    arg = argv[1:]
    if len(arg) > 0:
        message = arg[0]
        out = open("%s.png" % message, 'wb')
    else:
        message = sys.stdin.read()
        out = sys.stdout
        # on Windows it is necessary to switch stdout to binary mode
        # to avoid \n translation
        # http://stackoverflow.com/questions/2374427/python-2-x-write-binary-output-to-stdout
        if sys.platform == "win32":
            import msvcrt
            import os
            msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)

    render(message, out)


if __name__ == '__main__':
    main()
