3

I need to create a small, simple TIFF image that is just black text on a white background, as part of a support library we use across several of our products. (Think a simple receipt or deposit ticket, that kind of thing). I would prefer not to have to use any third-party libraries, to avoid requiring us to add additional dependencies to all of the existing products that use the library.

I have almost gotten the problem solved using just WPF, based on the code from this question. The TIFF is created and rendered properly, but the text quality is horrible. As far as I can determine, the problem is that RenderTargetBitmap, at least the way I'm using it, isn't doing any kind of anti-aliasing, isn't using ClearType, or otherwise is just really bad at rendering plain text, e.g.:

Sample Rendered Text

I've read similar questions, such as this one or this one, but they all seem to discuss how to capture text from a visual control or window handle without reducing the quality. I'm doing this entirely off-screen, so I'm not clear if/how I can apply those solutions to my problem.

What I'm currently doing is:

var visual = new DrawingVisual();
using (var draw = visual.RenderOpen())
{
    // 'background' is a pre-loaded blank white bitmap
    draw.DrawImage(background, new Rect(0, 0, IMG_WIDTH, IMG_HEIGHT));

    var font = new Typeface("Segoe UI");
    Func<string, FormattedText> format = s => new FormattedText(
        s,
        CultureInfo.CurrentUICulture, 
        FlowDirection.LeftToRight, 
        font, 
        16, 
        Brushes.Black);

    draw.DrawText(format("Some text"), new Point(HEADER_LEFT, HEADER_TOP);

    // Call draw.DrawText a whole bunch more times.
        .
        .
        .
}

var render = new RenderTargetBitmap(IMG_WIDTH, IMG_HEIGHT, IMG_DPI, IMG_DPI, PixelFormats.Default);
render.Render(visual);

var encoder = new TiffBitmapEncoder()
{
    Compression = TiffCompressOption.Ccitt4
};
encoder.Frames.Add(BitmapFrame.Create(render));

var stream = new MemoryStream();
encoder.Save(stream);

Is there something else I can put in there to get the text quality better than what it is?

Community
  • 1
  • 1
Michael Edenfield
  • 28,070
  • 4
  • 86
  • 117
  • So, bitonal is a requirement. Is the choice of font a requirement as well? For small, bitonal images, I wonder if bitmap fonts (etc) aren't the best choice if you can constrain your use case enough - what sizes do you need? – J Trana Oct 20 '15 at 03:41
  • no, the font was arbitrary, I can pick anything that has a roughly 10-14 point size. I'll try some others. – Michael Edenfield Oct 20 '15 at 10:41

1 Answers1

1

Don't use Ccitt4, it's for black-and-white images only. Try Default instead.

UPDATE:

It's a TT font and you're trying to render it at a small size, no matter what you do it's going to look pretty crap. Still, if you insist then don't use a visual. Screen elements use hardware acceleration, when you use them with a render target as you are here it has to fall back to a software approach which isn't always pixel-for-pixel the same. And either way you always get at least some aliasing. If you really do want proper bitonal then you'll have to use a Graphics object which will use GDI:

var bitmap = new Bitmap(512, 256);
var graphics = Graphics.FromImage(bitmap);
graphics.Clear(System.Drawing.Color.White);
var font = new Font("Segoe UI", 16);
graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit;
graphics.DrawString("Hello World", font, System.Drawing.Brushes.Black, 0, 0);


ImageCodecInfo myImageCodecInfo = GetEncoderInfo("image/tiff");
var myEncoderParameters = new EncoderParameters(1);
myEncoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Compression, (long)EncoderValue.CompressionCCITT4);
bitmap.Save(@"myimage.tif", myImageCodecInfo, myEncoderParameters);
graphics.Dispose();


    private static ImageCodecInfo GetEncoderInfo(String mimeType)
    {
        int j;
        ImageCodecInfo[] encoders;
        encoders = ImageCodecInfo.GetImageEncoders();
        for (j = 0; j < encoders.Length; ++j)
        {
            if (encoders[j].MimeType == mimeType)
                return encoders[j];
        }
        return null;
    }

Result (which SO is blurring a bit itself):

enter image description here

Mark Feldman
  • 15,731
  • 3
  • 31
  • 58