Re: RTF Render with Scale
- From: "AliR \(VC++ MVP\)" <AliR@xxxxxxxxxxxxx>
- Date: Tue, 28 Oct 2008 11:49:13 -0500
I setup another machine with Vista 64 and it is happening there too. So it
looks like Vista 64 is the problem!
AliR.
"AliR (VC++ MVP)" <AliR@xxxxxxxxxxxxx> wrote in message
news:5cGNk.1507$%11.785@xxxxxxxxxxxxxxxxxxxxxxx
Let me also mention that the laptop that is having the funny results is
running Vista 64, the other machines which everything works fine on is
running Vista 32 or XP.
AliR.
"AliR (VC++ MVP)" <AliR@xxxxxxxxxxxxx> wrote in message
news:pqqNk.5297$yr3.950@xxxxxxxxxxxxxxxxxxxxxxx
I'm trying to write a RTF render code in C#. I have the code in C++/MFC
and it works fine, but I have run into a problem with the C# code. I
think the C# code is from Microsoft, and I added scaling to it. The C#
code is working fine on my desktop PC but it cuts off some of the text
when I run it on my laptop. The MFC code works fine both on the laptop
and the PC. I have looked at all the numbers used in the MFC and the C#
code and they are identical. Both PC and Laptop are at 1440x900 96dpi
font size.
I have been looking at the code for 3 or 4 days now without any
improvment. Was wondering if anyone here wants to take a stab at it.
You can download both sample projects:
http://www.learnstar.com/AliR/RTFScaler.zip
http://www.learnstar.com/AliR/RTFScalerMFC.zip
Thanks
AliR.
Here is the MFC code
// *** copying/scaling/rotating and shearing the text is done here
void CRTFScalerDlg::RenderText(int nScalePercent,int nRotationDegrees,int
nHorzShearPercent,int nVertShearPercent)
{
// validate the arguments
if (nScalePercent < 1)
nScalePercent = 1;
if (nScalePercent > 10000)
nScalePercent = 10000;
if (nRotationDegrees < 0)
nRotationDegrees = 0;
if (nRotationDegrees > 360)
nRotationDegrees = 360;
if (nHorzShearPercent < 0)
nHorzShearPercent = 0;
if (nHorzShearPercent > 100)
nHorzShearPercent = 100;
if (nVertShearPercent < 0)
nVertShearPercent = 0;
if (nVertShearPercent > 100)
nVertShearPercent = 100;
// setup target window, rect and DC
CWnd* pTargetWin = GetDlgItem(IDC_EDIT1);
CRect crTarget;
pTargetWin->GetClientRect(&crTarget);
crTarget.OffsetRect(0,20);
CDC* pTargetDC = pTargetWin->GetDC();
pTargetDC->IntersectClipRect(&crTarget); // so we don't clobber the
parent dialog
pTargetDC->SetBkMode(TRANSPARENT); // so the legend doesn't erase too
much
// also need advanced graphics mode for the world transform stuff
if (!SetGraphicsMode(pTargetDC->m_hDC,GM_ADVANCED))
{
pTargetDC->DrawText("Nice try but I need Windows NT, 2000, XP or 2003
for this.",crTarget,DT_SINGLELINE | DT_VCENTER | DT_CENTER);
return;
}
// erase previous contents
FillRect(pTargetDC->m_hDC,crTarget,(HBRUSH)(COLOR_WINDOW + 1));
CString Temp;
Temp.Format("crTarget %d,%d
%d,%d\n",crTarget.left,crTarget.top,crTarget.Width(),crTarget.Height());
TRACE(Temp);
// setup rectangles for metafile fiddling
CSize cTargetSize = crTarget.Size();
pTargetDC->DPtoHIMETRIC(&cTargetSize); // from MM_Text to MM_HIMETRIC
CSize TopLeft;
TopLeft.cx = crTarget.left;
TopLeft.cy = crTarget.top;
pTargetDC->DPtoHIMETRIC(&TopLeft); // from MM_Text to MM_HIMETRIC
CRect cHiMetricRect(0,0,cTargetSize.cx,cTargetSize.cy);
CRect cTwipsRect(0,0,(TWIPS_INCH * cTargetSize.cx + HIMETRIC_INCH / 2) /
HIMETRIC_INCH,
(TWIPS_INCH * cTargetSize.cy + HIMETRIC_INCH / 2) / HIMETRIC_INCH);
Temp.Format("MetaSize
%d,%d\n",cHiMetricRect.Width(),cHiMetricRect.Height());
TRACE(Temp);
Temp.Format("cTwipsRect %d,%d\n",cTwipsRect.Width(),cTwipsRect.Height());
TRACE(Temp);
// create the enhanced metafile
HDC hDCEMF = CreateEnhMetaFile(pTargetDC->m_hDC,NULL,cHiMetricRect,NULL);
// setup the format struct and do the RTF range formatting call
FORMATRANGE stFR;
stFR.hdcTarget = stFR.hdc = hDCEMF; // render to the memory helper EMF
stFR.rcPage = stFR.rc = cTwipsRect; // using this rectangle (in
twips)
stFR.chrg.cpMin = 0; // and render all of the text
stFR.chrg.cpMax = -1;
GetDlgItem(IDC_RICHEDIT1)->SendMessage(EM_FORMATRANGE,TRUE,(LPARAM)
&stFR);
GetDlgItem(IDC_RICHEDIT1)->SendMessage(EM_FORMATRANGE,TRUE,NULL); // this
call clears the cache
// drawing into the metafile is done, get ourselves an handle to it (used
for the replay)
HENHMETAFILE hEMF = CloseEnhMetaFile(hDCEMF);
// calculate the automagic fudge factors by getting the device metrics
int nHorzSize = pTargetDC->GetDeviceCaps(HORZSIZE ); // width in
millimeters
int nVertSize = pTargetDC->GetDeviceCaps(VERTSIZE ); // height in
millimeters
int nHorzRes = pTargetDC->GetDeviceCaps(HORZRES ); // width in
pixels
int nVertRes = pTargetDC->GetDeviceCaps(VERTRES ); // height in
pixels
int nLogPixelsX = pTargetDC->GetDeviceCaps(LOGPIXELSX); // # of pixels
per inch horizontally
int nLogPixelsY = pTargetDC->GetDeviceCaps(LOGPIXELSY); // # of pixels
per inch vertically
float fHorzSizeInches = nHorzSize / 25.4f;
float fVertSizeInches = nVertSize / 25.4f;
float fHorzFudgeFactor = (nHorzRes / fHorzSizeInches) / nLogPixelsX; //
divide expected DPI with actual DPI
float fVertFudgeFactor = (nVertRes / fVertSizeInches) / nLogPixelsY; //
Temp.Format("Fudgfactor %.2f %.2f\n",fHorzFudgeFactor,fVertFudgeFactor);
TRACE(Temp);
// change from degrees to radians
// (also need to change sign since page space is upside down)
float fRotationInRadians = (PI * (0 - nRotationDegrees)) / 180.0F;
// do the world transforms, first apply the scale and fudge factors
XFORM stX;
ZeroMemory(&stX,sizeof(stX));
stX.eM11 = fHorzFudgeFactor * (nScalePercent / 100.0f);
stX.eM22 = fVertFudgeFactor * (nScalePercent / 100.0f);
SetWorldTransform(pTargetDC->m_hDC,&stX);
// then apply the rotation
ZeroMemory(&stX,sizeof(stX));
stX.eM11 = (float) cos(fRotationInRadians);
stX.eM12 = (float) sin(fRotationInRadians);
stX.eM21 = 0 - (float) sin(fRotationInRadians);
stX.eM22 = (float) cos(fRotationInRadians);
ModifyWorldTransform(pTargetDC->m_hDC,&stX,MWT_LEFTMULTIPLY);
// finally apply the horizontal and vertical shearing
ZeroMemory(&stX,sizeof(stX));
stX.eM11 = 1.0f;
stX.eM12 = nHorzShearPercent / 100.0f;
stX.eM21 = nVertShearPercent / 100.0f;
stX.eM22 = 1.0f;
ModifyWorldTransform(pTargetDC->m_hDC,&stX,MWT_LEFTMULTIPLY);
crTarget.OffsetRect(0,-20);
crTarget.OffsetRect(0,20 / (nScalePercent / 100.0f) * fVertFudgeFactor);
// play the metafile
pTargetDC->PlayMetaFile(hEMF,&crTarget);
// that's it, kiss the metafile goodbye
DeleteEnhMetaFile(hEMF);
// also show a legend
ModifyWorldTransform(pTargetDC->m_hDC,NULL,MWT_IDENTITY);
CString sLegend;
sLegend.Format("Scale: %d%% rotation: %dº horizontal shear: %d%%
vertical shear: %d%%.",
nScalePercent,nRotationDegrees,nHorzShearPercent,nVertShearPercent);
pTargetDC->SetTextColor(RGB(55,111,111));
pTargetDC->DrawText(sLegend,crTarget,DT_SINGLELINE | DT_VCENTER |
DT_CENTER);
}
Here is the C# code:
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.Text;
public static class Graphics_DrawRtfText
{
private static RichTextBoxDrawer rtfDrawer;
public static void DrawRtfText(this Graphics graphics, string rtf,
RectangleF layoutArea,float xFactor)
{
if (Graphics_DrawRtfText.rtfDrawer == null)
{
Graphics_DrawRtfText.rtfDrawer = new RichTextBoxDrawer();
}
Graphics_DrawRtfText.rtfDrawer.Rtf = rtf;
Graphics_DrawRtfText.rtfDrawer.Draw(graphics, layoutArea,xFactor);
}
private class RichTextBoxDrawer : RichTextBox
{
//Code converted from code found here:
http://support.microsoft.com/kb/812425/en-us
//Convert the unit used by the .NET framework (1/100 inch)
//and the unit used by Win32 API calls (twips 1/1440 inch)
private const double anInch = 14.4;
protected override CreateParams CreateParams
{
get
{
CreateParams createParams = base.CreateParams;
if (SafeNativeMethods.LoadLibrary("msftedit.dll") !=
IntPtr.Zero)
{
createParams.ExStyle |=
SafeNativeMethods.WS_EX_TRANSPARENT; // transparent
createParams.ClassName = "RICHEDIT50W";
}
return createParams;
}
}
private void DPToHIMETRIC(Graphics graphics,ref SizeF size)
{
size.Width = (size.Width * 2540.0f) / graphics.DpiX;
size.Height = (size.Height * 2540.0f) / graphics.DpiY;
}
public void Draw(Graphics graphics, RectangleF layoutArea, float
xFactor)
{
System.Diagnostics.Debug.WriteLine("LayoutArea " +
layoutArea);
SizeF metaSize = layoutArea.Size;
DPToHIMETRIC(graphics, ref metaSize);
System.Diagnostics.Debug.WriteLine("MetaSize " + metaSize);
IntPtr hdc = graphics.GetHdc();
//create a metafile, convert the size to himetrics
Metafile metafile = new Metafile(hdc, new
RectangleF(0,0,metaSize.Width,metaSize.Height));
graphics.ReleaseHdc(hdc);
Graphics g = Graphics.FromImage(metafile);
IntPtr hDCEMF = g.GetHdc();
//Calculate the area to render.
SafeNativeMethods.RECT rectLayoutArea;
rectLayoutArea.Left = 0;
rectLayoutArea.Top = 0;
rectLayoutArea.Right = (int)((1440 * metaSize.Width + 2540 /
2) / 2540);
rectLayoutArea.Bottom = (int)((1440 * metaSize.Height + 2540 /
2) / 2540);
System.Diagnostics.Debug.WriteLine(String.Format("RectLayoutArea
({0},{1})",rectLayoutArea.Right,rectLayoutArea.Bottom));
SafeNativeMethods.FORMATRANGE fmtRange;
fmtRange.chrg.cpMax = -1; //Indicate character from
to character to
fmtRange.chrg.cpMin = 0;
fmtRange.hdc = hDCEMF; //Use the same DC for
measuring and rendering
fmtRange.hdcTarget = hDCEMF; //Point at printer hDC
fmtRange.rc = rectLayoutArea; //Indicate the area on
page to print
fmtRange.rcPage = rectLayoutArea; //Indicate size of page
IntPtr wParam = IntPtr.Zero;
wParam = new IntPtr(1);
//Get the pointer to the FORMATRANGE structure in memory
IntPtr lParam = IntPtr.Zero;
lParam = Marshal.AllocCoTaskMem(Marshal.SizeOf(fmtRange));
Marshal.StructureToPtr(fmtRange, lParam, false);
SafeNativeMethods.SendMessage(this.Handle,
SafeNativeMethods.EM_FORMATRANGE, wParam, lParam);
SafeNativeMethods.SendMessage(this.Handle,
SafeNativeMethods.EM_FORMATRANGE, wParam, IntPtr.Zero);
//Free the block of memory allocated
Marshal.FreeCoTaskMem(lParam);
//Release the device context handle obtained by a previous
call
g.ReleaseHdc(hDCEMF);
g.Dispose();
hdc = graphics.GetHdc();
int nHorzSize = SafeNativeMethods.GetDeviceCaps(hdc,
SafeNativeMethods.DeviceCap.HORZSIZE);
int nVertSize = SafeNativeMethods.GetDeviceCaps(hdc,
SafeNativeMethods.DeviceCap.VERTSIZE);
int nHorzRes = SafeNativeMethods.GetDeviceCaps(hdc,
SafeNativeMethods.DeviceCap.HORZRES);
int nVertRes = SafeNativeMethods.GetDeviceCaps(hdc,
SafeNativeMethods.DeviceCap.VERTRES);
int nLogPixelsX = SafeNativeMethods.GetDeviceCaps(hdc,
SafeNativeMethods.DeviceCap.LOGPIXELSX);
int nLogPixelsY = SafeNativeMethods.GetDeviceCaps(hdc,
SafeNativeMethods.DeviceCap.LOGPIXELSY);
graphics.ReleaseHdc(hdc);
float fHorzSizeInches = nHorzSize / 25.4f;
float fVertSizeInches = nVertSize / 25.4f;
float fHorzFudgeFactor = (nHorzRes / fHorzSizeInches) /
nLogPixelsX;
float fVertFudgeFactor = (nVertRes / fVertSizeInches) /
nLogPixelsY;
System.Diagnostics.Debug.WriteLine("Fudge Factor " +
fHorzFudgeFactor.ToString() + " " + fVertFudgeFactor.ToString() + "
XFactor " + xFactor.ToString());
Pen RedPen = new Pen(Color.Red);
graphics.DrawRectangle(RedPen, layoutArea.X * xFactor,
layoutArea.Y * xFactor, layoutArea.Width * xFactor, layoutArea.Height *
xFactor);
float Left = layoutArea.Left;
float Top = layoutArea.Top;
//layoutArea.X = layoutArea.Y = 0;
layoutArea.Offset(-Left, -Top);
layoutArea.Offset(Left / fHorzFudgeFactor, Top /
fVertFudgeFactor);
System.Drawing.Drawing2D.GraphicsState state =
graphics.Save();
graphics.ScaleTransform(fHorzFudgeFactor * xFactor,
fVertFudgeFactor * xFactor);
graphics.DrawImage(metafile, layoutArea);
graphics.Restore(state);
System.Diagnostics.Debug.WriteLine("Layout Aread :
"+layoutArea);
}
#region SafeNativeMethods
private static class SafeNativeMethods
{
[DllImport("USER32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, int msg,
IntPtr wp, IntPtr lp);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr LoadLibrary(string lpFileName);
[DllImport("gdi32.dll")]
public static extern int GetDeviceCaps(IntPtr hdc, DeviceCap
nIndex);
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
[StructLayout(LayoutKind.Sequential)]
public struct CHARRANGE
{
public int cpMin; //First character of range (0 for
start of doc)
public int cpMax; //Last character of range (-1 for
end of doc)
}
[StructLayout(LayoutKind.Sequential)]
public struct FORMATRANGE
{
public IntPtr hdc; //Actual DC to draw on
public IntPtr hdcTarget; //Target DC for determining
text formatting
public RECT rc; //Region of the DC
to draw to (in twips)
public RECT rcPage; //Region of the whole
DC (page size) (in twips)
public CHARRANGE chrg; //Range of text to draw (see
earlier declaration)
}
public enum DeviceCap : int
{
/// <summary>
/// Device driver version
/// </summary>
DRIVERVERSION = 0,
/// <summary>
/// Device classification
/// </summary>
TECHNOLOGY = 2,
/// <summary>
/// Horizontal size in millimeters
/// </summary>
HORZSIZE = 4,
/// <summary>
/// Vertical size in millimeters
/// </summary>
VERTSIZE = 6,
/// <summary>
/// Horizontal width in pixels
/// </summary>
HORZRES = 8,
/// <summary>
/// Vertical height in pixels
/// </summary>
VERTRES = 10,
/// <summary>
/// Number of bits per pixel
/// </summary>
BITSPIXEL = 12,
/// <summary>
/// Number of planes
/// </summary>
PLANES = 14,
/// <summary>
/// Number of brushes the device has
/// </summary>
NUMBRUSHES = 16,
/// <summary>
/// Number of pens the device has
/// </summary>
NUMPENS = 18,
/// <summary>
/// Number of markers the device has
/// </summary>
NUMMARKERS = 20,
/// <summary>
/// Number of fonts the device has
/// </summary>
NUMFONTS = 22,
/// <summary>
/// Number of colors the device supports
/// </summary>
NUMCOLORS = 24,
/// <summary>
/// Size required for device descriptor
/// </summary>
PDEVICESIZE = 26,
/// <summary>
/// Curve capabilities
/// </summary>
CURVECAPS = 28,
/// <summary>
/// Line capabilities
/// </summary>
LINECAPS = 30,
/// <summary>
/// Polygonal capabilities
/// </summary>
POLYGONALCAPS = 32,
/// <summary>
/// Text capabilities
/// </summary>
TEXTCAPS = 34,
/// <summary>
/// Clipping capabilities
/// </summary>
CLIPCAPS = 36,
/// <summary>
/// Bitblt capabilities
/// </summary>
RASTERCAPS = 38,
/// <summary>
/// Length of the X leg
/// </summary>
ASPECTX = 40,
/// <summary>
/// Length of the Y leg
/// </summary>
ASPECTY = 42,
/// <summary>
/// Length of the hypotenuse
/// </summary>
ASPECTXY = 44,
/// <summary>
/// Shading and Blending caps
/// </summary>
SHADEBLENDCAPS = 45,
/// <summary>
/// Logical pixels inch in X
/// </summary>
LOGPIXELSX = 88,
/// <summary>
/// Logical pixels inch in Y
/// </summary>
LOGPIXELSY = 90,
/// <summary>
/// Number of entries in physical palette
/// </summary>
SIZEPALETTE = 104,
/// <summary>
/// Number of reserved entries in palette
/// </summary>
NUMRESERVED = 106,
/// <summary>
/// Actual color resolution
/// </summary>
COLORRES = 108,
// Printing related DeviceCaps. These replace the
appropriate Escapes
/// <summary>
/// Physical Width in device units
/// </summary>
PHYSICALWIDTH = 110,
/// <summary>
/// Physical Height in device units
/// </summary>
PHYSICALHEIGHT = 111,
/// <summary>
/// Physical Printable Area x margin
/// </summary>
PHYSICALOFFSETX = 112,
/// <summary>
/// Physical Printable Area y margin
/// </summary>
PHYSICALOFFSETY = 113,
/// <summary>
/// Scaling factor x
/// </summary>
SCALINGFACTORX = 114,
/// <summary>
/// Scaling factor y
/// </summary>
SCALINGFACTORY = 115,
/// <summary>
/// Current vertical refresh rate of the display device
(for displays only) in Hz
/// </summary>
VREFRESH = 116,
/// <summary>
/// Horizontal width of entire desktop in pixels
/// </summary>
DESKTOPVERTRES = 117,
/// <summary>
/// Vertical height of entire desktop in pixels
/// </summary>
DESKTOPHORZRES = 118,
/// <summary>
/// Preferred blt alignment
/// </summary>
BLTALIGNMENT = 119
}
public const int WM_USER = 0x0400;
public const int EM_FORMATRANGE = WM_USER + 57;
public const int WS_EX_TRANSPARENT = 0x20;
}
#endregion
}
}
.
- References:
- RTF Render with Scale
- From: AliR \(VC++ MVP\)
- Re: RTF Render with Scale
- From: AliR \(VC++ MVP\)
- RTF Render with Scale
- Prev by Date: Re: Toolstrip
- Next by Date: Why events if we have delegates
- Previous by thread: Re: RTF Render with Scale
- Next by thread: Debugging - how to track a button visibility change and stop program execution
- Index(es):