This small Python program converts ANSI art to raster images. It simulates a PC's text mode using different VGA soft fonts (or EGA, or the CGA ROM font) and renders colourful BBS graphics, affectionately known as ANSI (after the
ANSI.SYS MS-DOS driver that rendered them back in the day). So-called ‘ANSI’ graphics are text files in one of the IBM PC extended ‘ASCII’ encodings. The files include terminal directives for the MS-DOS terminal driver ANSI.SYS, which implements a small subset of the ANSI X.364 standard for Control Sequences for Video Terminals.
With the special characters in the various IBM PC character sets, box-drawing and crude graphics were feasible. Using ANSI, these graphics could also be coloured and even repositioned for simple animations.
Such files were commonly used on Bulletin Board Systems (BBS) and other similar software and, despite (or perhaps because) of the limitations of the format, eventually developed into an art form.
ANSI.SYS only supported a small subset of ANSI X.364, and common sense would have it that modern virtual terminals would be able to display these graphics unhindered. Unfortunately, there are three obstacles in this.
- The encodings (‘codepages’) themselves need a certain amount of expertise to render the intended characters on modern (usually Unicode-based) terminals.
- Modern fonts are designed with legibility in mind, and have different metrics and proportions to the low-resolution bitmap fonts of the Eighties. As designers used these characters for various purposes (aside from their original ones), we should only reproduce these files using their original fonts.
- Most modern virtual terminals are either incapable of displaying ANSI X.364 escape sequences (the terminal emulator of Windows Vista is one example), or support a subtly different set of commands and features. These factors limit the rendering accuracy of ANSI graphics.
One of my mantras is: nothing is worth doing unless it's reusable and generalised. To do this,
renderconsole, my generalised, abstract terminal rendering framework for Python. This Python package is able to render any terminal control language and convert it to any format. At the moment, only the
ANSI.SYS subset of ANSI X.364 is supported. The library outputs XPM files by default, but it can use the Python Imaging Library (PIL) to save any bitmap format your heart desires.
IBM PC Text Mode Fonts
Until the advent of the VGA, IBM PCs had a range of 256 visible glyphs per font1.
The IBM Monochrome Display Adapter|IBM MDA, the first display adapter (a text-only, monochrome device) offered a 9x14 font, but this is not (as of yet) supported by this program. The IBM Color Graphics Adapter|IBM CGA supported 8x8 fonts (200 scanlines). The font was in ROM and could not be changed in software. The IBM Enhanced Graphics Adapter|IBM EGA was the first commonly used IBM card to support soft fonts of various sizes, the usual being 8x14. It also included the CGA 8x8 font for compatibility. The IBM VGA contained all the CGA and EGA fonts and added an 8x16 font. This is rendered as 9x16 at the default text resolution of 720x480, which is still with us.
EGA and VGA fonts are simple arrays of bytes. Each character from 0 to 255 is represented by 8, 14, or 16 bytes. The least significant bit of each byte represented the rightmost pixel of each character cell. This resulted in fonts of 2,048, 3,584 and 4,096 bytes. This program supports any such uncompressed, unencoded font without metadata, including fonts with arbitrary character heights (it will do its best to guess).
A default VGA font for CP437, the US codepage, is built into the program.
renderconsole and the
ansi2img program are bundled together as part of the
renderconsole distribution. Get it while it's fresh!
It's all available under the terms of the GNU Public License. The example files in the distribution are © their respective artists.
Here's an example kindly contributed by a reader of my previous homepage and modified for simplicity:
from cStringIO import StringIO import renderconsole # Create a memory file to display "dynamically" on the web with # no files written to disk. png = StringIO() font = renderconsole.DefaultFont() framebuffer = renderconsole.Framebuffer(80, 25, growable=True) term = renderconsole.ANSITerminalEmulator(framebuffer) screen = renderconsole.PNGWriter(framebuffer, font, stream=png) term.write(open('art.ansi').read()) screen.render() # Get data in mem file. This is the image binary data. Add HTTP headers # to display on the web. Or just use a standard file open for writing # and you'll already have data saved on it at this point. png.getvalue()
Try It Online!
If you'd rather not install anything on your computer, you can use the ANSI converter online The online version does everything the offline one does, except you're limited to the few fonts it has (CGA, EGA, and VGA, plus Greek versions2).