Table of Contents
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
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,
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 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 CGA supported 8x8 fonts (200 scanlines). The font was in ROM and could not be changed in software. The 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).
- 1 The VGA can display up to 512 glyphs by using up more video RAM and turning off bright backgrounds and blinking. This, however, was such a rare feature to see used that no BBS software could assume its presence, and there was never a standard for what those upper 256 characters had to look like, so portability was nil.
- 2 I have lots of ANSI work with characters coded in Greek CP737, and I had all those fonts lying about anyway.