Printing pictures like its 1873 using Oki 3321 dot-matrix printer

Steinway hall 1873
As wikipedia says oldest halftone image printed in a newspaper back in 1873

Long, long time ago, before prices of inkjet and laser printers fell to levels allowing home users to own and use them, there was a primitive printing technology called dot-matrix. As any technology of the past, it is not competitive anymore. However it still has few advantages and one of them is reliability of these devices. Some time ago I found quite a cheap Oki 3321 printer that has 9 pin head and is capable of printing on A3 paper in portrait orientation. Usual mode of printing for these devices was simple text mode, where you just were writing your text in ASCII (or any weird coding popular in your country of origin) to its parallel port. Fortunately these printers usually had also graphic mode, where you could fully use capabilities of the device.

I already was experimenting some time with my device, so I already know it uses Mazovia variant (with zł as single glyph) as its codepage. I was also able to guess how to switch into graphic mode, so in theory I was able to print images for some time. Unfortunately any CUPS drivers I used did not provide acceptable results, so all I could do was to write some support tool myself.

Meet png2lp

png2lp is a tool that (as its name suggests) converts PNG images to format understandable by line printers. I tried to write it in a way that allows further extensions, so in theory it is possible to write support for a printer that works completely different way than mine. There is only one limitation – PNG image that comes as input must be saved in indexed mode with only two colors in a table. So, you cannot support printer that can print more colors or shades than one (or you could but png2lp can’t give you input of sufficient quality).

Usage is quite straightforward – you supply filename as a parameter or give filename “-” and put data on standard in. On stdout, you get converted image. As program supports more than one output format, you have to specify desired format also. You can list available sinks by typing:

png2lp -l

Then you can choose your printer from the list and list available page formats:

png2lp -L oki3321

And finally you can convert the image by typing:

png2lp -p oki3321 -P a4-p 484px-Tux_mono.svg.png

Converting ordinary images to 1bit PNG

Tux logo
Tux logo (source:

As you might have noticed, there are not many 1bit PNG images these days, so what you might want to do is to convert some existing image to what png2lp expects. It is easiest with SVG images, as they are quite flexible and you won’t loose much of a quality during conversion. In case of Oki 3321 and probably also other Oki printers from these days, maximum resolution I was able to get for portrait A4 page is 484×760 dots. However in practice I can see the image printed is not exactly proportional. This is a thing that should not be fixed in png2lp itself, as this might break more precise images. So, we have to manage this problem during conversion to input PNG image.

Let’s take well known Tux logo as an example. It can be downloaded from Wikipedia as SVG, then we can use ImageMagick to do all the transformations at once:

convert Tux_Mono.svg -resize 90%x106% -resize 484x760 -monochrome 484px-Tux_mono.svg.png
484px Tux logo
Tux converted to 1bit PNG and stretched to match printer’s distortion

After that we can try to pipe result of png2lp example above to our line printer device, that is usually /dev/usb/lp0 or /dev/lp0 on Linux. One note here: png2lp does not produce form feed character to allow potential embedding its results into bigger page. You have to write it manually or press button on your printer for it to give the page back to you!


Tux printed on Oki 3321
Printed Tux

As can be seen in the picture above, there is quite a huge margin on the bottom. There is not much that can be done with it, as if I try to write something there, page sensor disconnects and printer stops printing until new page is fed. In theory it is possible to override this behavior by pressing select button few times (every press allows printing one additional line).

Another thing we can see here is exceptional quality of the printout 🙂 With this, we can do even less, as this page was printed using so called “high quality” mode.

Output format

Finally, few words about format of the input data printer have to receive. They are based on escape codes, so at first may seem similar to ANSI escape codes used in terminals for e.g. coloring or presenting window-like experience with help of ncurses. Despite that, I don’t think any of these codes were standardized somehow. At least some part of them come from IBM printers, so may be compatible with many devices of different vendors, as IBM was the one creating standards at that time.

For printing in graphical mode there are two codes, I am aware of, that allows to print virtually everything: \eK and \eJ. Their formats are as follows:

struct K {
  uint8_t esc;
  uint8_t K;
  uint16_t columns;

struct K k = {'\033', 'K', htole16(columns)};

struct J {
  uint8_t esc;
  uint8_t J;
  uint8_t offset;

struct J j = {'\033', 'J', 0x18}; // 0x18 - experimentally found magic

Leave a Reply

Your email address will not be published. Required fields are marked *