Page 1 of 2

How to access metafile records ?

PostPosted: October 14th, 2004, 1:22 pm
by P_G
Hello,

Has anyone a code example for accessing the data of a metafiles records? I need to get the drawing operation (polyline, circle etc.) and the coordinates of each record of a given metafile. I think I have to enumerate through the records with enumenhmetafile but I don't know how to get the code working and how to get the data. :(
Any ideas or examples would be appreciated.

Thanks, P_G

How to access the records of a metafile

PostPosted: October 15th, 2004, 9:36 am
by P_G
No ideas? Anyone? :shock:

PostPosted: October 15th, 2004, 1:12 pm
by Stefan
Something like this should work:

function EnumEnhFunc(DC: HDC; lpHTable: PHandleTable; lpEMFR:
PEnhMetaRecord; nObj: Integer; lpData: LPARAM): BOOL; stdcall;
begin
case lpEMFR^.iType of
EMR_HEADER: Form1.Memo1.Lines.Add('EMR_HEADER:');
end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
hEmf: HENHMETAFILE;
rect: TRect;
MetaFile: TMetaFile;
begin
MetaFile := TMetaFile.Create;
MetaFile.LoadFromFile('C:\sample.emf');
rect.Left := 0;
rect.Top := 0;
rect.Right := MetaFile.MMWidth;
rect.Bottom := MetaFile.MMHeight;
EnumEnhMetaFile(nil, MetaFile.Handle, @EnumEnhFunc, nil, rect)
end;

Metafile records

PostPosted: October 18th, 2004, 11:43 am
by P_G
Thank you, Stefan

I think I understand the basic concept of EnumEnhMetafile now.
But I'm still struggling with the EnumEnhFunc function of yours.
I changed the line to
EMR_HEADER: Form1.Memo1.Lines.Add(IntToStr(EMR_HEADER));
because EMR_HEADER: Form1.Memo1.Lines.Add('EMR_HEADER:');
delivers a string of course (EMR_HEADER:).
By the way - The result is always 1.
So I still don't know how to extract the drawing instruction and the coordinates of each record. :?:

P_G

PostPosted: October 18th, 2004, 3:16 pm
by Stefan
P_G,

Have a look at:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/metafile_5hkj.asp

for an overview of all possible metafile records and how to parse them...

Cheers,
Stefan

Metafile records

PostPosted: October 19th, 2004, 10:35 am
by P_G
Thanks, Stefan

That's exactly what I was looking for.

P_G

PostPosted: October 25th, 2004, 10:16 am
by P_G
Well, sorry, it's me again...

This code of mine is a little tricky. Now I'm able to access the metafile records and load their values into an array of record. I can get the drawing operation of each record (in the example below I only implemented the Polyline16 but it works fine). But the cpts (size of TPoint array) and the apts (array of TPoint) of each metafile record deliver absolutely wrong values.
Any ideas? This really drives me crazy ... :(

Code: Select all
type TSignChunk = record
 SignOp : string[5];
 Data   : Array of TPoint;
end;

var
  Form1: TForm1;
  SignChunk: TSignChunk;
  SignArray: Array of TSignChunk;

implementation

{$R *.dfm}

function MyEnhMetafileProc(DC: HDC; lpHTable: PHandleTable; lpEMFR: pEnhMetaRecord; nObj: Integer; TheForm:TForm1):Integer; stdcall;
var p: PEMRPolyline16;
 rec: TENHMetaRecord;
 i: Integer;
 nb: Integer;
begin
 PlayEnhMetafileRecord(dc, lpHTable^, lpEMFR^,nObj);
case lpEMFR^.iType of
 EMR_Polyline16 : begin
                   p := PEMRPolyLine16(@rec);
                   nb := p.cpts;
                   SetLength(SignArray, Length(SignArray) + 1);
                   SignArray[High(SignArray)].SignOp := 'Pl';
                   SetLength(SignArray[High(SignArray)].Data, nb);
                   For I := 0 to nb -1 do
                    begin
                     SignArray[High(SignArray)].Data[I] := Point(p.apts[I].X, p.apts[I].y);
                    end;
                  end;
 end;
 result := 1;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
MyMetafile: TMetafile;
begin
 MyMetafile := TMetafile.Create;
 MyMetafile.LoadFromFile('sample.emf');
 SignArray := nil;
 EnumEnhMetafile(Canvas.Handle, MyMetafile.Handle, @MyEnhMetafileProc, self, rect(0, 0, MyMetafile.Width,MyMetafile.Height));
 MyMetafile.Free;
end;

PostPosted: October 27th, 2004, 10:49 am
by Stefan
Hi,

The cpts does not seem to return the right ammount of points (and if it does it will surely overflow the maximum of your dynamic array)

try the following code:

Code: Select all
procedure DrawPolyline16 (var rec: TEnhMetaRecord; dc: HDC);
var
    p: PEMRPolyline16;
    i: integer;
    ar: array of TPoint;
begin
    p := PEMRPolyline16(@rec);
    SetLength(ar, Length(p.apts));
    for i := 0 to Length(p.apts)-1 do
      ar[i] := Point(p.apts[i].y, p.apts[i].x);
    polyline(dc, ar, Length(ar)-1);
end;


function MyEnhMetafileProc(DC: HDC; lpHTable: PHandleTable; lpEMFR: pEnhMetaRecord; nObj: Integer; TheForm:TForm1):Integer; stdcall;
var
  p: PEMRPolyline16;
  rec: TENHMetaRecord;
  i: Integer;
  nb: Integer;
begin
  PlayEnhMetafileRecord(dc, lpHTable^, lpEMFR^,nObj);
  case lpEMFR^.iType of
    EMR_Polyline16 : DrawPolyline16(rec, dc);
  end;
  result := 1;
end;

This drives me nuts ...

PostPosted: October 28th, 2004, 9:16 am
by P_G
Stefan wrote:The cpts does not seem to return the right ammount of points (and if it does it will surely overflow the maximum of your dynamic array)


You're absolutely right, Stefan (I noticed this problem a couple of days ago too). But not only cpts doesn't return the correct number of points,
Code: Select all
Length(p.apts)
returns always 1 (!). Even if I use something like 20 or so for the iteration of apts, the returned coordinates are absolutely wrong.
It seems as if Microsoft did not document the metafile in a proper way, I'm afraid.
I send you a metafile, but I checked this code with several metafiles as well, and the problem occurs with any of them.
Thank you so far for your interest in this problem and your time of course.

P_G

Attachment

PostPosted: October 28th, 2004, 9:25 am
by P_G
I hope the attachment works this time...

nuts? I love nuts!

PostPosted: October 28th, 2004, 9:41 am
by Stefan
P_G,

I've tried to draw your emf to canvas and it works perfectly (see attachment)....?

PostPosted: October 28th, 2004, 2:01 pm
by Kambiz
Stefan,

Please change your code as follow to see whether it is working.

Code: Select all
function MyEnhMetafileProc(DC: HDC; lpHTable: PHandleTable; lpEMFR: pEnhMetaRecord; nObj: Integer; TheForm:TForm1):Integer; stdcall;
var
  p: PEMRPolyline16;
  rec: TENHMetaRecord;
  i: Integer;
  nb: Integer;
begin
  case lpEMFR^.iType of
    EMR_Polyline16 : DrawPolyline16(rec, dc);
  else
    PlayEnhMetafileRecord(dc, lpHTable^, lpEMFR^,nObj);
  end;
  result := 1;
end;


It's PlayEnhMetafileRecord that draws the polyline not your function. :wink:

PostPosted: October 28th, 2004, 2:24 pm
by Kambiz
Do you know what? None of you has initialized the rec variable at your codes. :lol:

Code: Select all
procedure DrawPolyline16 (const rec: TEnhMetaRecord; dc: HDC);
var
  Count: Integer;
  Pt: PSmallPoint;
begin
  with PEMRPolyline16(@Rec)^ do
  begin
    Count := cpts - 1;
    Pt := @apts[0];
  end;
  MoveToEx(dc, Pt.x, Pt.y, nil);
  while Count > 0 do
  begin
    Inc(Pt);
    Dec(Count);
    LineTo(dc, Pt.x, Pt.y);
  end;
end;


function MyEnhMetafileProc(DC: HDC; lpHTable: PHandleTable; lpEMFR: pEnhMetaRecord; nObj: Integer; TheForm:TForm1):Integer; stdcall;
begin
  case lpEMFR^.iType of
    EMR_Polyline16 : DrawPolyline16(lpEMFR^, dc);
  else
    PlayEnhMetafileRecord(dc, lpHTable^, lpEMFR^,nObj);
  end;
  result := 1;
end;

PostPosted: October 28th, 2004, 3:39 pm
by Stefan
good to still have you around Kambiz :)

PostPosted: October 29th, 2004, 7:16 pm
by P_G
Uhmm ... Yes ... That's it.
How could I ever doubt in a microsoft documentation ? :wink:
This code works perfectly.
Thanks a lot Kambiz and Stefan. You guys are lifesavers. I already had my own tombstone in mind: Died in the attempt to access metafile records...

P_G