Source file: /~heha/hs/wmfview.zip/src/wmf.c

/* WMF decoder/viewer, based on code by a taiwanese programmer */
#include "wmfview.h"

// Irgendwie müsste die Abhängigkeit von GDI-Funktionen markiert werden!
// Bspw. Ellipse <- PEN, BRUSH, ROP2
// LineTo <- PEN, BKMODE*, ROP2, POSITION -> POSITION
// Polygon <- PEN, BRUSH, BKMODE*, FILLMODE, ROP2
// TextOut <- FONT, TEXTCOLOR, BKMODE*, CHAREXTRA, JUSTIFY, ALIGN

// BKMODE* = BKMODE sowie ggf. BKCOLOR wenn PEN und/oder BRUSH "löchrig"

static LPCSTR FuncNames[256]={
/*0000*/"EOF()",
/*0201*/"SetBkColor(COLORREF crColor)",
/*0102*/"SetBkMode(E1 iBkMode)",
/*0103*/"SetMapMode(E2 fnMapMode)",
/*0104*/"SetRop2(E6 fnDrawMode)",
/*0105*/"SetRelAbs",
/*0106*/"SetPolyFillMode(E0 iPolyFillMode)",
/*0107*/"SetStretchBltMode(iStretchMode)",
/*0108*/"SetTextCharExtra(nCharExtra)",
/*0209*/"SetTextColor(COLORREF crColor)",
/*020a*/"SetTextJustification(nBreakCount,nBreakExtra)",
/*020b*/"SetWindowOrg(Y,X)",
/*020c*/"SetWindowExt(nYExtent,nXExtent)",
/*020d*/"SetViewportOrg(Y,X)",
/*020e*/"SetViewportExt(nYExtent,nXExtent)",
/*020f*/"OffsetWindowOrg(nYOffset,nXOffset)",
/*0410*/"ScaleWindowExt(YDenom,YNum,XDenom,XNum)",
/*0211*/"OffsetViewportOrg(nYOffset,nXOffset)",
/*0412*/"ScaleViewportExt(YDenom,YNum,XDenom,XNum)",
/*0213*/"LineTo(Y,X)L",
/*0214*/"MoveTo(Y,X)",
/*0415*/"ExcludeClipRect(iBottom,iRight,iTop,iLeft)",
/*0416*/"IntersectClipRect(iBottom,iRight,iTop,iLeft)",
/*0817*/"Arc(iYEnd,iXEnd,iYStart,iXStart,iBottom,iRight,iTop,iLeft)A",
/*0418*/"Ellipse(iBottom,iRight,iTop,iLeft)F",
/*0419*/"FloodFill(COLORREF crColor,nYStart,nXStart)",
/*081A*/"Pie(iYEnd,iXEnd,iYStart,iXStart,iBottom,iRight,iTop,iLeft)F",
/*041B*/"Rectangle(iBottom,iRight,iTop,iLeft)F",
/*061C*/"RoundRect(iHeight,iWidth,iBottom,iRight,iTop,iLeft)F",
/*061D*/"PatBlt",
/*001E*/"SaveDC()",
/*041F*/"SetPixel(COLORREF crColor,Y,X)",
/*0220*/"OffsetClipRgn(nYOffset,nXOffset)",
/*0521*/"TextOut(cbString,CHAR lpString[cbString],X,Y)T",
/*0922*/"BitBlt",
/*0b23*/"StretchBlt",
/*0324*/"Polygon(nCount,POINT lpPoints[nCount])P",
/*0325*/"Polyline(nCount,POINT lpPoints[nCount])",
/*0626*/"Escape(cbString,CHAR lpString[cbString])",
/*0127*/"RestoreDC(nSaveDC)R",
/*0228*/"FillRegion",
/*0429*/"FrameRegion(OBJIDX hFrame,OBJIDX hBrush,nWidth,nHeight)",
/*012A*/"InvertRegion",
/*012B*/"PaintRegion",
/*012C*/"SelectClipregion",
/*012D*/"SelectObject(OBJIDX iObject)",
/*012E*/"SetTextAlign(E5 fMode)",
	NULL,
/*0830*/"Chord(iLeft,iTop,iRight,iBottom,iXStart,iYStart,iXEnd,iYEnd)F",
/*0231*/"SetMapperFlags(unsigned dwFlag)",
/*0a32*/"ExtTextOut",
/*0d33*/"SetDibToDev",
/*0234*/"SelectPalette",
/*0035*/"RealizePalette",
/*0436*/"AnimatePalette",
/*0037*/"SetPalEntries",
/*0538*/"PolyPolygon(nCount,nPolyPoints[nCount],POINT pPoints[nPoints])P",
/*0139*/"ResizePalette",
	NULL,NULL,NULL,NULL,NULL,NULL,
/*0940*/"DibBitblt",
/*0b41*/"DibStretchblt",
/*0142*/"DibCreatePatternBrush",
/*0f43*/"StretchDib",
	NULL,NULL,NULL,NULL,
/*0548*/"ExtFloodFill(nXStart,nYStart,COLORREF crColor,E14 fuFillType)",
	NULL,NULL,NULL,NULL,NULL,NULL,NULL,
/*50*/	NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
/*60*/	NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
/*70*/	NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
/*80*/	NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
/*90*/	NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
/*A0*/	NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
/*B0*/	NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
/*C0*/	NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
/*D0*/	NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
/*E0*/	NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
/*01F0*/"DeleteObject",
	NULL,NULL,NULL,NULL,NULL,NULL,
/*00F7*/"CreatePalette",
	NULL,
/*01F9*/"CreatePatternBrush",
/*02FA*/"CreatePen(E17 fnPenStyle,WidthX,WidthY,COLORREF crColor)",
/*02FB*/"CreateFont(lfHeight,lfWidth,FIX1 lfEscapement,FIX1 lfOrientation,E7 lfWeight,"
	"EB8 lfItalic,EB8 lfUnderline,EB8 lfStrikeOut,EB9 lfCharSet,"
	"EB10 lfOutPrecision,EB11 lfClipPrecision,EB12 lfQuality,EB13 lfPitchAndFamily,"
	"CHAR lfFaceName[32])",
/*02FC*/"CreateBrush(E15 lbStyle,COLORREF lbColor,E16 lbHatch)",
	NULL,NULL,
/*06FF*/"CreateRegion"};

static const LPSTR headers[]={
 "Metaheader(mtType,mtHeaderSize,WORD Version,long nFileSize,nObjects,DWORD nMaxRecSize,WORD nParameters)",
 "Placeable Metaheader(DWORD id,handle,RECT rcArea,inch,DWORD reserved,WORD checksum)",
 "MetaFilePict(EL2 MapMode, long xExt, long yExt, DWORD hMF)",
};

#define MAXARGS 16

// ANSI- oder Unicode-String als Quelle
LPTSTR EscapeStr(LPTSTR buf,LPCTSTR end, LPCSTR s, int slen, UINT Flags) {
 WCHAR c;
 for(;slen;slen--) {
  LPCTSTR fmt;
  if (Flags&ES_WIDECHAR) c=*((LPCWSTR)s)++;
  else c=(BYTE)(*s++);
  switch (c) {
   case 0: if (Flags&ES_ZEROTERMINATED) return buf; goto p;
   case L'\a': c=L'a'; goto o;	//7
   case L'\b': c=L'b'; goto o;	//8
   case L'\t': c=L't'; goto o;	//9
   case L'\n': c=L'n'; goto o;	//10
   case L'\v': c=L'v'; goto o;	//11?
   case L'\f': c=L'f'; goto o;	//12
   case L'\r': c=L'r'; goto o;	//13
   case 127: goto p;
   case L'"':
   case L'\\':
o:  fmt=T("\\%c"); break;
   default: if (c<0x20) {
p:  fmt=T("\\x%02X");
#ifndef UNICODE
   }else if (c>=256) {
    fmt=T("\\u%04X");
#endif
   }else fmt=T("%c");
// fehlt noch IsDbcsLeadByte()!! Oder ADDBUF ist inkorrekt für DBCS
  }
  ADDBUF fmt,c);
 }
 return buf;
}

static GDIINFO NextGdiInfo;
static BYTE *pObjTypes;		// anhand WMF-Kopf alloziert
static OBJINFO *pObjInfos;	// anhand WMF-Kopf alloziert
static int nObjects;		// Überlauf-Erkennung
static int nCurObject;		// wo CreateXxx in die Tabelle fällt

void ListDep(DECODEINFO*di, TVINSERTSTRUCT*pChildInsert, DWORD dep) {
 LPTSTR buf;
 LPTSTR end=pChildInsert->item.pszText+pChildInsert->item.cchTextMax;
#define  INIBUF buf=pChildInsert->item.pszText
 if (dep&DEP_PEN) {
  int objidx=di->gi.objidx[OBJ_PEN-1];
  LOGPEN *pLogPen=&pObjInfos[objidx].pen;
  INIBUF;
  ADDBUF T("hPen (ObjIdx=%u) "),objidx);
  buf=DecodeE((LPSTR)17,pLogPen->lopnStyle,buf,end);
  if (pLogPen->lopnStyle!=PS_NULL) {
   ADDBUF T(",%d,%d,%06X"),
     pLogPen->lopnWidth.x,
     pLogPen->lopnWidth.y,
     pLogPen->lopnColor);
  }
  TreeView_InsertItem(hContent,pChildInsert);
  switch (pLogPen->lopnStyle) {
   case PS_DASH:
   case PS_DOT:
   case PS_DASHDOT:
   case PS_DASHDOTDOT: dep|=DEP_BKMODE; break;
  }
 }
 if (dep&DEP_BRUSH) {
  int objidx=di->gi.objidx[OBJ_BRUSH-1];
  LOGBRUSH *pLogBrush=&pObjInfos[objidx].brush;
  INIBUF;
  ADDBUF T("hBrush (ObjIdx=%u) "),objidx);
  buf=DecodeE((LPCSTR)15,pLogBrush->lbStyle,buf,end);
  if (pLogBrush->lbStyle!=BS_NULL) {
   ADDBUF T(",%06X"),pLogBrush->lbColor);
   if (pLogBrush->lbStyle==BS_HATCHED) {
    ADDBUF T(","));
    buf=DecodeE((LPCSTR)16,(UINT)pLogBrush->lbHatch,buf,end);
   }
  }
  TreeView_InsertItem(hContent,pChildInsert);
  switch (pLogBrush->lbStyle) {
   case BS_HATCHED: dep|=DEP_BKMODE; break;
  }
 }
 if (dep&DEP_BKMODE) {
  INIBUF;
  ADDBUF T("BkMode = "));
  buf=DecodeE((LPSTR)1,di->gi.BkMode,buf,end);
  TreeView_InsertItem(hContent,pChildInsert);
  if (di->gi.BkMode==OPAQUE) {
   INIBUF;
   ADDBUF T("BkColor = %06X"),di->gi.BkColor);
   TreeView_InsertItem(hContent,pChildInsert);
  }
 }
 if (dep&DEP_POLYFILL) {	/* && ComplexPolygon()*/
  INIBUF;
  ADDBUF T("PolyFillMode = "));
  buf=DecodeE((LPSTR)0,di->gi.PolyFillMode,buf,end);
  TreeView_InsertItem(hContent,pChildInsert);
 }
 if (dep&DEP_POSITION) {
  INIBUF;
  VADDBUF T("Position: x=%d, y=%d"),(va_list)&di->gi.Position);
  TreeView_InsertItem(hContent,pChildInsert);
 }
 if (dep&DEP_TEXT) {
  int objidx=di->gi.objidx[OBJ_FONT-1];
  LOGFONT *pLogFont=&pObjInfos[objidx].font;
  INIBUF;
  ADDBUF T("hFont (ObjIdx=%u) "),objidx);
  ADDBUF T("%d,%d,\"%s\""),	// um nur einige zu nennen!
    pLogFont->lfHeight,
    pLogFont->lfEscapement,
    pLogFont->lfFaceName);
  TreeView_InsertItem(hContent,pChildInsert);
  INIBUF;
  ADDBUF T("TextAlign: "));
  buf=DecodeE((LPSTR)5,di->gi.TextAlign,buf,end);
  TreeView_InsertItem(hContent,pChildInsert);
  INIBUF;
  ADDBUF T("TextColor: %06X"),di->gi.TextColor);
  TreeView_InsertItem(hContent,pChildInsert);
 }
 if (dep&DEP_MM) {
  INIBUF;
  ADDBUF T("MapMode = "));
  buf=DecodeE((LPCSTR)2,di->gi.MapMode,buf,end);
  TreeView_InsertItem(hContent,pChildInsert);
  if (di->gi.Flags&GI_WINDOWORG_VALID) {
   INIBUF;
   VADDBUF T("WindowOrg: x=%d, y=%d"),(va_list)&di->gi.WindowOrg);
   TreeView_InsertItem(hContent,pChildInsert);
  }
  if (di->gi.Flags&GI_WINDOWEXT_VALID) {
   switch (di->gi.MapMode) {
    case MM_ISOTROPIC:
    case MM_ANISOTROPIC: {
     INIBUF;
     VADDBUF T("WindowExt: cx=%d, cy=%d"),(va_list)&di->gi.WindowExt);
     TreeView_InsertItem(hContent,pChildInsert);
    }break;
   }
  }
  if (di->gi.Flags&GI_VIEWPORTORG_VALID) {
   INIBUF;
   VADDBUF T("ViewportOrg: x=%d, y=%d"),(va_list)&di->gi.ViewportOrg);
   TreeView_InsertItem(hContent,pChildInsert);
  }
  if (di->gi.Flags&GI_VIEWPORTEXT_VALID) {
   switch (di->gi.MapMode) {
    case MM_ISOTROPIC:
    case MM_ANISOTROPIC: {
     INIBUF;
     VADDBUF T("ViewportExt: cx=%d, cy=%d"),(va_list)&di->gi.ViewportExt);
     TreeView_InsertItem(hContent,pChildInsert);
    }break;
   }
  }
 }
}

void WmfDecodeFunc(DECODEINFO*di, TVINSERTSTRUCT*pChildInsert, LPTSTR buf, LPCTSTR end) {
 LPSTR p,q,pType,pName,pArray,pNext,s=NULL;
 LPTSTR pBuf=buf;
 CHAR pointertype;
 DWORD hexval;	// für angehängte Hexadezimalausgabe
 int i,ArgIdx,ArrIdx,ArrLen;
 MULTIPOINTER pParam;
 PVOID pParamEnd;
 LPSTR aNames[MAXARGS];	// Merker für INT-Argumente (=Längen)
 long aValues[MAXARGS];

 pParam.v=&di->wmr;
 switch (di->Type) {
  case WMF_RECORD: {
   pParam.v=di->wmr.rdParm;
   s=(LPSTR)FuncNames[LOBYTE(di->wmr.rdFunction)];
   if (s) s=StrDupA(s);
  }break;
  case WMF_PLACEABLE_HEADER:
  case WMF_METAHEADER:
  case WMF_METAFILEPICT: {
   s=StrDupA(headers[di->Type-1]);
  }break;
  case WMF_GARBAGE: {
   ADDBUF T("Garbage 0x%X bytes"), di->dlen);
  }return;
  default: {
   FatalAppExit(0,T("Internal Logic"));
  }
 }
 if (!s) {
  if (!pChildInsert) ADDBUF T("unknown: rdSize=0x%X rdFunction=0x%X"),
    di->wmr.rdSize,di->wmr.rdFunction);
 }else{
  p=StrChrA(s,'(');
  if (p) {
   *p++=0;	// jetzt ist <s>=Funktionsname und <p> Parameterliste
   q=StrChrA(p,')');
   if (q) *q++=0;	// q zeigt hinter die schließende Klammer (= künftig Extra-Info)
  }else q=NULL;
  if (pChildInsert) {
   if ((Config.fopt&optRecordHead) && di->Type==WMF_RECORD) {
    ADDBUF T("rdSize=0x%X, rdFunction=0x%X (") SA T(")"),di->wmr.rdSize,di->wmr.rdFunction,s);
    TreeView_InsertItem(hContent,pChildInsert); buf=pBuf;
   }
  }else{
   ADDBUF SA T("("),s);
  }
  for (pType=p,ArgIdx=0; pType && *pType; pType=pNext,ArgIdx++) {
   pNext=StrChrA(pType,',');
   if (pNext) *pNext++=0;	// zerhacken
   pName=StrPBrkA(pType,"* ");
   if (pName) {
    pointertype=*pName;		// '*' == Zeiger
    *pName++=0;			// zerhacken, pName zeigt auf Name
   }else{
    pName=pType;		// ohne Typangabe nur Name
    pType=NULL;			// Typ immer <int>
    pointertype=0;		// kein Zeiger
   }
   pArray=StrChrA(pName,'[');
   if (pArray) {
    *pArray++=0;		// zerhacken, pArray zeigt auf Längen-Name
    pointertype='*';		// immer Zeigertyp
    p=StrChrA(pArray,']');
    if (p) *p=0;		// ohne ']' geht die Welt nicht
   }
   aNames[ArgIdx]=pName;
   aValues[ArgIdx]=*pParam.s;
   ArrLen=1;
   if (pointertype=='*') {
    ArrLen=StrToIntA(pArray);	// für feste Längen
    if (!ArrLen) {		// dann muss es ein Name sein, der vorher vorkam
     for (i=0; i<ArgIdx; i++) {	// der eigene Arrayname kommt nicht in Betracht!
      if (!lstrcmpA(pArray,aNames[i])) {
       ArrLen=aValues[i];	// da ist die Länge!
       break;
      }
     }
    }
   }
   for (ArrIdx=0; ArrIdx<ArrLen; ArrIdx++) {
    pBuf=buf;		// für ChildInsert, aber auch bei langen Listen immer von vorn anfangen
    if (pChildInsert){
     ADDBUF
       pointertype=='*' && lstrcmpA(pType,"CHAR") ? SA T("[%d] = ") : SA T(" = "),
       pName,ArrIdx);	// Name oder Name+Index ausgeben
    }
    if (!lstrcmpA(pType,"COLORREF")) {
     COLORREF cr=*pParam.color++;
     ADDBUF T("#%02X%02X%02X"),GetRValue(cr),GetGValue(cr),GetBValue(cr));
    }else if (!lstrcmpA(pType,"RECT")) {
     ADDBUF T("(%d,%d,%d,%d)"),pParam.rs->left,pParam.rs->top,pParam.rs->right,pParam.rs->bottom);
     pParam.rs++;
    }else if (pType && *pType=='E') {
     pType++;
     switch (*pType) {
      case 'B': {
       pType++;
       hexval=*pParam.uc++;
      }break;
      case 'L': {
       pType++;
       hexval=*pParam.ul++;
      }break;
      case 'W': {
       pType++;
      }nobreak;
      default: {
       hexval=*pParam.us++;
      }
     }
     buf=DecodeE(pType,hexval,buf,end);
// Enum zusätzlich als Hexzahl ausgeben
     goto AddHex;
    }else if (!lstrcmpA(pType,"POINT")) {
     if (!di->nPoints) {	// ersten Punkt gefunden: Punktliste einbauen
      di->nPoints=ArrLen;
      di->PointList.ps=pParam.ps;
      di->Flags|=DI_POINTS;
     }
     ADDBUF T("(%d,%d)"),pParam.ps->x,pParam.ps->y);
     pParam.ps++;
    }else if (!lstrcmpA(pType,"WORD")) {	// hexadezimal ausgeben
     ADDBUF T("0x%X"),*pParam.us++);
    }else if (!lstrcmpA(pType,"long")) {
     hexval=*pParam.ul++;
     ADDBUF T("%d"),(long)hexval);
     goto AddHex2;
    }else if (!lstrcmpA(pType,"DWORD")) {	// hexadezimal ausgeben
     hexval=*pParam.ul++;
     ADDBUF T("0x%X"),hexval);
    }else if (!lstrcmpA(pType,"OBJIDX")) {	// Typ ausgeben
     static char *TypNames[]={
      "PEN","BRUSH",NULL,NULL,"PAL","FONT","BITMAP","REGION"};
     hexval=*pParam.us++;
     ADDBUF T("%u"),hexval);
     if (pChildInsert) ADDBUF T(" (H") SA T(")"),TypNames[pObjTypes[hexval]-1]);
    }else if (!lstrcmpA(pType,"CHAR")) {
     ADDBUF T("\""));
     buf=EscapeStr(buf,end,pParam.c,ArrLen,ES_ZEROTERMINATED);	// eigentlich: von Fall zu Fall!
     ADDBUF T("\""));
     pParam.s+=(ArrLen+1)>>1;
     ArrLen=1;
    }else if (!lstrcmpA(pType,"FOURCC")) {
     hexval=*pParam.ul++;
     ADDBUF T("'"));
     *(LPDWORD)buf=hexval; buf+=4;
     ADDBUF T("'"));
AddHex:
     if (pChildInsert && hexval<10) ADDBUF T(" (%d)"),hexval);
AddHex2:	// Hexzahl ausgeben, wenn >=10
     if (pChildInsert && hexval>=10) ADDBUF T(" (0x%X)"),hexval);
    }else{	// Integer blank (dezimal vzb.)
     hexval=*pParam.us++;
     ADDBUF T("%d"),(short)hexval);
     goto AddHex2;
    }
// Einsame X/Y-Koordinate als Punkt (später)
    if (pChildInsert) {	// Zeile ausgeben
     TreeView_InsertItem(hContent,pChildInsert); buf=pBuf;
    }else{	// anhängen (wenn's nicht zu viel ist)
     if (ArrLen>4) buf=pBuf;
    }
   }/* Skalar oder Array abgearbeitet */
   if (!pChildInsert) {
    if (ArrLen>4) ADDBUF T("..."));	// ausgelassene Zahlen
    if (pNext && *pNext) ADDBUF T(","));// nächstes Argument
   }
  }/* Parameterliste abgearbeitet */
  if (!pChildInsert) ADDBUF T(")"));
// Abhängigkeiten listen
  if (q && *q && pChildInsert) {
   DWORD dep;
   HTREEITEM hSaveParent=pChildInsert->hParent;
   pChildInsert->item.cChildren=TRUE;
   ADDBUF T("depends"));
   pChildInsert->hParent=TreeView_InsertItem(hContent,pChildInsert); buf=pBuf;
   switch (*q) {
    case 'A': dep=DEP_PEN|DEP_ARCDIR|DEP_MM; break;
    case 'P': dep=DEP_PEN|DEP_BRUSH|DEP_POLYFILL|DEP_MM; break;
    case 'F': dep=DEP_PEN|DEP_BRUSH|DEP_MM; break;
    case 'L': dep=DEP_PEN|DEP_MM|DEP_POSITION; break;
    case 'T': dep=DEP_TEXT|DEP_MM; break;
    default: dep=0;
   }
   ListDep(di,pChildInsert,dep);
   pChildInsert->hParent=hSaveParent;
   pChildInsert->item.cChildren=FALSE;
  }
// Modifikationen listen?
  LocalFree(s);
 }
 if (di->Type == WMF_RECORD) {
// Überschüssige WORDs listen!
  pParamEnd=(short*)&di->wmr+di->wmr.rdSize;
  if (pChildInsert) for (; pParam.v<pParamEnd; pParam.s++) {
   ADDBUF T("rdParm[%d] = %d (0x%X)"),pParam.s-di->wmr.rdParm,*pParam.us,*pParam.us);
   TreeView_InsertItem(hContent,pChildInsert); buf=pBuf;
  }
// Zu lange Daten anzeigen!
  if (pParam.v>pParamEnd) {
   if (pChildInsert) {
    ADDBUF T("!!! nSize too short by %X WORDs !!!"),pParam.us-(PWORD)pParamEnd);
    TreeView_InsertItem(hContent,pChildInsert); buf=pBuf;
   }else ADDBUF T(" !"));
  }
 }
}

/* Prüft <di> auf GDI-Objekt-Erzeugung und trägt Objekt in <hObjects> ein */
void HandleObjectCreation(const DECODEINFO*di) {
 switch (di->Type) {
  case WMF_METAHEADER: {
   if (nObjects) {
    LocalFree(pObjTypes);
    LocalFree(pObjInfos);
   }
   nObjects=di->wmh.mtNoObjects;
   pObjTypes=LocalAlloc(LPTR,sizeof(BYTE)*nObjects);
   pObjInfos=LocalAlloc(LPTR,sizeof(OBJINFO)*nObjects);
   nCurObject=0;
   ZeroMemory(&NextGdiInfo,sizeof(NextGdiInfo));
  }break;

  case WMF_RECORD: {
   int ObjType=0;
   TVINSERTSTRUCT tvis;
   LPSTR s,p,k;
   s=(LPSTR)FuncNames[LOBYTE(di->wmr.rdFunction)];
   if (s) s=StrDupA(s);
   if (!s) return;	// ungültig
   p=StrChrA(s,'(');
   if (p) *p++=0;
   k=StrStrA(s,"Create");	// alle Funktionen mit "Create" erzeugen ein GDI-Objekt
   if (!k)  goto Ende1;
   if (StrStrA(s,"Pen")) ObjType=OBJ_PEN;
   else if (StrStrA(s,"Brush")) ObjType=OBJ_BRUSH;
   else if (StrStrA(s,"Palette")) ObjType=OBJ_PAL;
   else if (StrStrA(s,"Font")) ObjType=OBJ_FONT;
   else if (StrStrA(s,"Bitmap")) ObjType=OBJ_BITMAP;
   else if (StrStrA(s,"Region")) ObjType=OBJ_REGION;
   else goto Ende1;
   if (nCurObject<nObjects) {	// sonst Problem (Inkonsistenz)
    pObjTypes[nCurObject]=ObjType;
// hier bObjInfos[nCurObject] je nach Typ ausfüllen!
    switch (LOBYTE(di->wmr.rdFunction)) {
     case LOBYTE(META_CREATEPENINDIRECT): {
      LOGPEN *pLogPen=&pObjInfos[nCurObject].pen;
      pLogPen->lopnStyle=di->wmr.rdParm[0];
      pLogPen->lopnWidth.x=(short)di->wmr.rdParm[1];
      pLogPen->lopnWidth.y=(short)di->wmr.rdParm[2];
      pLogPen->lopnColor=*(COLORREF*)&di->wmr.rdParm[3];
     }break;
     case LOBYTE(META_CREATEBRUSHINDIRECT): {
      LOGBRUSH *pLogBrush=&pObjInfos[nCurObject].brush;
      pLogBrush->lbStyle=di->wmr.rdParm[0];
      pLogBrush->lbColor=*(COLORREF*)&di->wmr.rdParm[1];
      pLogBrush->lbHatch=di->wmr.rdParm[3];
     }break;
     case LOBYTE(META_CREATEFONTINDIRECT): {
      LOGFONT *pLogFont=&pObjInfos[nCurObject].font;
      pLogFont->lfHeight=(short)di->wmr.rdParm[0];
      pLogFont->lfEscapement=(short)di->wmr.rdParm[2];
#ifdef UNICODE
      MultiByteToWideChar(CP_ACP,0,(LPSTR)&di->wmr.rdParm[9],-1,pLogFont->lfFaceName,LF_FACESIZE);
#else
      lstrcpyn(pLogFont->lfFaceName,(LPSTR)&di->wmr.rdParm[9],LF_FACESIZE);
#endif
     }break;
    }
    nCurObject++;
   }
   tvis.hParent=TVI_ROOT;
   tvis.hInsertAfter=TVI_LAST;
   tvis.item.mask=TVIF_TEXT|TVIF_CHILDREN|TVIF_PARAM;
   {
    TCHAR w[32];
#ifdef UNICODE
    wnsprintf(w,elemof(w),T("%d: %S"),nCurObject,s+6);
#else
    wnsprintf(w,elemof(w),T("%d: %s"),nCurObject,s+6);
#endif
    tvis.item.pszText=w;
   }
   tvis.item.cChildren=0;
   tvis.item.lParam=(LPARAM)di;
   TreeView_InsertItem(hObjects,&tvis);
Ende1:
   LocalFree(s);
  }break;
 }
}

void HandleGdiInfoModify(const DECODEINFO*di) {
 if (di->Type != WMF_RECORD) return;
 switch (LOBYTE(di->wmr.rdFunction)) {
  case LOBYTE(META_SETBKCOLOR): {
   NextGdiInfo.BkColor=*(COLORREF*)di->wmr.rdParm;
   NextGdiInfo.Flags|=GI_BKCOLOR_VALID;
  }break;
  case LOBYTE(META_SETBKMODE): {
   NextGdiInfo.BkMode=(BYTE)di->wmr.rdParm[0];	// sollte !=0 sein
  }break;
  case LOBYTE(META_SETMAPMODE): {
   NextGdiInfo.MapMode=(BYTE)di->wmr.rdParm[0];
  }break;
  case LOBYTE(META_SETROP2): {
   NextGdiInfo.ROP2=(BYTE)di->wmr.rdParm[0];
  }break;
//#define META_SETRELABS               0x0105
  case LOBYTE(META_SETPOLYFILLMODE): {
   NextGdiInfo.PolyFillMode=(BYTE)di->wmr.rdParm[0];
  }break;
  case LOBYTE(META_SETSTRETCHBLTMODE): {
   NextGdiInfo.StretchBltMode=(BYTE)di->wmr.rdParm[0];
  }break;
  case LOBYTE(META_SETTEXTCHAREXTRA): {
   NextGdiInfo.TextCharExtra=di->wmr.rdParm[0];
  }break;
  case LOBYTE(META_SETTEXTCOLOR): {
   NextGdiInfo.TextColor=*(COLORREF*)di->wmr.rdParm;
  }break;
  case LOBYTE(META_SETTEXTJUSTIFICATION): {
   NextGdiInfo.TextJustify_BreakExtra=di->wmr.rdParm[0];
   NextGdiInfo.TextJustify_BreakCount=di->wmr.rdParm[1];
  }break;
  case LOBYTE(META_SETWINDOWORG): {
   NextGdiInfo.WindowOrg.x=(short)di->wmr.rdParm[1];
   NextGdiInfo.WindowOrg.y=(short)di->wmr.rdParm[0];
   NextGdiInfo.Flags|=GI_WINDOWORG_VALID;
  }break;
  case LOBYTE(META_SETWINDOWEXT): {
   NextGdiInfo.WindowExt.cx=(short)di->wmr.rdParm[1];
   NextGdiInfo.WindowExt.cy=(short)di->wmr.rdParm[0];
   NextGdiInfo.Flags|=GI_WINDOWEXT_VALID;
  }break;
  case LOBYTE(META_SETVIEWPORTORG): {
   NextGdiInfo.ViewportOrg.x=(short)di->wmr.rdParm[1];
   NextGdiInfo.ViewportOrg.y=(short)di->wmr.rdParm[0];
   NextGdiInfo.Flags|=GI_VIEWPORTORG_VALID;
  }break;
  case LOBYTE(META_SETVIEWPORTEXT): {
   NextGdiInfo.ViewportExt.cx=(short)di->wmr.rdParm[1];
   NextGdiInfo.ViewportExt.cy=(short)di->wmr.rdParm[0];
   NextGdiInfo.Flags|=GI_VIEWPORTEXT_VALID;
  }break;
  case LOBYTE(META_OFFSETWINDOWORG): {
   NextGdiInfo.WindowOrg.x+=(short)di->wmr.rdParm[1];
   NextGdiInfo.WindowOrg.y+=(short)di->wmr.rdParm[0];
  }break;
  case LOBYTE(META_SCALEWINDOWEXT): {
   NextGdiInfo.WindowExt.cx=MulDiv(di->gi.WindowExt.cx,
     (short)di->wmr.rdParm[3],(short)di->wmr.rdParm[2]);
   NextGdiInfo.WindowExt.cy=MulDiv(di->gi.WindowExt.cy,
     (short)di->wmr.rdParm[1],(short)di->wmr.rdParm[0]);
  }break;
  case LOBYTE(META_OFFSETVIEWPORTORG): {
   NextGdiInfo.ViewportOrg.x+=(short)di->wmr.rdParm[1];
   NextGdiInfo.ViewportOrg.y+=(short)di->wmr.rdParm[0];
  }break;
  case LOBYTE(META_SCALEVIEWPORTEXT): {
   NextGdiInfo.ViewportExt.cx=MulDiv(di->gi.ViewportExt.cx,
     (short)di->wmr.rdParm[3],(short)di->wmr.rdParm[2]);
   NextGdiInfo.ViewportExt.cy=MulDiv(di->gi.ViewportExt.cy,
     (short)di->wmr.rdParm[1],(short)di->wmr.rdParm[0]);
  }break;
  case LOBYTE(META_LINETO):
  case LOBYTE(META_MOVETO): {
   NextGdiInfo.Position.x=(short)di->wmr.rdParm[1];
   NextGdiInfo.Position.y=(short)di->wmr.rdParm[0];
   NextGdiInfo.Flags|=GI_POSITION_VALID;
  }break;
  case LOBYTE(META_SELECTOBJECT): {
// rdParm[0] muss <nCurObject sein, sonst Problem! (Inkonsistenz)
   int ObjType=pObjTypes[di->wmr.rdParm[0]];	// muss !=0 sein, sonst Problem!
   NextGdiInfo.objidx[ObjType-1]=(BYTE)di->wmr.rdParm[0];
  }break;
  case LOBYTE(META_SETTEXTALIGN): {
   NextGdiInfo.TextAlign=(BYTE)di->wmr.rdParm[0];
  }break;
 }
}

bool MfEnumInternal(BYTE Type, LPCVOID data, DWORD dlen) {
 TVINSERTSTRUCT tvis;
 DECODEINFO*di;
 di=LocalAlloc(LPTR,sizeof(DECODEINFO)-sizeof(METARECORD)+dlen);
 if (!di) return false;
// GDI-Info einfach kopieren
 RtlMoveMemory(&di->gi,&NextGdiInfo,sizeof(GDIINFO));
 RtlMoveMemory(&di->wmr,data,dlen);
 di->Type=Type;
 di->dlen=dlen;
 tvis.hParent=TVI_ROOT;
 tvis.hInsertAfter=TVI_LAST;
 tvis.item.mask=TVIF_TEXT|TVIF_CHILDREN|TVIF_PARAM;
 tvis.item.pszText=LPSTR_TEXTCALLBACK;
 tvis.item.cChildren=I_CHILDRENCALLBACK;
 tvis.item.lParam=(LPARAM)di;
 TreeView_InsertItem(hContent,&tvis);
 HandleObjectCreation(di);
 HandleGdiInfoModify(di);
 return true;
}

// Füllt die Baumansicht mit Items (ohne String-Generierung) und hinterlegt
// jedes Element mit einer Kopie des Meta-Records (anders geht es nicht,
// wenn man EnumMetaFile() verwendet!)
// Alles weitere ist Sache von Callback-Routinen
int _stdcall WmfEnumProc(HDC dc, HANDLETABLE *ht, METARECORD *wmr,
  int cObj, LPARAM lParam) {
 DWORD nSize=wmr->rdSize<<1;
 if (nSize<6) return MfEnumInternal(WMF_GARBAGE,wmr,nSize);	// trotzdem listen!
 return MfEnumInternal(WMF_RECORD,wmr,nSize);
}
Detected encoding: ANSI (CP1252)4
Wrong umlauts? - Assume file is ANSI (CP1252) encoded