Printing bitmaps using CPCL

I've had no end of grief trying to print a PCX to a Zebra Printer using the CPCL printer language. Silly me, didn't notice the EG command (expanded graphics) so there was no need to convert my BMP to a PCX and then struggle with binary data.

I still had a bit of grief working out how to print using the EG command because the documentation is quite frankly crap. The expected command format is

EG {WidthInBytes} {HeightInPixels} {XPos} {YPos} {Data}\r\n

The printer expects a 1 bit pixel matrix. So if pixel(0, 0) is set you will set "80" in the data. If pixel(0, 0) is set and pixel (7, 0) is also set you would sent "81". Basically what you need to do is to read each set of 8 horizontal pixels and then use bit operations to create a byte value 0..255, and then output this as hex 00..FF.

Here's the routine :-)

  public void DrawBitmap(Bitmap bmp, int xPosition, int yPosition)
if (bmp == null)
throw new ArgumentNullException("bmp");

//Make sure the width is divisible by 8
int loopWidth = 8 - (bmp.Width % 8);
if (loopWidth == 8)
loopWidth = bmp.Width;
loopWidth += bmp.Width;

DataString.Append(string.Format("EG {0} {1} {2} {3} ", loopWidth / 8, bmp.Height, xPosition, yPosition));

for (int y = 0; y < bmp.Height; y++)
int bit = 128;
int currentValue = 0;
for (int x = 0; x < loopWidth; x++)
int intensity;

if (x < bmp.Width)
Color color = bmp.GetPixel(x, y);
intensity = 255 - ((color.R + color.G + color.B) / 3);
intensity = 0;

if (intensity >= 128)
currentValue |= bit;
bit = bit >> 1;
if (bit == 0)
bit = 128;
currentValue = 0;


Brendon said...

Hi Peter, i was delighted to find you snippet for 'pcx' to 'bitmap' conversion.

As I'm a VB.net CF2 developer, I am attempting to migrate your code to VB.

I pretty much have all but the 'for..next' loop and wondered could you help please.

// VB.Net Snippet //
Private Function GetImagePixelsAsHex(ByVal mybmp As Bitmap, ByVal xPosition As Integer, ByVal yPosition As Integer) As String

Dim result As StringBuilder = New StringBuilder()

' Make sure the width is divisible by 8
Dim loopWidth As Integer
loopWidth = (8 - (mybmp.Width Mod 8))

If loopWidth = 8 Then
loopWidth = mybmp.Width
loopWidth += mybmp.Width
End If

result.Append(String.Format("EG {0} {1} {2} {3} ", loopWidth / 8, mybmp.Height, xPosition, yPosition))

' For...Next Loop goes here

Return result.ToString()

End Function

Brendon said...

One further comment.

Have you seen the following:

This could become very interesting for mobile printer developers.

Ciaran said...

Hi Peter,

Great code, exactly what I need!! The only problem is I can't make out the c# code at the start of the for loop, it seems to be formatted wrong? Could you repost this if you have a chance?

Many thanks,

Anonymous said...

Thanks!! This was just what I was looking for.

seth said...

Peter... Sweet!!!
thank you...
I owe you a beer.

Anonymous said...

Good stuff Peter, I just checked this out because I was pulling my hair out with, like you say, the crap documentation from zebra.

On another note to anybody who stumbles upon this, there is also the compressed-graphics command which is twice as efficient as it requires the exact bytes to be sent to the printer rather than having to encode them as a hex encoded string.

MemoryStream s = new MemoryStream();

// Replace

// With

Then write the buffer directly to the port after you've written the CG graphics command.

Deepa said...

Thanks Peter,
Its working fine.

But i want to print Text along with my image, i tried using Text Command but it is not working.

Hope u will suggest some method to print Text and image together?

Thanks in Advance.

Owais Khan said...

Thanks a lot. Please read following article where complete source code for Android port is available. I can't put here due to size limitation of Comments Box.


Uğraş Bay said...

Thanks Peter
It is a good solution.

But it has a performance penalty on Windows Mobile Device. For a bitmap with size 560x200, it takes almost 10 seconds to complete DrawBitmap routine.

Can any improvement be done in your code?