From: AliR (VC++ MVP) on
Hi everyone,

I need to draw some rft to the screen. And I found some code out there that
uses a Richedit control and sends it an EM_FORMATRANGE to do exactly that.

Now I need to add scaling to this code. What I'm trying to do is to have
the richedit control draw to a metafile and then I can set the ScaleTrasform
of the Graphics object and draw the metafile.

But everytime I call Metafile.GetHenhmetafile() it throws a "Parameter is
not valid" execption.

I haven't been able to find a solution to this problem anywhere.

public void Draw(Graphics graphics, RectangleF layoutArea, float
xFactor)
{
//Calculate the area to render.
SafeNativeMethods.RECT rectLayoutArea;
rectLayoutArea.Top = (int)(layoutArea.Top * anInch);
rectLayoutArea.Bottom = (int)(layoutArea.Bottom * anInch);
rectLayoutArea.Left = (int)(layoutArea.Left * anInch);
rectLayoutArea.Right = (int)(layoutArea.Right * anInch);

IntPtr hdc = graphics.GetHdc();

Metafile metafile = new Metafile(hdc, layoutArea);

//Release the device context handle obtained by a previous call
graphics.ReleaseHdc(hdc);

IntPtr hDCEMF = metafile.GetHenhmetafile(); <-------- I can't
get passed this line!

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);

//Free the block of memory allocated
Marshal.FreeCoTaskMem(lParam);

graphics.ScaleTransform(xFactor, xFactor);

graphics.DrawImage(metafile, layoutArea.Location);
}


AliR.


From: Peter Duniho on
On Tue, 12 Aug 2008 08:50:36 -0700, AliR (VC++ MVP) <AliR(a)online.nospam>
wrote:

> Hi everyone,
>
> I need to draw some rft to the screen. And I found some code out there
> that
> uses a Richedit control and sends it an EM_FORMATRANGE to do exactly
> that.
>
> Now I need to add scaling to this code. What I'm trying to do is to have
> the richedit control draw to a metafile and then I can set the
> ScaleTrasform
> of the Graphics object and draw the metafile.
>
> But everytime I call Metafile.GetHenhmetafile() it throws a "Parameter is
> not valid" execption.

Why not use Graphics.FromImage() to get a Graphics instance from the
Metafile, and then Graphics.GetHdc() to get the HDC to assign to
fmtRange.chrg.hdc?

By the way, the class "SafeNativeMethods" looks very much like code from
the .NET Framework itself. I don't know where you got that, but you may
want to be very careful to not be using code copied from .NET.
Microsoft's published the code, but I'm pretty sure the license is for
debugging purposes only, not for reuse within your own code.

Pete
From: AliR (VC++ MVP) on
"Peter Duniho" <NpOeStPeAdM(a)nnowslpianmk.com> wrote in message
news:op.ufr745wz8jd0ej(a)petes-computer.local...
> Why not use Graphics.FromImage() to get a Graphics instance from the
> Metafile, and then Graphics.GetHdc() to get the HDC to assign to
> fmtRange.chrg.hdc?
>
> By the way, the class "SafeNativeMethods" looks very much like code from
> the .NET Framework itself. I don't know where you got that, but you may
> want to be very careful to not be using code copied from .NET.
> Microsoft's published the code, but I'm pretty sure the license is for
> debugging purposes only, not for reuse within your own code.
>
> Pete

I got the code from here http://www.andrewvos.com/?p=392
which is pretty much the same code as
http://support.microsoft.com/kb/812425/en-us
Which .Net code are you refering to? Am I reinventing the wheele? Is there
built-in code that does this already?

I don't really see how GraphicFromImage is going to help. Under native c++
code, I have to draw to a meta file and then I can use world transformations
to scale the text in a way that would not case detering and things like
that.

I tried your suggestion but it didn't draw anything.

public void Draw(Graphics graphics, RectangleF layoutArea, float
xFactor)
{
//Calculate the area to render.
SafeNativeMethods.RECT rectLayoutArea;
rectLayoutArea.Top = (int)(layoutArea.Top * anInch);
rectLayoutArea.Bottom = (int)(layoutArea.Bottom * anInch);
rectLayoutArea.Left = (int)(layoutArea.Left * anInch);
rectLayoutArea.Right = (int)(layoutArea.Right * anInch);

IntPtr hdc = graphics.GetHdc();

Metafile metafile = new Metafile(hdc, layoutArea);

Graphics metagraphic = Graphics.FromImage(metafile);
IntPtr hDCEMF = metagraphic.GetHdc();

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);

//Free the block of memory allocated
Marshal.FreeCoTaskMem(lParam);

metagraphic.ReleaseHdc(hDCEMF);
//Release the device context handle obtained by a previous call
graphics.ReleaseHdc(hdc);

graphics.ScaleTransform(xFactor, xFactor);

graphics.DrawImage(metafile, layoutArea.Location);
}


From: AliR (VC++ MVP) on
"AliR (VC++ MVP)" <AliR(a)online.nospam> wrote in message
news:iCkok.18061$mh5.2592(a)nlpi067.nbdc.sbc.com...
> "Peter Duniho" <NpOeStPeAdM(a)nnowslpianmk.com> wrote in message
> news:op.ufr745wz8jd0ej(a)petes-computer.local...
>> Why not use Graphics.FromImage() to get a Graphics instance from the
>> Metafile, and then Graphics.GetHdc() to get the HDC to assign to
>> fmtRange.chrg.hdc?
>>
>> By the way, the class "SafeNativeMethods" looks very much like code from
>> the .NET Framework itself. I don't know where you got that, but you may
>> want to be very careful to not be using code copied from .NET.
>> Microsoft's published the code, but I'm pretty sure the license is for
>> debugging purposes only, not for reuse within your own code.
>>
>> Pete
>
> I got the code from here http://www.andrewvos.com/?p=392
> which is pretty much the same code as
> http://support.microsoft.com/kb/812425/en-us
> Which .Net code are you refering to? Am I reinventing the wheele? Is
> there built-in code that does this already?
>
> I don't really see how GraphicFromImage is going to help. Under native
> c++ code, I have to draw to a meta file and then I can use world
> transformations to scale the text in a way that would not case detering
> and things like that.
>
> I tried your suggestion but it didn't draw anything.
>
> public void Draw(Graphics graphics, RectangleF layoutArea, float
> xFactor)
> {
> //Calculate the area to render.
> SafeNativeMethods.RECT rectLayoutArea;
> rectLayoutArea.Top = (int)(layoutArea.Top * anInch);
> rectLayoutArea.Bottom = (int)(layoutArea.Bottom * anInch);
> rectLayoutArea.Left = (int)(layoutArea.Left * anInch);
> rectLayoutArea.Right = (int)(layoutArea.Right * anInch);
>
> IntPtr hdc = graphics.GetHdc();
>
> Metafile metafile = new Metafile(hdc, layoutArea);
>
> Graphics metagraphic = Graphics.FromImage(metafile);
> IntPtr hDCEMF = metagraphic.GetHdc();
>
> 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);
>
> //Free the block of memory allocated
> Marshal.FreeCoTaskMem(lParam);
>
> metagraphic.ReleaseHdc(hDCEMF);
> //Release the device context handle obtained by a previous call
> graphics.ReleaseHdc(hdc);
>
> graphics.ScaleTransform(xFactor, xFactor);
>
> graphics.DrawImage(metafile, layoutArea.Location);
> }
>
>

Here is the MFC version of this:
// setup rectangles for metafile fiddling
DC->DPtoHIMETRIC(&cTargetSize); // 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);

// create the enhanced metafile
HDC hDCEMF = CreateEnhMetaFile(DC->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;
pCtrl->SendMessage(EM_FORMATRANGE,TRUE,(LPARAM) &stFR);
pCtrl->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 = DC->GetDeviceCaps(HORZSIZE ); // width in millimeters
int nVertSize = DC->GetDeviceCaps(VERTSIZE ); // height in millimeters
int nHorzRes = DC->GetDeviceCaps(HORZRES ); // width in pixels
int nVertRes = DC->GetDeviceCaps(VERTRES ); // height in pixels
int nLogPixelsX = DC->GetDeviceCaps(LOGPIXELSX); // # of pixels per inch
horizontally
int nLogPixelsY = DC->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; //

// do the world transforms, first apply the scale and fudge factors
XFORM stX;
ZeroMemory(&stX,sizeof(stX));
stX.eM11 = fHorzFudgeFactor * (m_XFactor);
stX.eM22 = fVertFudgeFactor * (m_XFactor);
SetWorldTransform(DC->m_hDC,&stX);

long Top = WinRect.top;
long Left = WinRect.left;
WinRect.OffsetRect(-Left,-Top);
WinRect.OffsetRect((int)(Left/fHorzFudgeFactor),int(Top/fVertFudgeFactor));

DC->PlayMetaFile(hEMF,&WinRect);

// that's it, kiss the metafile goodbye
DeleteEnhMetaFile(hEMF);


From: Peter Duniho on
On Tue, 12 Aug 2008 11:27:29 -0700, AliR (VC++ MVP) <AliR(a)online.nospam>
wrote:

> I got the code from here http://www.andrewvos.com/?p=392
> which is pretty much the same code as
> http://support.microsoft.com/kb/812425/en-us
> Which .Net code are you refering to? Am I reinventing the wheele? Is
> there
> built-in code that does this already?

That I know of, no. I was simply commenting on the class
"SafeNativeMethods", because that's the exact name used in the .NET
Framework's implementation. Could be that the original source lifted it
from the .NET source code, or just the name.

Personally, it doesn't concern me one way or the other. It's just that
someone using that code should be careful if they are at all concerned
about running afoul of Microsoft's legal department (which, while not as
rabid as those found in other corporations, do still need to justify their
existence).

> I don't really see how GraphicFromImage is going to help.

The Graphics instance you get from that wraps the Metafile itself, so any
DC you get from that Graphics instance is for the Metafile.

In other words, the HDC that you get from the Graphics instance returned
by that method returns exactly the thing you need to assign to the
FORMATRANGE.hdc field.

I don't know why the call to Metafile.GetHenhmetafile() doesn't work,
other than the fact that I personally have had trouble with the .NET
Metafile implementation in the past (in other words, things that you'd
think would work based on how the unmanaged version works, wind up not
working). But I do know that you can get an HDC that draws into the
Metafile by using Graphics.FromImage() and the Graphics.GetHdc(). I'm
pretty sure I've done that before and had it work (for sure it works with
Bitmaps, and I'm pretty sure I've used it with Metafiles too).

> Under native c++
> code, I have to draw to a meta file and then I can use world
> transformations
> to scale the text in a way that would not case detering and things like
> that.
>
> I tried your suggestion but it didn't draw anything.

Well, it at least didn't cause an error, right?

I can't say exactly what's wrong with the code you're using. But I
suspect you're closer to a solution now than you were before. :)

You'll probably have to break the problem down into smaller parts and see
what's not working as expected. The code you're using is almost, but not
exactly, like the MSDN code, so one place to start would be to start with
_exactly_ the code in the MSDN sample and see if you can get that to work.

You should also check to see whether you're able to create a Metafile via
more conventional means that does what you want, to double-check that your
Metafile-specific code is valid.

And of course, I hope this is a silly question, but you are in fact
putting that code in a control that inherits RichTextBox, right?

Pete