/* 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
|
|