4

Language: VB .NET 2010 Win Form

Scope: I have developed a label printing program that is intended to print custom labels to a zebra printer. I was having problems with clarity from the printer when I tried to print the entire label as an image therefore I am trying to store all of the text from labels to an array, clear the labels out, send the leftover image to the printer, and overlay new text based on the stored array. This result is intended to send text to the printer rather than an image.

Problem: The resulting label is very clear for the text as I want it however I was having troubles with alignment for my printing method. For tests I have the original image being displayed with the overlay text on top to which they should align perfectly or close within reason. When doing this the result is that they are not aligned.

Testing:

    Dim g2 As Graphics
    g2 = Form1.PictureBox2.CreateGraphics
    g2.CompositingQuality = Drawing2D.CompositingQuality.HighQuality
    g2.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
    g2.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
    g2.Clear(Color.White)

    g2.DrawImage(largeimage, New Point(0, 0))

    Dim myBrush As Brush
    Dim i As Integer = 0
    Do Until i = label_array.Count - 1
        myBrush = New SolidBrush(label_array(i).ForeColor)
        g2.DrawString(label_array(i).Text, label_array(i).Font, myBrush, label_array(i).Location)
        i = i + 1
    Loop

Within the printing method I used the above method to output the overlay and image to a picture box. When doing this it WORKS, however...

    e.Graphics.CompositingQuality = Drawing2D.CompositingQuality.HighQuality
    e.Graphics.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
    e.Graphics.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
    e.Graphics.Clear(Color.White)

    e.Graphics.DrawImage(largeimage, New Point(0, 0))

    i = 0
    Do Until i = label_array.Count - 1
        myBrush = New SolidBrush(label_array(i).ForeColor)
        e.Graphics.DrawString(label_array(i).Text, label_array(i).Font, myBrush, label_array(i).Location)
        i = i + 1
    Loop

..doing it to the printing graphic shown above results in misalignment...

Any ideas are welcome as I will try about anything. I am guessing that the printing graphic is doing something additional from a normal graphic that I am not aware of.


I did notice that TextRenderer.DrawText improves results compared to DrawString. However when using this the result seems to be scaled from the original by some unknown scaling factor...

Below is the resulting overlay on top of the original: enter image description here

Eric F
  • 899
  • 2
  • 21
  • 45
  • Before you create a new brush, don't forget to dispose the old one. – John Alexiou Feb 25 '13 at 16:24
  • Note that the printing `Rectangle` always starts from `(0,0)` on the screen, but not on the printer. Check the print rectangle `.Left` and `.Top` properties and offset the text accordingly. – John Alexiou Feb 25 '13 at 16:25
  • Margin bounds = (100,100) and Page bounds = (0,0) so I don't see a correlation really. – Eric F Feb 25 '13 at 17:41

2 Answers2

2

You are re-discovering a well-known problem with Graphics.DrawString(), it is not accurate. The most graphic demonstration of this problem is this sample Winforms form:

public partial class Form1 : Form {
    public Form1() {
        InitializeComponent();
    }
    protected override void OnPaint(PaintEventArgs e) {
        e.Graphics.DrawString("Hiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii", 
            this.Font, Brushes.Black, 0, 0);
    }
}

Which looks like this:

enter image description here

No amount of magic is ever going to get you overlap on the part of the string where the spacing between the letters i suddenly changes. GDI+ was Microsoft's first attempt at resolution independent text rendering and it didn't work out well. It is in good company, WPF was the second attempt and it got a lot of flak for fuzzy output. Solved in a way that was similar to the way it got fixed in .NET 2.0 for Winforms, you should always render text to a window with TextRenderer.DrawText(). Which makes no attempt to make rendered text scale smoothly, it always prefers fitting to the pixel grid. Same thing with WPF's ideal vs display rendering modes, added in .NET 4.

There are significant other problems with your approach, the way the text is rendered is significantly unsuitable for a printer. You can see that when you zoom in you can see the pixels, SysInternals' ZoomIt utility is good for that. I zoomed in on the letter a in your right-hand side graphic:

enter image description here

Note the reddish and bluish pixels at the fringe of the letter shape. This is an anti-aliasing technique called ClearType, it enhances the perceived resolution of LCD monitors. It only works well on LCD panels, it does not work on a printer which doesn't have the same stripe pattern. On paper, the letter will just look fuzzy with a colored fringe. Albeit that it is not quite as bad on a Zebra printer since it doesn't supporting printing in color.

These are artifacts that are specific to rendering text to a monitor, devices that have a rather poor resolution. Pixel grid fitting and ClearType are tricks to make the text look decent. Typical monitors have no more than about 120 pixels per inch. Albeit that this is finally improving with Apple's push for "retina" displays. A company that has a stake in high resolution monitors, they traditionally rendered text in "ideal" mode.

These rendering tricks are completely inappropriate for a printer, a device that has much higher resolution. 600 pixels per inch is typical, easily 5 times better than a monitor. By drawing a picturebox to the printer you are in effect wasting the improvements you'll get when drawing directly to the printer. They difference is very significant and easy to see with the unaided eye. Text looks much more beautiful and crisp when you render text at 600 dpi. Text that was originally rendered for 120 dpi and enlarged for a printer looks "blobby" and crude.

Always use the PrintDocument class to draw to the printer.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
1

A long time ago I wrote an application for printing labels to a Zebra printer, so maybe I can help. Back in the day we only had the ZPL2 API, and only printed text and simple graphics, none of this image stuff. Anyway...

My suspicion is that it is a scaling issue. That might explain why your original image is fuzzy -- perhaps the printer is automatically attempting to scale it to fit the label, and doing a poor job of it? And of course that would explain the resulting labels shown above.

What strikes me about your code above is how you get the Graphics object. One comes from the picture box; the other presumably from the printer. Keep in mind that the Graphics object is device dependent. That is, it's properties and how it prints depends on whether you're printing to a particular control (different controls can have different scaling) or to a particular printer.

Have you tried using your existing code to print to a laser printer? It may be interesting to see the results. Also see Graphics properties such as ScaleTransform.

Adventure
  • 189
  • 1
  • 7
  • So I am assuming that you are talking about the TextRenderer.DrawText method and trying to check the scale of that vs the printer graphics scale? I will try that and post the results. – Eric F Mar 01 '13 at 18:53