I am learning C# and I am trying this making my homeworks on image processing examples. I am trying to read the pixel values along an horizontal line in a gray levels jpeg image. Some prints I made inform me that I am reading a Format8bppIndexed image (this was a kind of surprise to me, because my belief was that jpeg images do not use a palette at all). The code to load the image in the proper control in the windows form is something like this:
try
{
myImage = ( Bitmap ) Image.FromFile(imageName);
}
catch
{
MessageBox .Show("Unable to load the image" , Text, MessageBoxButtons .OK, MessageBoxIcon .Hand);
}
pictureBox1.Image = myImage ;
Then I try to read the pixel along an arbitrary straight path in the image listing them in a csv file when pressing a button. I assume that, being the image a gray level one, reading the red color is enough (is it? other color components are equal; using the getBrightness seems to me an overkill):
cursorStartx = 0;
cursorStarty = 256;
cursorEndx = myImage.Width;
cursorEndy = 256;
Color pixel;
StreamWriter fs = new StreamWriter( "pixels.csv" , true );
for (var i = 0; i < cursorEndx; i++)
{
pixel = myImage.GetPixel(i, cursorStarty);
fs.WriteLine( String .Format("{0}; {1}; {2}" , i, cursorStarty, pixel.R));
}
fs.Close();
When reading the cross section in the file I see values that make no sense at all: they are all multiple of 17 (???):
0; 256; 17
1; 256; 0
2; 256; 17
3; 256; 0
4; 256; 17
5; 256; 0
6; 256; 17
7; 256; 0
8; 256; 17
9; 256; 0
10; 256; 17
............
66; 256; 17
67; 256; 34
68; 256; 51
69; 256; 68
70; 256; 85
71; 256; 85
72; 256; 102
73; 256; 85
An histogram made in the same program shows clearly distinct count peaks at multiple of 17 (????)
Just to perform a reality check, I made something similar in Perl (I have a lot more experience in perl, I am just learning C#) using the module GD and, reading the pixel on the very same path in the same image, I get very different values.
First rows of the histogram drwan by imageJ are the following:
0 0
1 0
2 1
3 1
4 15
5 81
6 304
7 984
8 2362
9 206144
10 2582
11 1408
12 653
13 451
14 345
15 321
16 277
17 288
a quite different story.
Someone could possibly explain to me what horribly stupid kind of error I am doing?
Thanks a lot
----------------------------------- EDITED LATER --------------------------------------
Things are becoming intriguing. Reading the same image using Aforge image lab, written in C#, I got the same kind of error: the histogram shows discrete count at pixel values that are multiple of 17. Reading the image with ImageJ, written in Java, I get a correct histogram. Now I am trying to figure out if there is a way, using C#, to manage the image reading somehow, in order to be able to get the correct values, or this is a major issue and the alternatives are: to give up or to perform some sort of low level image reading (hard stuff for jpeg format...)
Multiples of 17 are actually quite common when converting 4bpp channels into 8bpp channels. 0x00, 0x11, 0x22 ... 0xFF are all multiples of 17.
Looks to me you have a quantized 16 colors grayscale image with dithering.
Related
Platform: Windows Mobile 6.5 Handheld
Language: C#
My problem: I'm being asked to capture a signature from the user and then send that signature to a printer to print on a receipt. I've successfully captured the image of the signature and have the byte array of the signature in memory but cannot seem to print it properly.
To get me started, I followed the blog here to get the Hex representation of the bitmap. However, this just printed out a very long receipt with the hex
representation of the signature. Code here instead of following the link:
private static string DrawBitmap(Bitmap bmp, int xPosition, int yPosition)
{
if (bmp == null)
throw new ArgumentNullException("bmp");
StringBuilder DataString = new StringBuilder();
//Make sure the width is divisible by 8
int loopWidth = 8 - (bmp.Width % 8);
if (loopWidth == 8)
loopWidth = bmp.Width;
else
loopWidth += bmp.Width;
//DataString.Append(string.Format("EG {0} {1} {2} {3} ", xPosition, yPosition));
DataString.Append(string.Format("EG 64 128 {0} {1} ", 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);
}
else
intensity = 0;
if (intensity >= 128)
currentValue |= bit;
bit = bit >> 1;
if (bit == 0)
{
DataString.Append(currentValue.ToString("X2"));
bit = 128;
currentValue = 0;
}
}//x
}//y
DataString.Append("\r\n");
return DataString.ToString();
}
After that failed, I found the CPCL programming guide for Zebra printers and followed the example on page 95 to print the little tile image. However, this did the same thing as the signature. Once that failed, I found that I needed to run the command: ! U1 setvar "device.languages" "zpl" before doing any EG commands; so I went ahead and did this but things took a bad turn here which end up forcing me to fully reset the printer and/or cleanboot the handheld because it causes a COM exception that crashes COM6 and the printer.
I have exhausted most if not all of the resources that I can think of and none of them have worked.
Does anyone have any other ideas or examples that could help me get this working?
Thanks
I found another CPCL programmers guide and it has this simple (test) example:
! 0 200 200 210 1
EG 2 16 90 45 F0F0F0F0F0F0F0F00F0F0F0F0F0F0F0F
F0F0F0F0F0F0F0F00F0F0F0F0F0F0F0F
FORM
PRINT
This should print a small checker board pattern.
The next example prints a PCX graphic:
PCX Commands
The PCX command gives a user the ability to send “.PCX” graphics formatted images to the printer. The
.PCX image MUST be encoded as a black and white image.
Format:
{command} {x} {y}
{data}
where:
{command}: PCX
{x}: X-coordinate of the top-left corner.
{y}: Y-coordinate of the top-left corner.
{data}: PCX image data.
Example:
! 0 200 200 500 1
PCX 0 30
<binary black and white pcx data stream>
FORM
PRINT
Example using a file (loaded previously the printers file system)
! 0 200 200 500 1
PCX 0 30 !<IMAGE.PCX
FORM
PRINT
If the printer has been switched to line printer mode, the command
! U1 PCX {x coordinate} {y coordinate} !< {filename.pcx}
for example
! U1 PCX 0 30 !< IMAGE.PCX
can be used to print monochrome PCX from file system.
Remeber that .NET is UTF-8 and so all commands and data has to be converted to ASCII before sending over a COM port. So do something like this:
Encoding ansi = Encoding.GetEncoding(1252);
byte[] buf = ansi.GetBytes(DataString);
System.IO.Ports.SerialPort sp = new System.IO.Ports.SerialPort("COM1:");
sp.Write(buf, 0, buf.Length);
for the PCX data just use the byte stream for the byte[] buffer.
I write program in C# but hope that C++ and C# in background exactly same.
What i want - take grayscaled image and separate colors over 127 and under 17 to separate images. If i simply get "white" colors and programmatically stretch them from range (127-255) to (0-255) like
// pseudocode
int min = 127, max = 255;
for(int x; x< width; x++)
pixels[x] = pixels[x]/(max-min) * max;
Then here will be not smooth interval.. I mean, that 127 converts to 0 but 128 converts to 2 and colors 1,3,5,... are not exist.
That is original image with alpha:image original
That is image with "extracted white":image original
That is image with "extracted black": snorgg.ru/patchwork/tst_black.png.
I don't clearly understand how it can be realized so exampe code will like:
{
im.MagickImage image = new im.MagickImage("c:/55/11.png");
im.MagickImage imageWhite = ExtractWhite(image);
im.MagickImage imageBlack = ExtractBlack(image);
}
....
public static im.MagickImage ExtractWhite(im.MagickImage img){
im.MagickImage result = new im.MagickImage(img);
?????
?????
return result;
}
thankы in advance ))
I think your calculation is wrong. You are confusing the input range with the output range. The input ranges from min to max and the output ranges from 0 to 255. It is a coincidence that your input max is equal to your output max (255).
If you want to stretch a value in the range of min ... max (= input range) to 0 ... 255 (= output range) then calculate this
int brightness = pixel[x];
if (brightness <= min) {
pixel[x] = 0;
} else if (brightness >= max) {
pixel[x] = 255;
} else {
pixel[x] = 255 * (brightness - min) / (max - min);
}
Where min >= 0 and max <= 255 and min < max.
First you have to make sure the brightness is within the range min ... max, otherwise your result will exceed the range 0 ... 255. You could also limit the range of the output afterwards, but in any case you have to make a range check.
Then subtract min from the brightness. Now you have a value between 0 and (max - min). By dividing by (max - min) you get a value between 0 and 1. Multiply the result by 255 and you get a value in the desired range 0 ... 255.
Also you must be aware of the fact that you are performing integer arithmetic. Therefore multiply by 255 first and then divide. If you start by dividing you get either 0 or 1 as intermediate result (because integer arithmetic does not yield decimals and the final result will either be 0 or 255 and all the gray tones get lost.
The effect you are seeing is called banding or posterisation. It is caused by making contrast stretches to data that is not sampled with sufficient bit-depth. As you only have 8-bit data, you only have 255 grey levels. If you stretch the 50 levels between 100-150 over a range of 255 levels, there will be gaps in your histogram around 5 levels wide. The solution is either to obtain 16-bit data, or make less drastic changes in the contrast.
Alternatively, if like me, you are a photographer, and more interested in the aesthetics of the image than its scientific accuracy, you can add a small amount of random noise to disguise and "smear over" the banding...
There is a nice description here.
I can also show you an example with ImageMagick, first we create two greyscale ramps (gradients), one 8-bit and one 16-bit, both ranging from brightness level 100 to 150 like this:
convert -depth 8 -size 100x500 gradient:"rgb(100,100,100)-rgb(150,150,150)" -rotate 90 gradient8.png
convert -depth 16 -size 100x500 gradient:"rgb(100,100,100)-rgb(150,150,150)" -rotate 90 gradient16.png
They look like this:
If I now stretch them both to the full range of 0-255 you will immediately see the banding effect in the 8-bit version, and the smoothness of the 16-bit version - which, incidentally, is the reason for using RAW format (12-14 bit) on your camera rather than shooting 8-bit JPEGs:
convert gradient8.png -auto-level out8.png
convert gradient16.png -auto-level out16.png
I alluded to using noise to redue the visibility of the banding effect, and you can do that using a technique like this:
convert out8.png -attenuate 0.3 +noise gaussian out.png
which gives you a less marked effect, somewhat similar to film grain:
I am not certain exactly what you are trying to do, but if you just want to spread the brightness levels from 127-255 over the full range of 0-255, you can do that simply at the command-line like this:
convert orig.png -level 50%,100% whites.png
Likewise, if you want the brightness levels from 0-17 spread over the range 0-255, you can do
convert orig.png -level 0,6.66667% blacks.png
I have a Windows Forms application that I am upgrading to target a newer .Net Framework. For one of my unit tests, a greyscale TIFF image is used to create a Bitmap. The pixel data is extracted and then run through an algorithm to determine the center of a fiducial that is in the image. On Windows XP, Visual Studio 2005, .Net Framework 2.0 the correct center is located and the test passes. On Windows 7 64-Bit, Visual Studio 2013, .Net Framework 4.0 the test fails because the center that it finds is off by a small amount.
I have inspected the pixel data on one machine to the next by debugging and looking at the Byte array. The pixel values on the XP machine are a little different than pixel values on the Windows 7 machine. It's the same exact image and the same code on both machines. One other thing is that this only occurs on greyscale images, all the color images pass and the ones I checked had the same pixel values. Lastly, the images in question appear to be the exact same width and height according the bitmap properties I inspected when debugging.
Here are the first 10 pixel values from the Windows XP machine:
[0] 94 byte
[1] 92 byte
[2] 92 byte
[3] 91 byte
[4] 92 byte
[5] 94 byte
[6] 93 byte
[7] 92 byte
[8] 90 byte
[9] 91 byte
[10] 91 byte
Here are the first 10 values from the Windows 7 machine:
[0] 85 byte
[1] 102 byte
[2] 85 byte
[3] 85 byte
[4] 85 byte
[5] 85 byte
[6] 102 byte
[7] 85 byte
[8] 85 byte
[9] 85 byte
[10] 85 byte
Here is the test method:
public void Test_CenteringBottomMiddle8BitMono()
{
int centerX, centerY;
DoCenter(path + "BottomMiddle - 8 - Mono.tif", out centerX, out centerY, "npf02");
CheckOffsets(centerX, centerY, 0, centerOffsetPixels);
}
And the DoCenter Method:
private void DoCenter(string filename, out int centerX, out int centerY, string fiducialType)
{
if (File.Exists(filename))
{
Bitmap bmp = new Bitmap(filename);
CenterCalibration center = new CenterCalibration();
byte[] bytes = ImageProcessing.GetByteStream(bmp);
int numChan = 1;
if (bmp.PixelFormat == System.Drawing.Imaging.PixelFormat.Format24bppRgb)
{
numChan = 3;
}
center.GetCenterPoint(bytes, bmp.Width, bmp.Height, numChan, out centerX, out centerY, fiducialType);
Console.WriteLine("{0} (Center Coords): {1},{2}", filename, centerX, centerY);
}
else
{
string msg = string.Format("File: {0} Does Not Exist.", filename);
throw new FileNotFoundException(msg);
}
}
And the GetByteStream method:
public static byte[] GetByteStream(Bitmap bmp)
{
DateTime startFunctionTimer = DateTime.Now;
DateTime endFunctionTimer;
DateTime startTimer, endTimer;
startTimer = DateTime.Now;
int numChan = 1;
if (bmp.PixelFormat == System.Drawing.Imaging.PixelFormat.Format24bppRgb)
{
numChan = 3;
}
System.Drawing.Imaging.BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);
byte[] dst = new byte[bmp.Width * bmp.Height * numChan];
byte[] src = new byte[bmpData.Stride * bmp.Height];
startTimer = DateTime.Now;
System.Runtime.InteropServices.Marshal.Copy(bmpData.Scan0, src, 0, dst.Length);
for (int i = 0; i < bmp.Height; i++)
{
Buffer.BlockCopy(src, (i * bmpData.Stride), dst, (i * bmp.Width * numChan), bmp.Width * numChan);
}
endTimer = DateTime.Now;
bmp.UnlockBits(bmpData);
TimeSpan functionInterval, timerInterval;
timerInterval = endTimer - startTimer;
endFunctionTimer = DateTime.Now;
functionInterval = endFunctionTimer - startFunctionTimer;
// Console.WriteLine("GetByteStream() Total = {0}, CopyTimer = {1}", functionInterval.TotalMilliseconds, timerInterval.TotalMilliseconds);
return dst;
}
Things I have tried:
Targeting the original .Net Framework on the newer machine.
Turning on and off color correction when creating the Bitmap object.
Confirming the pixel format is the same on both machines. Format8BppIndexed
I am pretty sure it has to do with the pixel values being different rather than the algorithm that actually finds the fiducial, but I could be wrong. I first would just like to know why the pixel values are showing up differently.
Thanks for any help/clues.
**UPDATE**
I have made some progress thanks to TaW's suggestions in the comments. He asked if the TIF was compressed.
When I look at the TIF Information with IrfanViewer it showed that it is using LZW compression. I saved a copy of the Greyscale TIF in question and chose to not compress the image. When I debugged the unit test with this image I was able to see the values that I was hoping for and the unit test passed. It appears that the compression is affecting the pixel values and something is working a little differently in my new setup.
What's interesting is that this difference in pixel values does not appear to happen on the color images, even though they are compressed with LZW also.
Any further thoughts on the reason this might be different on the newer setup?
I am printing the mono chorme bit map image on thermal printer where i am able to print the image but at rightmost, one vertical line is getting printed. (The line is from Top right to bottom right with nearly 2mm thick)
Bitmap image = new Bitmap(imagePath, false);
int imageDepth = System.Drawing.Bitmap.GetPixelFormatSize(image.PixelFormat);
Rectangle monoChromeBitmapRectangle = new Rectangle(0, 0, image.Width, image.Height);
BitmapData monoChromebmpData = null;
int stride = 0;
monoChromebmpData = image.LockBits(monoChromeBitmapRectangle, ImageLockMode.ReadOnly, resizedImage.PixelFormat);
IntPtr ptr = monoChromebmpData.Scan0;
stride = monoChromebmpData.Stride;
int numbytes = stride * image.Height;
byte[] bitmapFileData = new byte[numbytes];
Marshal.Copy(ptr, bitmapFileData, 0, numbytes);
image.UnlockBits(monoChromebmpData);
//Invert bitmap colors
for (int i = 0; i < bitmapFileData.Length; i++)
{
bitmapFileData[i] ^= 0xFF;
}
StringBuilder hexaDecimalImageDataString = new StringBuilder(bitmapFileData.Length * 2);
foreach (byte b in bitmapFileData)
hexaDecimalImageDataString.AppendFormat("{0:X2}", b);
return hexaDecimalImageDataString;
Here i am converting the mono chrome bitmap image to byte array and from byte array to hexadecimal string.
i googled in forums but this kind of error is not discussed. (May be i am doing silly mistake)
Can any one suggest where exactly i am making the mistake.
Thanks in advance.
Cheers,
Siva.
Your are returning monoChromebmpData.Stride * image.Height bytes, i.e. each line in the image will be exactly monoChromebmpData.Stride * 8 pixels wide - but probably the original image has a pixel width that is less than that, hence the extra vertical line on the right.
Try something like this:
byte[] masks = new byte[]{0xff, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f};
int byteWidth = (image.Width+7)/8;
int nBits = imageWidth % 8;
byte[] actualBitmapFileData = new byte[byteWidth*image.Height];
int yFrom = 0;
for (int y=0; y<image.Height; y++) {
for (int x=0; x<byteWidth-1; x++) {
actualBitmapFileData[y*byteWidth + x] = (bitmapFileData[yFrom + x] ^ 0xFF);
}
int lastX = byteWidth - 1;
actualBitmapFileData[y*byteWidth + lastX] = (bitmapFileData[yFrom + lastX] ^ 0xFF) & masks[nBits];
yFrom += stride;
}
it creates an actualBitmapFileData array for bitmapFileData with of the correct size.
Note that the last byte of every line would contain only nBits pixels - and so needs to be 'masked' to clear out the extra bit not corresponding with any pixel. This is done by & masks[nBits], where masks is an array of 8 bytes with the 8 masks to use. The actual values of the mask depend on how the printer works: you might need to set the extra bits to 0 or to 1, and the extra bits can be the most-significant or the least-significant ones. The mask values used above assume that the most significant bits are rendered to the right, and that the masked bits should be set to 0. Depending on how the printer works it might be necessary to swap the bits and/or set the masked bits to 1 instead than zero (complementing the mask and using | instead than &)
For performance reasons each horizontal row in a Bitmap is buffered to a DWORD boundary (see this answer for more details). So if your Bitmap's width multiplied by it's bits-per-pixel(bpp) is not divisible by 32 (DWORD = 32bits) then it's padded with extra bits. So a 238x40 1bpp Bitmap has a memory foot print of 8 DWORDs per row or 256 bits.
The BitmapData object's Stride property is the number of bytes that each row of your bitmap consumes in memory. When you capture the Byte Array, you're capturing that padding as well.
Before you convert the byte array to hex you need to trim the buffer off the end. The following function should do that nicely.
public static byte[] TruncatePadding(byte[] PaddedImage, int Width, int Stride, int BitsPerPixel)
{
//Stride values can be negative
Stride = Math.Abs(Stride);
//Get the actual number of bytes each row contains.
int shortStride = (int)Math.Ceiling((double)(Width*BitsPerPixel/8));
//Figure out the height of the image from the array data
int height = PaddedImage.Length / Stride;
if (height < 1)
return null;
//Allocate the new array based on the image width
byte[] truncatedImage = new byte[shortStride * height];
//Copy the data minus the padding to a new array
for(int i = 0; i < height; i++)
Buffer.BlockCopy(PaddedImage,i*Stride,truncatedImage,i*shortStride,shortStride);
return truncatedImage;
}
The comments from MiMo and MyItchyChin helped me alot on resolving the issue.
The problem is getting the extra line at the end. So technically, on printing the each row of image, last few byte information is incorrect.
The reason for getting this problem is, the image size could be anything, but whne it sends to printer, the byte width should be divisible by eight. In my case my printer expects the bytewidth as input so i must be careful on passing the image.
Assume i have image 168x168 size.
byteWidth = Math.Ceiling(bitmapDataWidth / 8.0);
so the byteWidth is 21 here, As per printer expectation i did Left shift operation to 24 which is diviseble by 8, so virtually i increased the size of image by 3bytes and then started reading the byte information. The line i am talking about is that extra 3 bytes. Since no data is there, the black line is getting printed.
I wrote the logic in such a way, where byte array doesnot effect with shift operations hence it worked for me.
Early days for me in image processing, So please ignore, if i made a silly mistake and explaining the solution here.
I am printing using the Windows Compact Framework to a Zebra belt printer using the OpenNetCF serial port class and CPCL. The printed label is pretty much as it should be, but the barcode value is not printing beneath the barcode as it should.
I create an ArrayList of commands to be sent the printer and then pass them one at a time to the serial port. If the controls that supply the values are empty, I use some dummy data, like so:
private void btnPrint_Click(object sender, System.EventArgs e)
{
string listPrice = txtList.Text;
if (listPrice.Trim() == string.Empty)
{
listPrice = "3.14";
}
string description = txtDesc.Text;
if (description.Trim() == string.Empty)
{
description = "The Life of Pi";
}
string barcode = txtUPC.Text;
if (barcode.Trim() == string.Empty)
{
barcode = "01701013992";
}
ArrayList arrList = new ArrayList();
arrList.Add("! 0 200 200 120 1\r\n"); // replace 120 with label height if different than 1.25"/120 pixels (at 96 pixels per inch)
arrList.Add("RIGHT\r\n");
arrList.Add(string.Format("TEXT 0 5 0 0 {0}\r\n", listPrice));
arrList.Add("LEFT\r\n");
arrList.Add(string.Format("TEXT 0 0 0 52 {0}\r\n", description));
arrList.Add("CENTER\r\n");
arrList.Add("BARCODE-TEXT 0 0 5\r\n");
arrList.Add(string.Format("BARCODE 128 1 1 50 0 77 {0}\r\n", barcode));
arrList.Add("FORM\r\n");
arrList.Add("PRINT\r\n");
PrintUtils pu = new PrintUtils();
pu.PrintLabel(arrList);
}
public void PrintLabel(ArrayList linesToSend)
{
using (SerialPort serialPort = new SerialPort())
{
serialPort.BaudRate = 19200;
serialPort.Handshake = Handshake.XOnXOff;
serialPort.DataBits = 8;
serialPort.Parity = Parity.None;
serialPort.StopBits = StopBits.One;
serialPort.PortName = "COM1:";
serialPort.Open();
Thread.Sleep(500); //this may not even be necessary and, if so, a different value may be better
foreach (string line in linesToSend)
{
serialPort.Write(line);
}
serialPort.Close();
}
}
...the problem is that the label (when I allow the dummy data to print) should be:
3.14
The Life of Pi
<barcode here>
01701013992
...and here's what is really printing:
3.14
The Life of Pi
<barcode here>
[blank]
So the problem is that the barcode as text ("01701013992") is not printing beneath the barcode.
Does anybody know why this is occurring even though I've got a BARCODE-TEXT command in there, and how to rectify it?
UPDATE
A key piece of info came my way, namely that the label height (in my case) should be 254, not 120 (for my 1.25" in height label, I was calculating based on 96 pixels == 1 inch, but in actuality this particular printer is 203 dpi, so 1.25 X == 254 (more precisely 253.75, but 254 is close enough).
So the code has changed to this:
// Command args (first line, prepended with a "!": horizontal (X) pos, resolution, resolution, label height, copies
// TEXT args are: fontNumber, fontSizeIdentifier, horizontal (X) pos, vertical (Y) pos
// BARCODE args are: barcodeType, unitWidthOfTheNarrowBar, ratioOfTheWideBarToTheNarrowBar, unitHeightOfTheBarCode,
// horizontal (X) pos, vertical (Y) pos, barcodeValue
// BARCODE-TEXT args are: fontNumber, fontSizeIdentifier, space between barcode and -text
// 1 inch = 203 dots (Zebra QL220 is a 203 dpi printer); font 4,3 == 90 pixels; font 2,0 == 12 pixels
arrList.Add("! 0 200 200 254 1\r\n"); // 203 dpi X 1.25 = 254
arrList.Add("RIGHT\r\n");
arrList.Add(string.Format("TEXT 4 3 0 0 {0}\r\n", listPrice));
arrList.Add("LEFT\r\n");
arrList.Add(string.Format("TEXT 2 0 0 100 {0}\r\n", description));
arrList.Add("BARCODE-TEXT 2 0 5\r\n");
arrList.Add("CENTER\r\n");
arrList.Add(string.Format("BARCODE 128 1 1 50 0 120 {0}\r\n", barcode));
arrList.Add("FORM\r\n");
arrList.Add("PRINT\r\n");
...but I'm STILL not seeing the description label - except for a lonely "P" below the "3" and the "." in the price.
Are my calculations wrong, or what?
Here's what I'm thinking I have:
Label is 254 dots/1.25" high.
First line starts at YPos 0 and prints "3.14" in a 90 pixel font, right-aligned. That prints fine.
Second line starts at YPos 100 (10 dots below the 90-dot first line), left-aligned. All I see is the aforementioned "P" in what seems to be the right size.
Third line is the barcode, at YPos (120), centered; prints fine
Fourth/final line is the barcode as text beneath the barcode proper, centered; prints fine.
NOTE: I can't put a bounty on this yet, but anybody who solves it I will award 100 points as soon as I'm able (in two days, I reckon).
It turns out that the problem was that I was using font # 2 in order to get a font size of 12 (it is the only font that provides that size). The problem with font # 2 is that it is "OCR-A" and as such only prints certain characters. In the string I was passing as a test ("The Life of Pi", to go along with the list price of 3.14), the only character it recognizes in that string is P. So that's why it's the only one I saw.
I had to increase my font size to the next available, namely 24, using font #5 (Manhattan) or 7 (Warwick).
"mk" from zebra provided me with this information ("The OCR font is a special font that doesn’t include all characters that you are trying to print.").
If you look at Appendix D in the CPCL programming manual, it does show font #2 as being "OCR-A," but it didn't dawn on me that that meant it's character set precluded most alpha characters. Even if that's obvious to some, it seems to me that should be emphasized in the manual: When printing text, don't use font #2!
Note: Font #6 (MICR) is also to be avoided for text.