Rich Text Box printing ignores GDI+ rendering hints in WinXP but works in Win2000?

M

Mike A.

I have an application that needs to save 2-color bitmaps from a rich
text box (RTB) using the EM_FORMATRANGE Message via interOp to get a
rendering of the RTB to a BMP surface. It's based from examples on
the web that tell how to properly "print" a RichTextBox.

I need the text bitmap to be in just two colors (foreground and
background) and so I set my Graphics object options to to do away with
anti-aliasing and smoothing, and set interpolation and compositing to
the lowest possible.

When I run a pilot of the app I have in mind, it works perfect in
Win2k and Win2k Advanced Server. However, it's a different story when
It runs on WinXP or Win2003. The same app shows a bitmap with
undesired Anti-aliasing.

Image links, view them in zoom mode to compare the anti-aliasing
For the Win2k:
www.hotblue.com/%7Etrekblazer/images/testFromWin2000.bmp
For the Win2003:
www.hotblue.com/%7Etrekblazer/images/testFromXP.bmp

My question, is there a setting I forgot to include for handling
"no-aliasing" in WindowsXP and Win2003 RichTextBox printing via
interop? Does this have something to do with XP themes?

Thanks in advance.

P.S.
The code I use follows, the processing is done in the btnSave_Click
method and SendMessage calls are done on the "rtb" RichTextBox
control.

////////////////////////////////// Code ////////////////////////
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Drawing.Printing;
using System.Drawing.Imaging;
using System.Drawing.Text;
using System.Drawing.Drawing2D;
using System.Data;
using System.Runtime.InteropServices;

namespace textRtfPrint
{
public class Form1 : System.Windows.Forms.Form
{

protected static Int32 HundredthInchToTwips(int n)
{
return (Int32)(n*14.4);
}

protected static Int32 TwipstoHundrethInch(int n)
{
return (Int32)(n/14.4);
}


[ StructLayout( LayoutKind.Sequential )]
public struct STRUCT_RECT
{
public Int32 left;
public Int32 top;
public Int32 right;
public Int32 bottom;


public STRUCT_RECT(Rectangle aRect, bool convert)
{
if (convert)
{
left = HundredthInchToTwips(aRect.Left);
top = HundredthInchToTwips(aRect.Top);
right = HundredthInchToTwips(aRect.Right);
bottom = HundredthInchToTwips(aRect.Bottom);

}
else
{
left = aRect.Left;
top = aRect.Top;
right = aRect.Right;
bottom = aRect.Bottom;
}
}
}
[ StructLayout( LayoutKind.Sequential )]
public struct STRUCT_CHARRANGE
{
public Int32 cpMin;
public Int32 cpMax;

public STRUCT_CHARRANGE(Int32 iMin, Int32 iMax )
{
cpMin = iMin;
cpMax = iMax;
}
}


[ StructLayout( LayoutKind.Sequential )]
public struct STRUCT_FORMATRANGE
{
public IntPtr hdc;
public IntPtr hdcTarget;
public STRUCT_RECT rc;
public STRUCT_RECT rcPage;
public STRUCT_CHARRANGE chrg;
}

[ StructLayout( LayoutKind.Sequential )]
private struct CHARFORMATSTRUCT
{
public int cbSize;
public UInt32 dwMask;
public UInt32 dwEffects;
public Int32 yHeight;
public Int32 yOffset;
public Int32 crTextColor;
public byte bCharSet;
public byte bPitchAndFamily;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=32)]
public char[] szFaceName;
}

[DllImport("user32.dll", SetLastError=true)]
private static extern Int32 SendMessage(IntPtr hWnd, Int32 msg,
Int32 wParam, IntPtr lParam);

[DllImport("user32.dll", SetLastError=true)]
private static extern Int32 SendMessage(IntPtr hWnd, Int32 msg,
Int32 wParam, ref CHARFORMATSTRUCT lParam);

private const float MM_PER_INCH = 25.4f;
private const float TWIPS_PER_POINT = 20.0f;
private const float POINTS_PER_INCH = 72.0f;
private const float TWIPS_PER_INCH = ( TWIPS_PER_POINT *
POINTS_PER_INCH );
private const float MM_PER_TWIP= ( MM_PER_INCH / TWIPS_PER_INCH
);


// Windows Messages defines
private const Int32 WM_USER = 0x400;
private const Int32 EM_FORMATRANGE = WM_USER+57;
private const Int32 EM_GETCHARFORMAT = WM_USER+58;
private const Int32 EM_SETCHARFORMAT = WM_USER+68;

// Defines for EM_SETCHARFORMAT/EM_GETCHARFORMAT
private const Int32 SCF_SELECTION = 0x0001;
private const Int32 SCF_WORD = 0x0002;
private const Int32 SCF_ALL = 0x0004;

// Defines for STRUCT_CHARFORMAT member dwMask
private const UInt32 CFM_BOLD = 0x00000001;
private const UInt32 CFM_ITALIC = 0x00000002;
private const UInt32 CFM_UNDERLINE = 0x00000004;
private const UInt32 CFM_STRIKEOUT = 0x00000008;
private const UInt32 CFM_PROTECTED = 0x00000010;
private const UInt32 CFM_LINK = 0x00000020;
private const UInt32 CFM_SIZE = 0x80000000;
private const UInt32 CFM_COLOR = 0x40000000;
private const UInt32 CFM_FACE = 0x20000000;
private const UInt32 CFM_OFFSET = 0x10000000;
private const UInt32 CFM_CHARSET = 0x08000000;

// Defines for STRUCT_CHARFORMAT member dwEffects
private const UInt32 CFE_BOLD = 0x00000001;
private const UInt32 CFE_ITALIC = 0x00000002;
private const UInt32 CFE_UNDERLINE = 0x00000004;
private const UInt32 CFE_STRIKEOUT = 0x00000008;
private const UInt32 CFE_PROTECTED = 0x00000010;
private const UInt32 CFE_LINK = 0x00000020;
private const UInt32 CFE_AUTOCOLOR = 0x40000000;

private System.Windows.Forms.RichTextBox rtb;
private System.Windows.Forms.Button btnSave;
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;

public Form1()
{
InitializeComponent();
}

protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}

#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.rtb = new System.Windows.Forms.RichTextBox();
this.btnSave = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// rtb
//
this.rtb.BackColor = System.Drawing.Color.Red;
this.rtb.Font = new System.Drawing.Font("Microsoft Sans Serif",
26F, System.Drawing.FontStyle.Regular,
System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
this.rtb.ForeColor = System.Drawing.Color.White;
this.rtb.Location = new System.Drawing.Point(16, 40);
this.rtb.Name = "rtb";
this.rtb.Size = new System.Drawing.Size(256, 64);
this.rtb.TabIndex = 0;
this.rtb.Text = "Text";
//
// btnSave
//
this.btnSave.Location = new System.Drawing.Point(200, 120);
this.btnSave.Name = "btnSave";
this.btnSave.Size = new System.Drawing.Size(72, 24);
this.btnSave.TabIndex = 1;
this.btnSave.Text = "save";
this.btnSave.Click += new
System.EventHandler(this.btnSave_Click);
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(292, 189);
this.Controls.AddRange(new System.Windows.Forms.Control[] {

this.btnSave,

this.rtb});
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);

}
#endregion

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new Form1());
}


public int GetDrawRectangle(ref STRUCT_FORMATRANGE sFR,
bool measureOnly)
{

// Non-Zero wParam means render, Zero means measure
Int32 wParam = (measureOnly ? 0 : 1);

// Allocate memory for the FORMATRANGE struct and
// copy the contents of our struct to this memory
IntPtr lParam = Marshal.AllocCoTaskMem( Marshal.SizeOf( sFR ) );
Marshal.StructureToPtr(sFR, lParam, false);

// Send the actual Win32 message
int res = SendMessage(rtb.Handle, EM_FORMATRANGE, wParam,
lParam);

sFR =(STRUCT_FORMATRANGE)
Marshal.PtrToStructure(lParam,sFR.GetType());

// Free allocated memory
Marshal.FreeCoTaskMem(lParam);

FormatRangeDone();
return res;
}

public void FormatRangeDone()
{
IntPtr lParam = new IntPtr(0);
SendMessage(rtb.Handle, EM_FORMATRANGE, 0, lParam);
}


private Rectangle AdjustDrawRectangle( Rectangle startRect,
Graphics g )
{
Rectangle adjustedRect = startRect;

if (rtb.TextLength > 0)
{
STRUCT_FORMATRANGE sFR = new STRUCT_FORMATRANGE();
sFR.chrg = new STRUCT_CHARRANGE(0,-1);
sFR.rc = new STRUCT_RECT(adjustedRect,true);
sFR.rcPage = sFR.rc;
IntPtr hdc = g.GetHdc();
try
{
sFR.hdc = hdc;
sFR.hdcTarget = hdc;

while(
(GetDrawRectangle(ref sFR,true))< rtb.TextLength)
{
adjustedRect.Height = adjustedRect.Height * 2;
sFR.rc = new STRUCT_RECT(adjustedRect,true);
sFR.rcPage = sFR.rc;
}
adjustedRect = new Rectangle(0,0,
TwipstoHundrethInch(sFR.rc.right - sFR.rc.left) ,
TwipstoHundrethInch(sFR.rc.bottom- sFR.rc.top)) ;
}
finally
{
g.ReleaseHdc(hdc);
}
}
return adjustedRect;
}


private void btnSave_Click(object sender, System.EventArgs e)
{

Size aSize= rtb.ClientRectangle.Size;
Bitmap aBmp = new Bitmap
(aSize.Width,aSize.Height,PixelFormat.Format32bppArgb);
Graphics g = Graphics.FromImage(aBmp);

try
{
PageSettings ps = new PageSettings(new PrinterSettings());
Rectangle adjustedRect = new
Rectangle(0,0,aSize.Width,aSize.Height);

adjustedRect = AdjustDrawRectangle(adjustedRect,g);
int textAreaWidth = adjustedRect.Width ;
int textAreaHeight = adjustedRect.Height + 1;

ps.Margins.Left = 0;
ps.Margins.Top = 0;
ps.Margins.Right = 0;
ps.Margins.Bottom = 0;

ps.PaperSize = new
PaperSize("Custom",aSize.Width,aSize.Height);
adjustedRect.Height = textAreaHeight;


GraphicsUnit aUnit = GraphicsUnit.Pixel;
g.PageUnit = aUnit;
g.TextRenderingHint = TextRenderingHint.SingleBitPerPixel;
g.SmoothingMode = SmoothingMode.None;
g.CompositingMode = CompositingMode.SourceCopy;
g.CompositingQuality = CompositingQuality.AssumeLinear;
g.PixelOffsetMode = PixelOffsetMode.None;
g.InterpolationMode = InterpolationMode.NearestNeighbor;
g.Clear(rtb.BackColor);

PrintPageEventArgs ppEA = new
PrintPageEventArgs(g,adjustedRect,adjustedRect,ps);

FormatRange(rtb,false,ppEA,-1,this.Text.Length);
FormatRangeDone();

g.Dispose();
aBmp.Save(@"c:\test.bmp",ImageFormat.Bmp);
}
catch(Exception E)
{
if (E.InnerException != null)
MessageBox.Show(E.Message,E.InnerException.Message);
else
MessageBox.Show(E.Message,E.Message);

}
finally
{
aBmp.Dispose();
}
}


public int FormatRange(RichTextBox rtb ,bool measureOnly,
PrintPageEventArgs e,
int charFrom, int charTo)
{
// Specify which characters to print
STRUCT_CHARRANGE cr;
cr.cpMin = charFrom;
cr.cpMax = charTo;

// Specify the area inside page margins
STRUCT_RECT rc;
rc.top = HundredthInchToTwips(e.MarginBounds.Top);
rc.bottom = HundredthInchToTwips(e.MarginBounds.Bottom);
rc.left = HundredthInchToTwips(e.MarginBounds.Left);
rc.right = HundredthInchToTwips(e.MarginBounds.Right);

// Specify the page area
STRUCT_RECT rcPage;
rcPage.top = HundredthInchToTwips(e.PageBounds.Top);
rcPage.bottom = HundredthInchToTwips(e.PageBounds.Bottom);
rcPage.left = HundredthInchToTwips(e.PageBounds.Left);
rcPage.right = HundredthInchToTwips(e.PageBounds.Right);

// Get device context of output device
IntPtr hdc = e.Graphics.GetHdc();

// Fill in the FORMATRANGE struct
STRUCT_FORMATRANGE fr;
fr.chrg = cr;
fr.hdc = hdc;
fr.hdcTarget = hdc;
fr.rc = rc;
fr.rcPage = rcPage;

// Non-Zero wParam means render, Zero means measure
Int32 wParam = (measureOnly ? 0 : 1);

// Allocate memory for the FORMATRANGE struct and
// copy the contents of our struct to this memory
IntPtr lParam = Marshal.AllocCoTaskMem( Marshal.SizeOf( fr ) );
Marshal.StructureToPtr(fr, lParam, false);
// Send the actual Win32 message
int res = SendMessage(rtb.Handle, EM_FORMATRANGE, wParam,
lParam);
// Free allocated memory
Marshal.FreeCoTaskMem(lParam);
// and release the device context
e.Graphics.ReleaseHdc(hdc);

return res;
}
}
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top