/****************************** Module Header ******************************\ * Module Name: MfRec16.c * * Copyright (c) 1991-1999 Microsoft Corporation * * DESCRIPTIVE NAME: Metafile Recorder * * FUNCTION: Records GDI functions in memory and disk metafiles. * * PUBLIC ENTRY POINTS: * CloseMetaFile * CopyMetaFile * CreateMetaFile * GetMetaFileBitsEx * SetMetaFileBitsEx * PRIVATE ENTRY POINTS: * RecordParms * AttemptWrite * MarkMetaFile * RecordObject * ProbeSize * AddObjectToDCTable * * History: * 02-Jul-1991 -by- John Colleran [johnc] * Combined From Win 3.1 and WLO 1.0 sources \***************************************************************************/ #include "precomp.h" #pragma hdrstop #include "mf16.h" UINT AddObjectToDCTable(HDC hdc, HANDLE hObject, PUINT piPosition, BOOL bRealAdd); BOOL AddDCToObjectMetaList16(HDC hMeta16DC, HANDLE hObject); BOOL AttemptWrite(PMFRECORDER16 pMFRec, DWORD dwBytes, LPBYTE lpData); VOID MarkMetaFile(PMFRECORDER16 pMFRec); BOOL MakeLogPalette(HDC hdc, HANDLE hPal, WORD magic); HANDLE ProbeSize(PMFRECORDER16 pMF, DWORD dwLength); BOOL RecordCommonBitBlt(HDC hdcDest, INT x, INT y, INT nWidth, INT nHeight, HDC hdcSrc, INT xSrc, INT ySrc, INT nSrcWidth, INT nSrcHeight, DWORD rop, WORD wFunc); BOOL UnlistObjects(HDC hMetaDC); BOOL MF16_DeleteRgn(HDC hdc, HANDLE hrgn); // Metafile Logging stubs for 3.x Metafiles /******************************Public*Routine******************************\ * XXX_RecordParms * * These routines package up the parameters of an NT GDI call and send * them to a general purpose recording routine that validates the metafile * DC and records the parameters. * * Returns * TRUE iff successful * * Warnings * Windows 3.x metafile behavior is that when a function is being metafiled * the routine itself is not called; eg SetPixel does not call GreSetPixel * but (GDI) SetPixel intercepts the calls and records the parameters and * returns without taking further action * * History: * 24-Nov-1991 -by- John Colleran [johnc] * Wrote it. \**************************************************************************/ BOOL MF16_RecordParms1(HDC hdc, WORD Func) { return RecordParms(hdc, Func, 0, (LPWORD)NULL); } BOOL MF16_RecordParms2(HDC hdc, INT parm2, WORD Func) { return RecordParms(hdc, Func, 1, (LPWORD)&parm2); } BOOL MF16_RecordParms3(HDC hdc, INT parm2, INT parm3, WORD Func) { WORD aw[2]; aw[0] = (WORD)parm3; aw[1] = (WORD)parm2; return RecordParms(hdc, Func, 2, aw); } BOOL MF16_RecordParms5(HDC hdc, INT parm2, INT parm3, INT parm4, INT parm5, WORD Func) { WORD aw[4]; aw[0] = (WORD)parm5; aw[1] = (WORD)parm4; aw[2] = (WORD)parm3; aw[3] = (WORD)parm2; return RecordParms(hdc, Func, 4, aw); } BOOL MF16_RecordParms7(HDC hdc, INT parm2, INT parm3, INT parm4, INT parm5, INT parm6, INT parm7, WORD Func) { WORD aw[6]; aw[0] = (WORD)parm7; aw[1] = (WORD)parm6; aw[2] = (WORD)parm5; aw[3] = (WORD)parm4; aw[4] = (WORD)parm3; aw[5] = (WORD)parm2; return RecordParms(hdc, Func, 6, aw); } BOOL MF16_RecordParms9(HDC hdc, INT parm2, INT parm3, INT parm4, INT parm5, INT parm6, INT parm7, INT parm8, INT parm9, WORD Func) { WORD aw[8]; aw[0] = (WORD)parm9; aw[1] = (WORD)parm8; aw[2] = (WORD)parm7; aw[3] = (WORD)parm6; aw[4] = (WORD)parm5; aw[5] = (WORD)parm4; aw[6] = (WORD)parm3; aw[7] = (WORD)parm2; return RecordParms(hdc, Func, 8, aw); } BOOL MF16_RecordParmsD(HDC hdc, DWORD d1, WORD Func) { return RecordParms(hdc, Func, 2, (LPWORD) &d1); } BOOL MF16_RecordParmsWWD(HDC hdc, WORD w1, WORD w2, DWORD d3, WORD Func) { WORD aw[4]; aw[0] = LOWORD(d3); aw[1] = HIWORD(d3); aw[2] = w2; aw[3] = w1; return RecordParms(hdc, Func, 4, aw); } BOOL MF16_RecordParmsWWDW(HDC hdc, WORD w1, WORD w2, DWORD d3, WORD w4, WORD Func) { WORD aw[5]; aw[0] = w4; aw[1] = LOWORD(d3); aw[2] = HIWORD(d3); aw[3] = w2; aw[4] = w1; return RecordParms(hdc, Func, 5, aw); } BOOL MF16_RecordParmsWWWWD(HDC hdc, WORD w1, WORD w2, WORD w3, WORD w4, DWORD d5, WORD Func) { WORD aw[6]; aw[0] = LOWORD(d5); aw[1] = HIWORD(d5); aw[2] = w4; aw[3] = w3; aw[4] = w2; aw[5] = w1; return RecordParms(hdc, Func, 6, aw); } BOOL MF16_RecordParmsPoly(HDC hdc, LPPOINT lpPoint, INT nCount, WORD Func) { BOOL fRet; LPWORD lpW,lpT; DWORD cw; INT ii; cw = (nCount*sizeof(POINTS)/sizeof(WORD))+1; lpT = lpW = (LPWORD)LocalAlloc(LMEM_FIXED, cw*sizeof(WORD)); if (!lpW) return(FALSE); *lpW++ = (WORD)nCount; for(ii=0; iibmiHeader.biSize == sizeof(BMCH)) { CopyCoreToInfoHeader ( lpDIBInfoHeader, (LPBITMAPCOREHEADER) pBitsInfoDib ); if (iUsageDib == DIB_RGB_COLORS) { RGBQUAD *prgbq; RGBTRIPLE *prgbt; UINT ui; prgbq = ((PBMI) lpDIBInfoHeader)->bmiColors; prgbt = ((PBMC) pBitsInfoDib)->bmciColors; ASSERTGDI(cbBitsInfoDib >= sizeof(BMIH), "MF16_RecordDIBits: Bad cbBitsInfoDib size"); for ( ui = (UINT) (cbBitsInfoDib - sizeof(BMIH)) / sizeof(RGBQUAD); ui; ui-- ) { prgbq->rgbBlue = prgbt->rgbtBlue; prgbq->rgbGreen = prgbt->rgbtGreen; prgbq->rgbRed = prgbt->rgbtRed; prgbq->rgbReserved = 0; prgbq++; prgbt++; } } else { RtlCopyMemory ( (PBYTE) lpDIBInfoHeader + sizeof(BMIH), (PBYTE) pBitsInfoDib + sizeof(BMCH), cbBitsInfoDib - sizeof(BMIH) ); } } else { RtlCopyMemory ( (PBYTE) lpDIBInfoHeader, (PBYTE) pBitsInfoDib, cbBitsInfoDib ); if (pBitsInfoDib->bmiHeader.biBitCount >= 16) { DWORD UNALIGNED *pClrUsed = (DWORD UNALIGNED *)&lpDIBInfoHeader->biClrUsed; *pClrUsed = 0; } } } // Copy dib bits. RtlCopyMemory((PBYTE) lpDIBInfoHeader + cbBitsInfoDib, pBitsDib, cbBitsDib); // Finally record the parameters into the file. fRet = RecordParms(hdcDst, mrType, cwParms + (cbBitsInfoDib + cbBitsDib + 1) / sizeof(WORD), lpWStart); if (lpWStart) if (LocalFree((HANDLE) lpWStart)) ASSERTGDI(FALSE, "MF16_RecordDIBits: LocalFree Failed\n"); return(fRet); } BOOL MF16_BitBlt(HDC hdcDest, INT x, INT y, INT nWidth, INT nHeight, HDC hdcSrc, INT xSrc, INT ySrc, DWORD rop) { WORD aw[9]; // This is how windows works but really it should look at the ROP if (hdcDest == hdcSrc || hdcSrc == NULL) { aw[0] = (WORD)LOWORD(rop); aw[1] = (WORD)HIWORD(rop); aw[2] = (WORD)ySrc; aw[3] = (WORD)xSrc; aw[4] = (WORD)0; // No DC necessary aw[5] = (WORD)nHeight; aw[6] = (WORD)nWidth; aw[7] = (WORD)y; aw[8] = (WORD)x; return(RecordParms(hdcDest, META_DIBBITBLT, 9, aw)); } else return(RecordCommonBitBlt(hdcDest,x,y,nWidth,nHeight,hdcSrc, xSrc,ySrc,nWidth,nHeight,rop,META_DIBBITBLT)); } BOOL MF16_StretchBlt(HDC hdcDest, INT x, INT y, INT nWidth, INT nHeight, HDC hdcSrc, INT xSrc, INT ySrc, INT nSrcWidth, INT nSrcHeight, DWORD rop) { WORD aw[11]; // This is how windows works but really it should look at the ROP if (hdcDest == hdcSrc || hdcSrc == NULL) { aw[0] = (WORD)LOWORD(rop); aw[1] = (WORD)HIWORD(rop); aw[2] = (WORD)nSrcHeight; aw[3] = (WORD)nSrcWidth; aw[4] = (WORD)ySrc; aw[5] = (WORD)xSrc; aw[6] = (WORD)0; // No DC necessary aw[7] = (WORD)nHeight; aw[8] = (WORD)nWidth; aw[9] = (WORD)y; aw[10] = (WORD)x; return(RecordParms(hdcDest, META_DIBSTRETCHBLT, 11, aw)); } else return(RecordCommonBitBlt(hdcDest,x,y,nWidth,nHeight,hdcSrc, xSrc,ySrc,nSrcWidth,nSrcHeight,rop,META_DIBSTRETCHBLT)); } BOOL RecordCommonBitBlt(HDC hdcDest, INT x, INT y, INT nWidth, INT nHeight, HDC hdcSrc, INT xSrc, INT ySrc, INT nSrcWidth, INT nSrcHeight, DWORD rop, WORD wFunc) { BOOL fRet = FALSE; HBITMAP hBitmap; LPWORD lpW; LPWORD lpWStart = (LPWORD) NULL; WORD cwParms; BMIH bmih; DWORD cbBitsInfo; DWORD cbBits; PBMIH lpDIBInfoHeader; // hdcSrc must be a memory DC. if (GetObjectType((HANDLE)hdcSrc) != OBJ_MEMDC) { ERROR_ASSERT(FALSE, "RecordCommonBitblt hdcSrc must be MEMDC\n"); GdiSetLastError(ERROR_INVALID_HANDLE); return(FALSE); } // Retrieve the source bitmap. hBitmap = SelectObject(hdcSrc, GetStockObject(PRIV_STOCK_BITMAP)); ERROR_ASSERT(hBitmap, "RecordCommonBitblt: SelectObject1 failed\n"); // Get the bitmap info header and sizes. if (!bMetaGetDIBInfo(hdcSrc, hBitmap, &bmih, &cbBitsInfo, &cbBits, DIB_RGB_COLORS, 0, TRUE)) goto RecordCommonBitBlt_exit; // Get the number of parameters to save. cwParms = (WORD) ((wFunc == META_DIBSTRETCHBLT) ? 10 : 8); // in words // Allocate space for DIB plus parameters. lpWStart = lpW = (LPWORD) LocalAlloc(LMEM_FIXED, cbBitsInfo + cbBits + cwParms*sizeof(WORD)); if (!lpW) { ERROR_ASSERT(FALSE, "RecordCommonBitblt: out of memory\n"); goto RecordCommonBitBlt_exit; } // Copy the parameters. *lpW++ = (WORD)LOWORD(rop); *lpW++ = (WORD)HIWORD(rop); if (wFunc == META_DIBSTRETCHBLT) { *lpW++ = (WORD)nSrcHeight; *lpW++ = (WORD)nSrcWidth; } *lpW++ = (WORD)ySrc; *lpW++ = (WORD)xSrc; *lpW++ = (WORD)nHeight; *lpW++ = (WORD)nWidth; *lpW++ = (WORD)y; *lpW++ = (WORD)x; // Save the start of the bitmap info header field. lpDIBInfoHeader = (LPBITMAPINFOHEADER) lpW; // Copy the bitmap info header. *lpDIBInfoHeader = bmih; // Get bitmap info and bits. if (!GetDIBits(hdcSrc, hBitmap, 0, (UINT) bmih.biHeight, (LPBYTE) ((PBYTE) lpW + cbBitsInfo), (LPBITMAPINFO) lpDIBInfoHeader, DIB_RGB_COLORS)) { ERROR_ASSERT(FALSE, "RecordCommonBitBlt: GetDIBits2 failed\n"); goto RecordCommonBitBlt_exit; } // Finally record the parameters into the file. fRet = RecordParms(hdcDest, wFunc, cwParms + (cbBitsInfo + cbBits) / sizeof(WORD), lpWStart); RecordCommonBitBlt_exit: if (lpWStart) if (LocalFree((HANDLE)lpWStart)) ASSERTGDI(FALSE, "RecordCommonBitBlt: LocalFree Failed\n"); if (!SelectObject(hdcSrc, hBitmap)) ASSERTGDI(FALSE, "RecordCommonBitblt: SelectObject2 failed\n"); return(fRet); } BOOL MF16_DeleteRgn(HDC hdc, HANDLE hrgn) { UINT pos; if (AddObjectToDCTable(hdc, hrgn, &pos, FALSE) != 1) ASSERTGDI(FALSE, "MF16_DeleteRgn: AddObjectToDCTable failed"); return(RecordParms(hdc, META_DELETEOBJECT, 1, (LPWORD)&pos)); } BOOL MF16_DeleteObject(HANDLE hObject) { INT iCurDC; UINT pos; UINT iObjType; PMFRECORDER16 pMFRec; PMETALINK16 pml16; pml16 = pmetalink16Get(hObject); ASSERTGDI(pml16, "MF16_DeleteObject: Metalink is NULL\n"); iObjType = GetObjectType(hObject); ASSERTGDI(iObjType != OBJ_REGION, "MF16_DeleteObject: region unexpected"); // Delete the object from each metafile DC which references it. for(iCurDC = pml16->cMetaDC16 - 1; iCurDC >= 0; iCurDC--) { // Send a DeleteObject record to each metafile HDC hdc16 = pml16->ahMetaDC16[iCurDC]; if (!IS_METADC16_TYPE(hdc16)) { RIP("MF16_DELETEOBJECT: invalid metaDC16\n"); continue; } // If the object is not selected then delete it, if it is then mark as predeleted GET_PMFRECORDER16(pMFRec,hdc16); if (pMFRec->recCurObjects[iObjType - MIN_OBJ_TYPE] != hObject) { if (AddObjectToDCTable(hdc16, hObject, &pos, FALSE) == 1) RecordParms(hdc16, META_DELETEOBJECT, 1, (LPWORD)&pos); else RIP("MF16_DeleteObject Metalink16 and metadc table not in sync\n"); } else { if (pMFRec->metaHeader.mtNoObjects) { UINT ii; POBJECTTABLE pobjt; pobjt = (POBJECTTABLE) pMFRec->hObjectTable; for (ii=0; ii < (UINT) pMFRec->metaHeader.mtNoObjects; ii++) { if (pobjt[ii].CurHandle == hObject) { pobjt[ii].fPreDeleted = TRUE; break; } } } } } // This Object has been freed from all 3.x metafiles so free its MetaList16 // if the metalink field is in use resize METALINK16 if (pml16->metalink) { if (pml16->cMetaDC16 > 1) pml16 = pmetalink16Resize(hObject,1); if (pml16 == NULL) { ASSERTGDI(FALSE, "MF16_DeleteObject LocalReAlloc failed\n"); return (FALSE); } pml16->cMetaDC16 = 0; pml16->ahMetaDC16[0] = (HDC) 0; } else { if (!bDeleteMetalink16(hObject)) ASSERTGDI(FALSE, "MF16_DeleteObject LocalFree failed\n"); } return(TRUE); } BOOL MF16_RealizePalette(HDC hdc) { HPALETTE hpal; PMFRECORDER16 pMFRec; PMETALINK16 pml16; ASSERTGDI(IS_METADC16_TYPE(hdc),"MF16_RealizePalette - invalid handle\n"); GET_PMFRECORDER16(pMFRec,hdc); hpal = pMFRec->recCurObjects[OBJ_PAL - MIN_OBJ_TYPE]; ASSERTGDI(hpal, "MF16_RealizePalette: bad hpal\n"); // emit the palette again only if the palette is dirty. pml16 = pmetalink16Get(hpal); ASSERTGDI(IS_STOCKOBJ(hpal) || pml16,"MF16_RealizePalette - pml16 == NULL\n"); if (pml16) { if (PtrToUlong(pml16->pv) != pMFRec->iPalVer) if (!MakeLogPalette(hdc, hpal, META_SETPALENTRIES)) return(FALSE); // record which version of the palette the metafile is synced to. pMFRec->iPalVer = PtrToUlong(pml16->pv); } return(RecordParms(hdc, META_REALIZEPALETTE, 0, (LPWORD)NULL)); } BOOL MF16_AnimatePalette ( HPALETTE hpal, UINT iStart, UINT cEntries, CONST PALETTEENTRY *pPalEntries ) { INT iCurDC; PMETALINK16 pml16; LPWORD lpW,lpT; DWORD cw; UINT ii; PMFRECORDER16 pMFRec; if (!(pml16 = pmetalink16Get(hpal))) return(FALSE); cw = (cEntries * sizeof(PALETTEENTRY) / sizeof(WORD)) + 2; lpT = lpW = (LPWORD) LocalAlloc(LMEM_FIXED, cw * sizeof(WORD)); if (!lpW) return(FALSE); *lpW++ = (WORD) iStart; *lpW++ = (WORD) cEntries; for (ii = 0; ii < cEntries; ii++) ((PPALETTEENTRY) lpW)[ii] = pPalEntries[ii]; // Send a AnimatePalette record to each associated metafile that has the // palette selected. for (iCurDC = pml16->cMetaDC16 - 1; iCurDC >= 0; iCurDC--) { HDC hdc16 = pml16->ahMetaDC16[iCurDC]; if (!IS_METADC16_TYPE(hdc16)) { ASSERTGDI(FALSE, "MF16_AnimatePalette: invalid metaDC16\n"); continue; } GET_PMFRECORDER16(pMFRec,hdc16); if (pMFRec->recCurObjects[OBJ_PAL - MIN_OBJ_TYPE] == hpal) if (!RecordParms(pml16->ahMetaDC16[iCurDC], META_ANIMATEPALETTE, cw, lpT)) ASSERTGDI(FALSE, "MF16_AnimatePalette: RecordParms Failed\n"); } if (LocalFree((HANDLE) lpT)) ASSERTGDI(FALSE, "MF16_AnimatePalette: LocalFree Failed\n"); return(TRUE); } BOOL MF16_ResizePalette(HPALETTE hpal, UINT nCount) { INT iCurDC; PMETALINK16 pml16; PMFRECORDER16 pMFRec; if (!(pml16 = pmetalink16Get(hpal))) return(FALSE); // Send a ResizePalette record to each associated metafile that has the // palette selected. for (iCurDC = pml16->cMetaDC16 - 1; iCurDC >= 0; iCurDC--) { HDC hdc16 = pml16->ahMetaDC16[iCurDC]; if (!IS_METADC16_TYPE(hdc16)) { ASSERTGDI(FALSE, "MF16_ResizePalette: invalid metaDC16\n"); continue; } GET_PMFRECORDER16(pMFRec,hdc16); if (pMFRec->recCurObjects[OBJ_PAL - MIN_OBJ_TYPE] == hpal) if (!RecordParms(pml16->ahMetaDC16[iCurDC], META_RESIZEPALETTE, 1, (LPWORD) &nCount)) ASSERTGDI(FALSE, "MF16_ResizePalette: RecordParms Failed\n"); } return(TRUE); } BOOL MF16_DrawRgn(HDC hdc, HRGN hrgn, HBRUSH hBrush, INT cx, INT cy, WORD Func) { WORD aw[4]; BOOL bRet; // Each region function has at least a region to record aw[0] = (WORD)RecordObject(hdc, hrgn); switch(Func) { case META_PAINTREGION: case META_INVERTREGION: bRet = RecordParms(hdc, Func, 1, aw); break; case META_FILLREGION: aw[1] = (WORD)RecordObject(hdc, hBrush); bRet = RecordParms(hdc, Func, 2, aw); break; case META_FRAMEREGION: aw[1] = (WORD)RecordObject(hdc, hBrush); aw[2] = (WORD)cy; aw[3] = (WORD)cx; bRet = RecordParms(hdc, Func, 4, aw); break; default: ASSERTGDI(FALSE, "MF16_DrawRgn: Bad Func\n"); bRet = FALSE; break; } // Delete the metafile region handle in the metafile after use! // The reason is that a region can be modified (e.g. SetRectRgn) // between each use and we have to re-record it each time it is used // unless we use a dirty flag. if (!MF16_DeleteRgn(hdc, hrgn)) ASSERTGDI(FALSE, "MF16_DrawRgn: MF16_DeleteRgn failed\n"); return(bRet); } BOOL MF16_PolyPolygon(HDC hdc, CONST POINT *lpPoint, CONST INT *lpPolyCount, INT nCount) { BOOL fRet; LPWORD lpW,lpT; DWORD cw; INT cPt = 0; INT ii; for(ii=0; iirecCurObjects[OBJ_REGION - MIN_OBJ_TYPE] = 0; return(fRet); #else return TRUE; #endif } else return(MF16_SelectObject(hdc, hrgn) ? TRUE : FALSE); } // SelectObject returns previous object! - new in win3.1 HANDLE MF16_SelectObject(HDC hdc, HANDLE h) { HANDLE hOldObject; WORD position; PMFRECORDER16 pMFRec; UINT iType; PUTS("MF16_SelectObject\n"); GET_PMFRECORDER16(pMFRec,hdc); if (!IS_METADC16_TYPE(hdc) || !pMFRec) { GdiSetLastError(ERROR_INVALID_HANDLE); return(0); } iType = GetObjectType(h); if ((iType == 0) || !h || (position = (WORD) RecordObject(hdc, h)) == (WORD) -1) return((HANDLE) 0); else { if (!RecordParms(hdc, META_SELECTOBJECT, 1, &position)) return((HANDLE) 0); // maintain the new selection in the CurObject table ASSERTGDI(iType <= MAX_OBJ_TYPE && iType >= MIN_OBJ_TYPE, "MF16_SelectObject type > max\n"); hOldObject = pMFRec->recCurObjects[iType - MIN_OBJ_TYPE]; pMFRec->recCurObjects[iType - MIN_OBJ_TYPE] = h; // return the previously selected object or 1 if it is a region // (for compatibility) - new in win3.1 if (iType == OBJ_REGION) { // We also delete the region handle here! // The reason is that a region can be modified (e.g. SetRectRgn) // between each use and we have to re-record it each time it is used // unless we use a dirty flag. This is a bug in win3.1 return(MF16_DeleteRgn(hdc, h) ? (HANDLE) 1 : (HANDLE) 0); } else { return(hOldObject); } } } BOOL MF16_SelectPalette(HDC hdc, HPALETTE hpal) { WORD position; PMFRECORDER16 pMFRec; GET_PMFRECORDER16(pMFRec,hdc); if (!IS_METADC16_TYPE(hdc) || !pMFRec) { GdiSetLastError(ERROR_INVALID_HANDLE); return(0); } if (!hpal || (position = (WORD) RecordObject(hdc, (HANDLE) hpal)) == (WORD) -1) return(FALSE); else { PMETALINK16 pml16; if (!RecordParms(hdc, META_SELECTPALETTE, 1, &position)) return(FALSE); // maintain the new selection in the CurObject table pMFRec->recCurObjects[OBJ_PAL - MIN_OBJ_TYPE] = hpal; // Also record which version of the palette we are synced with // so we know whether to emit a new palette when RealizePalette // is called pml16 = pmetalink16Get(hpal); ASSERTGDI(IS_STOCKOBJ(hpal) || pml16,"MF16_RealizePalette - pml16 == NULL\n"); if (pml16) pMFRec->iPalVer = PtrToUlong(pml16->pv); return(TRUE); } } BOOL MF16_TextOut(HDC hdc, INT x, INT y, LPCSTR lpString, INT nCount, BOOL bUnicode) { BOOL fRet; LPWORD lpw, lpT; DWORD cw; cw = (nCount + 1)/sizeof(WORD) + 3; // word-aligned character string lpT = lpw = (LPWORD) LocalAlloc(LMEM_FIXED, cw*sizeof(WORD)); if (!lpw) return(FALSE); *lpw++ = (WORD)nCount; // Copy the string if (!bUnicode) { RtlCopyMemory(lpw, lpString, nCount); } else { (void) bToASCII_N((LPSTR) lpw, nCount, (LPWSTR) lpString, nCount); } lpw += (nCount+1)/sizeof(WORD); // keep word aligned *lpw++ = (WORD)y; *lpw++ = (WORD)x; fRet = RecordParms(hdc, META_TEXTOUT, cw, lpT); if(LocalFree((HANDLE)lpT)) ASSERTGDI(FALSE, "MF16_TextOut: LocalFree Failed\n"); return (fRet); } BOOL MF16_PolyTextOut(HDC hdc, CONST POLYTEXTA *ppta, int cpta, BOOL bUnicode) { int i; for (i = 0; i < cpta; i++) { if (!MF16_ExtTextOut(hdc, ppta[i].x, ppta[i].y, ppta[i].uiFlags, &ppta[i].rcl, (LPCSTR) ppta[i].lpstr, (INT) ppta[i].n, (LPINT) ppta[i].pdx, bUnicode)) return(FALSE); } return(TRUE); } BOOL MF16_ExtTextOut(HDC hdc, INT x, INT y, UINT flOptions, CONST RECT *lpRect, LPCSTR lpString, INT nCount, CONST INT *lpDX, BOOL bUnicode) { BOOL fRet; LPWORD lpw, lpT; DWORD cw = 0; INT nUnicodeCount = nCount; char *pjAnsiString = NULL; if(bUnicode) { // compute the real count of characters in the string RtlUnicodeToMultiByteSize((PULONG) &nCount, (PWCH) lpString, nCount * sizeof(WCHAR)); } // Compute buffer space needed // room for the char string // room for the 4 words that are the fixed parms // if there is a dx array, we need room for it // if the rectangle is being used, we need room for it // and we need extra byte for eventual word roundoff // if (flOptions & ETO_PDY) return FALSE; cw += (lpDX) ? nCount : 0; // DX array cw += (flOptions & (ETO_OPAQUE | ETO_CLIPPED)) ? 4 : 0; // sizeof RECTS cw += 4; // x,y,options and count cw += (nCount + 1)/sizeof(WORD); lpT = lpw = (LPWORD) LocalAlloc(LMEM_FIXED, cw*sizeof(WORD)); if (!lpw) return(FALSE); *lpw++ = (WORD)y; *lpw++ = (WORD)x; *lpw++ = (WORD)nCount; *lpw++ = (WORD)flOptions; // Copy the rect if present if (flOptions & (ETO_OPAQUE | ETO_CLIPPED)) { ERROR_ASSERT(lpRect, "MF16_ExtTextOut: expect valid lpRect\n"); *lpw++ = (WORD)lpRect->left; *lpw++ = (WORD)lpRect->top; *lpw++ = (WORD)lpRect->right; *lpw++ = (WORD)lpRect->bottom; } // Copy the string if (!bUnicode) { RtlCopyMemory(lpw, lpString, nCount); } else { (void) bToASCII_N((LPSTR) lpw, nCount, (LPWSTR) lpString, nUnicodeCount); pjAnsiString = (char*) lpw; } lpw += (nCount+1)/sizeof(WORD); // keep word aligned if (lpDX) { INT ii; if(nCount != nUnicodeCount) { INT jj; for(ii=0,jj=0; ii < nCount; ii++,jj++) { *lpw++ = (WORD)lpDX[jj]; if(IsDBCSLeadByte(pjAnsiString[ii])) { *lpw++ = 0; ii++; } } } else { for(ii=0; ii sizeof(META_ESCAPE_ENHANCED_METAFILE) - sizeof(DWORD) - 3 * sizeof(WORD) && ((DWORD UNALIGNED *) lpInData)[0] == MFCOMMENT_IDENTIFIER && ((DWORD UNALIGNED *) lpInData)[1] == MFCOMMENT_ENHANCED_METAFILE) { return(TRUE); } // Some wow apps (e.g. amipro) use metafiles for printing. As a result, // we need to record these printing escapes. cw = 2 + ((nCount + 1) / sizeof(WORD)); lpT = lpW = (LPWORD) LocalAlloc(LMEM_FIXED, cw * sizeof(WORD)); if (!lpW) return(FALSE); *lpW++ = (WORD) nEscape; // escape number *lpW++ = (WORD) nCount; // count of input data buffer RtlCopyMemory(lpW, lpInData, nCount); fRet = RecordParms(hdc, META_ESCAPE, cw, lpT); if (LocalFree((HANDLE) lpT)) ASSERTGDI(FALSE, "MF16_Escape: LocalFree Failed\n"); return(fRet); } /**************************************************************************** * * * RecordParms * * * * Parameters: 1.hMF handle to a metafile header. * * 2.The magic number of the function being recorded. * * 3.The number of parmmeter of the function (size of lpParm * * in words) * * 4.A long pointer to parameters stored in reverse order * * * * Warning call only once per function because max record is updated. * * * ****************************************************************************/ BOOL RecordParms(HDC hdc, DWORD magic, DWORD cw, CONST WORD *lpParm) { PMFRECORDER16 pMFRec; METARECORD MFRecord; PUTSX("RecordParms %lX\n", (ULONG)magic); ASSERTGDI(HIWORD(magic) == 0, "RecordParms: bad magic\n"); GET_PMFRECORDER16(pMFRec,hdc); if (!IS_METADC16_TYPE(hdc) || !pMFRec) { GdiSetLastError(ERROR_INVALID_HANDLE); return(FALSE); } // Make sure the Metafile hasn't died before we continue if (!(pMFRec->recFlags & METAFILEFAILURE)) { MFRecord.rdSize = SIZEOF_METARECORDHEADER/sizeof(WORD) + cw; MFRecord.rdFunction = (WORD)magic; // Write the header if (!AttemptWrite(pMFRec, SIZEOF_METARECORDHEADER, (LPBYTE)&MFRecord)) return(FALSE); // Write the data if (!AttemptWrite(pMFRec, cw*sizeof(WORD), (LPBYTE)lpParm)) return(FALSE); // Update max record size if (MFRecord.rdSize > pMFRec->metaHeader.mtMaxRecord) pMFRec->metaHeader.mtMaxRecord = MFRecord.rdSize; } return (TRUE); // Win 3.1 returns true even if METAFAILEFAILURE is on! } /***************************** Internal Function ***************************\ * AttemptWrite * * Tries to write data to a metafile disk file * * dwBytes is the byte count of lpData. * * Returns TRUE iff the write was sucessful * * \***************************************************************************/ BOOL AttemptWrite(PMFRECORDER16 pMFRec, DWORD dwBytes, LPBYTE lpData) { DWORD cbWritten; BOOL fRet; PUTS("AttemptWrite\n"); ASSERTGDI(dwBytes % 2 == 0, "AttemptWrite: bad dwBytes\n"); // must be even ASSERTGDI(!(pMFRec->recFlags & METAFILEFAILURE), "AttemptWrite: Bad recording\n"); // Handle disk file. if (pMFRec->metaHeader.mtType == DISKMETAFILE) { // Flush the buffer if it's not large enough. if (dwBytes + pMFRec->ibBuffer > pMFRec->cbBuffer) { fRet = WriteFile(pMFRec->hFile, (LPBYTE)pMFRec->hMem, pMFRec->ibBuffer, &cbWritten, (LPOVERLAPPED)NULL); if (!fRet || (cbWritten != pMFRec->ibBuffer)) { ERROR_ASSERT(FALSE, "AttemptWrite: Write1 failed\n"); goto AttemptWrite_Error; } pMFRec->ibBuffer = 0; // reset buffer info } // If the data is still too large, write it out to disk directly. if (dwBytes + pMFRec->ibBuffer > pMFRec->cbBuffer) { fRet = WriteFile(pMFRec->hFile, lpData, dwBytes, &cbWritten, (LPOVERLAPPED)NULL); if (!fRet || (cbWritten != dwBytes)) { ERROR_ASSERT(FALSE, "AttemptWrite: Write2 failed\n"); goto AttemptWrite_Error; } } else { // Store data in the buffer. RtlCopyMemory((LPBYTE)pMFRec->hMem + pMFRec->ibBuffer, lpData, dwBytes); pMFRec->ibBuffer += dwBytes; } } else { // Handle memory file. // Grow the buffer if necessary. if (dwBytes + pMFRec->ibBuffer > pMFRec->cbBuffer) { DWORD cbNewSize; HANDLE hMem; cbNewSize = pMFRec->cbBuffer + MF16_BUFSIZE_INC + dwBytes / MF16_BUFSIZE_INC * MF16_BUFSIZE_INC; hMem = LocalReAlloc(pMFRec->hMem, cbNewSize, LMEM_MOVEABLE); if (hMem == NULL) { ERROR_ASSERT(FALSE, "AttemptWrite: out of memory\n"); goto AttemptWrite_Error; } pMFRec->hMem = hMem; pMFRec->cbBuffer = cbNewSize; } // Record the data. RtlCopyMemory((LPBYTE)pMFRec->hMem + pMFRec->ibBuffer, lpData, dwBytes); pMFRec->ibBuffer += dwBytes; } // Update the header size. pMFRec->metaHeader.mtSize += dwBytes/sizeof(WORD); return(TRUE); AttemptWrite_Error: MarkMetaFile(pMFRec); return(FALSE); } /***************************** Internal Function ***************************\ * VOID MarkMetaFile * * Marks a metafile as failed * * Effects: * Frees the metafile resources * \***************************************************************************/ VOID MarkMetaFile(PMFRECORDER16 pMFRec) { // Clean up is done in CloseMetaFile. PUTS("MarkMetaFile\n"); pMFRec->recFlags |= METAFILEFAILURE; } /***************************** Internal Function **************************\ * MakeLogPalette * * Records either CreatePalette or SetPaletteEntries * * Returns TRUE iff sucessful * * \***************************************************************************/ BOOL MakeLogPalette(HDC hdc, HANDLE hPal, WORD magic) { WORD cPalEntries; BOOL fStatus = FALSE; DWORD cbPalette; LPLOGPALETTE lpPalette; PUTS("MakeLogPalette\n"); if (!GetObject(hPal, sizeof(WORD), &cPalEntries)) { ERROR_ASSERT(FALSE, "MakeLogPalette: GetObject Failed\n"); return(fStatus); } // alloc memory and get the palette entries if (lpPalette = (LPLOGPALETTE)LocalAlloc(LMEM_FIXED, cbPalette = sizeof(LOGPALETTE)-sizeof(PALETTEENTRY)+sizeof(PALETTEENTRY)*cPalEntries)) { lpPalette->palNumEntries = cPalEntries; GetPaletteEntries(hPal, 0, cPalEntries, lpPalette->palPalEntry); if (magic == (META_CREATEPALETTE & 255)) { lpPalette->palVersion = 0x300; magic = META_CREATEPALETTE; } else if (magic == (META_SETPALENTRIES & 255)) { lpPalette->palVersion = 0; /* really "starting index" */ magic = META_SETPALENTRIES; } fStatus = RecordParms(hdc, magic, (DWORD)cbPalette >> 1, (LPWORD)lpPalette); if (LocalFree((HANDLE)lpPalette)) ASSERTGDI(FALSE, "MakeLogPalette: LocalFree Failed\n"); } return(fStatus); } /***************************** Internal Function ***************************\ * RecordObject * * Records the use of an object by creating the object * * Returns: index of object in table * -1 if error * \***************************************************************************/ WIN3REGION w3rgnEmpty = { 0, // nextInChain 6, // ObjType 0x2F6, // ObjCount sizeof(WIN3REGION) - sizeof(SCAN) + 2, // cbRegion 0, // cScans 0, // maxScan {0,0,0,0}, // rcBounding {0,0,0,{0,0},0} // aScans[] }; WORD RecordObject(HDC hdc, HANDLE hObject) { UINT status; UINT iPosition; HDC hdcScreen = (HDC) 0; int iType; UINT iUsage; PUTS("RecordObject\n"); // Validate the object. iType = LO_TYPE(hObject); if (iType != LO_PEN_TYPE && iType != LO_BRUSH_TYPE && iType != LO_FONT_TYPE && iType != LO_REGION_TYPE && iType != LO_PALETTE_TYPE ) { GdiSetLastError(ERROR_INVALID_HANDLE); return((WORD) -1); } // Add the object to the metafiles list. status = AddObjectToDCTable(hdc, hObject, &iPosition, TRUE); // An error occurred. if (status == (UINT) -1) return((WORD) -1); // Object already exists. if (status == 1) return((WORD) iPosition); ASSERTGDI(!status, "RecordObject: Bad return code from AddObjectToDCTable\n"); // Object does not exist, record it. if (iType != LO_REGION_TYPE) // don't add regions to the metalist! if (!AddDCToObjectMetaList16(hdc,hObject)) return((WORD) -1); switch (iType) { case LO_PEN_TYPE: { LOGPEN16 logpen16; GetObject16AndType(hObject, (LPVOID)&logpen16); status = (UINT) RecordParms(hdc, (WORD)META_CREATEPENINDIRECT, (DWORD)((sizeof(LOGPEN16) + 1) >> 1), (LPWORD)&logpen16); break; } case LO_FONT_TYPE: { LOGFONT16 logfont16; GetObject16AndType(hObject, (LPVOID)&logfont16); /* size of LOGFONT adjusted based on the length of the facename */ status = (UINT) RecordParms(hdc, META_CREATEFONTINDIRECT, (DWORD)((sizeof(LOGFONT16) + 1) >> 1), (LPWORD) &logfont16); break; } /* * in win2, METACREATEREGION records contained an entire region object, * including the full header. this header changed in win3. * * to remain compatible, the region records will be saved with the * win2 header. here we save our region with a win2 header. */ case LO_REGION_TYPE: { PWIN3REGION lpw3rgn; DWORD cbNTRgnData; DWORD curRectl; WORD cScans; WORD maxScanEntry; WORD curScanEntry; DWORD cbw3data; PRGNDATA lprgn; LPRECT lprc; PSCAN lpScan; ASSERTGDI(!status, "RecordObject: bad status\n"); // Get the NT Region Data cbNTRgnData = GetRegionData(hObject, 0, NULL); if (cbNTRgnData == 0) break; lprgn = (PRGNDATA) LocalAlloc(LMEM_FIXED, cbNTRgnData); if (!lprgn) break; cbNTRgnData = GetRegionData(hObject, cbNTRgnData, lprgn); if (cbNTRgnData == 0) { LocalFree((HANDLE) lprgn); break; } // Handle the empty region. if (!lprgn->rdh.nCount) { status = (UINT) RecordParms(hdc, META_CREATEREGION, (sizeof(WIN3REGION) - sizeof(SCAN)) >> 1, // Convert to count of words (LPWORD) &w3rgnEmpty); LocalFree((HANDLE)lprgn); break; } lprc = (LPRECT)lprgn->Buffer; // Create the Windows 3.x equivalent // worst case is one scan for each rect cbw3data = 2*sizeof(WIN3REGION) + (WORD)lprgn->rdh.nCount*sizeof(SCAN); lpw3rgn = (PWIN3REGION)LocalAlloc(LMEM_FIXED, cbw3data); if (!lpw3rgn) { LocalFree((HANDLE) lprgn); break; } // Grab the bounding rect. lpw3rgn->rcBounding.left = (SHORT)lprgn->rdh.rcBound.left; lpw3rgn->rcBounding.right = (SHORT)lprgn->rdh.rcBound.right; lpw3rgn->rcBounding.top = (SHORT)lprgn->rdh.rcBound.top; lpw3rgn->rcBounding.bottom = (SHORT)lprgn->rdh.rcBound.bottom; cbw3data = sizeof(WIN3REGION) - sizeof(SCAN) + 2; // visit all the rects curRectl = 0; cScans = 0; maxScanEntry = 0; lpScan = lpw3rgn->aScans; while(curRectl < lprgn->rdh.nCount) { LPWORD lpXEntry; DWORD cbScan; curScanEntry = 0; // Current X pair in this scan lpScan->scnPntTop = (WORD)lprc[curRectl].top; lpScan->scnPntBottom = (WORD)lprc[curRectl].bottom; lpXEntry = (LPWORD) lpScan->scnPntsX; // handle rects on this scan do { lpXEntry[curScanEntry + 0] = (WORD)lprc[curRectl].left; lpXEntry[curScanEntry + 1] = (WORD)lprc[curRectl].right; curScanEntry += 2; curRectl++; } while ((curRectl < lprgn->rdh.nCount) && (lprc[curRectl-1].top == lprc[curRectl].top) && (lprc[curRectl-1].bottom == lprc[curRectl].bottom) ); lpScan->scnPntCnt = curScanEntry; lpXEntry[curScanEntry] = curScanEntry; // Count also follows Xs cScans++; if (curScanEntry > maxScanEntry) maxScanEntry = curScanEntry; // account for each new scan + each X1 X2 Entry but the first cbScan = sizeof(SCAN)-(sizeof(WORD)*2) + (curScanEntry*sizeof(WORD)); cbw3data += cbScan; lpScan = (PSCAN)(((LPBYTE)lpScan) + cbScan); } // Initialize the header lpw3rgn->nextInChain = 0; lpw3rgn->ObjType = 6; // old Windows OBJ_RGN identifier lpw3rgn->ObjCount= 0x2F6; // any non-zero number lpw3rgn->cbRegion = (WORD)cbw3data; // don't count type and next lpw3rgn->cScans = cScans; lpw3rgn->maxScan = maxScanEntry; status = (UINT) RecordParms(hdc, META_CREATEREGION, (cbw3data-2) >> 1, // Convert to count of words (LPWORD) lpw3rgn); if (LocalFree((HANDLE)lprgn)) ASSERTGDI(FALSE, "RecordObject: LocalFree(lprgn) Failed\n"); if (LocalFree((HANDLE)lpw3rgn)) ASSERTGDI(FALSE, "RecordObject: LocalFree(lpw3rgn) Failed\n"); break; } case LO_BRUSH_TYPE: { LOGBRUSH lb; if (!GetObjectA(hObject, sizeof(LOGBRUSH), &lb)) break; switch (lb.lbStyle) { case BS_HATCHED: case BS_HOLLOW: case BS_SOLID: { LOGBRUSH16 lb16; LOGBRUSH16FROMLOGBRUSH32(&lb16, &lb); // non-pattern brush status = (UINT) RecordParms(hdc, META_CREATEBRUSHINDIRECT, (DWORD) ((sizeof(LOGBRUSH16) + 1) >> 1), (LPWORD) &lb16); break; } case BS_PATTERN: case BS_DIBPATTERN: case BS_DIBPATTERNPT: { HBITMAP hbmRemote; BMIH bmih; DWORD cbBitsInfo; DWORD cbBits; LPWORD lpWStart, lpW; DWORD lbStyle = BS_DIBPATTERN; PBMIH lpDIBInfoHeader; if (!(hbmRemote = GetObjectBitmapHandle((HBRUSH) hObject, &iUsage))) { ASSERTGDI(FALSE, "RecordObject: GetObjectBitmapHandle failed"); break; } // For a pattern brush, if it is color, it is recorded as a // DIB pattern brush with BS_DIBPATTERN style. If it is // monochrome, it is recorded as a DIB pattern brush with // BS_PATTERN style. The playback code has a special // case to deal with monochrome brushes. if (lb.lbStyle == BS_PATTERN) { iUsage = DIB_RGB_COLORS; if (MonoBitmap(hbmRemote)) lbStyle = BS_PATTERN; } hdcScreen = CreateCompatibleDC((HDC) 0); // freed below // Get the bitmap info header and sizes. if (!bMetaGetDIBInfo(hdcScreen, hbmRemote, &bmih, &cbBitsInfo, &cbBits, iUsage, 0, TRUE)) break; // Make sure that cbBitsInfo is dword aligned // If we have converted the bitmap format in bMetaGetDIBInfo, // modify the iUsage to match the new format. if (bmih.biBitCount == 24) iUsage = DIB_RGB_COLORS; // Allocate space for DIB plus parameters. lpWStart = lpW = (LPWORD) LocalAlloc(LMEM_FIXED, cbBitsInfo + cbBits + 2*sizeof(WORD)); if (!lpW) { ERROR_ASSERT(FALSE, "RecordObject: out of memory\n"); break; } *lpW++ = (WORD) lbStyle; // BS_PATTERN or BS_DIBPATTERN *lpW++ = (WORD) iUsage; // usage word // Save the start of the bitmap info header field. lpDIBInfoHeader = (LPBITMAPINFOHEADER) lpW; // Copy the bitmap info header. *lpDIBInfoHeader = bmih; // Get bitmap info and bits. if (GetBrushBits(hdcScreen, hbmRemote, (UINT) iUsage, cbBitsInfo, (LPVOID) ((PBYTE) lpW + cbBitsInfo), (LPBITMAPINFO) lpDIBInfoHeader)) { // Finally record the parameters into the file. status = (UINT) RecordParms(hdc, META_DIBCREATEPATTERNBRUSH, 2 + (cbBitsInfo + cbBits) / sizeof(WORD), (LPWORD) lpWStart); } if (LocalFree((HANDLE) lpWStart)) ASSERTGDI(FALSE, "RecordObject: LocalFree Failed\n"); break; } default: { ASSERTGDI(FALSE, "RecordObject: Bad brush style"); break; } } // switch(lb.lbStyle) break; } // case LO_BRUSH case LO_PALETTE_TYPE: status = (UINT) MakeLogPalette(hdc, hObject, META_CREATEPALETTE); break; default: ERROR_ASSERT(FALSE, "unknown case RecordObject"); break; } // Free the DC created in the brush case. if (hdcScreen) if (!DeleteDC(hdcScreen)) ASSERTGDI(FALSE, "RecordObject: DeleteDC Failed\n"); ASSERTGDI(status == TRUE, "RecordObject: Failing\n"); return ((WORD) (status == TRUE ? iPosition : -1)); } /* RecordObject */ BOOL AddDCToObjectMetaList16(HDC hMetaDC16, HANDLE hObject) { ULONG cMetaDC16New; PMETALINK16 pml16; ASSERTGDI(LO_TYPE(hObject) != LO_REGION_TYPE, "AddDCToObjectMetaList16: unexpected region object"); // If the object is a stock object there is no work to do if (IS_STOCKOBJ(hObject)) return(TRUE); // If the Object's MetaList16 is NULL create allocate one pml16 = pmetalink16Get(hObject); if (!pml16) { ENTERCRITICALSECTION(&semLocal); pml16 = pmetalink16Create(hObject); LEAVECRITICALSECTION(&semLocal); if (pml16) { pml16->metalink = 0; pml16->cMetaDC16 = 1; pml16->ahMetaDC16[0] = hMetaDC16; } else { ASSERTGDI(FALSE, "AddDCToObjectMetaList16: Out of Memory 1"); return(FALSE); } } else { int cj; cMetaDC16New = pml16->cMetaDC16 + 1; if (pml16 = pmetalink16Resize(hObject,cMetaDC16New)) { pml16->ahMetaDC16[pml16->cMetaDC16++] = hMetaDC16; } else { ASSERTGDI(FALSE, "AddDCToObjectMetaList16: Out of Memory 2"); return(FALSE); } } return(TRUE); } /***************************** Internal Function ***************************\ * AddObjectToDCTable * * Add an object (brush, pen...) to a list of objects associated with the * metafile. * * * * Returns: 1 if object is already in table * 0 if object was just added to table * -1 if failure * * Remarks * bAdd is TRUE iff the object is being added otherwise it is being deleted * \***************************************************************************/ UINT AddObjectToDCTable(HDC hdc, HANDLE hObject, PUINT pPosition, BOOL bAdd) { UINT iEmptySpace = (UINT) -1; UINT i; UINT status = (UINT) -1; POBJECTTABLE pHandleTable; PMFRECORDER16 pMFRec; PUTS("AddObjectToDCTable\n"); GET_PMFRECORDER16(pMFRec,hdc); if (!IS_METADC16_TYPE(hdc) || !pMFRec) { GdiSetLastError(ERROR_INVALID_HANDLE); return((UINT)-1); } // if the Object table already exists search it for the object if (pHandleTable = (POBJECTTABLE)pMFRec->hObjectTable) { for (i=0; i < (UINT) pMFRec->metaHeader.mtNoObjects; ++i) { if (hObject == pHandleTable[i].CurHandle) { *pPosition = i; status = 1; // the object exists in the table // if we are doing a METADELETEOBJECT. // delete object from table if (!bAdd) { pHandleTable[i].fPreDeleted = FALSE; pHandleTable[i].CurHandle = (HANDLE)NULL; } goto AddObjectToTable10; } else if ((pHandleTable[i].CurHandle == 0) && (iEmptySpace == (UINT) -1)) { // if the entry has been deleted, we want to add a new object // in its place. iEmptySpace will tell us where that place is. iEmptySpace = i; } } // for } if (bAdd) { // If there is no object table for this MetaFile then Allocate one. if (pHandleTable == (POBJECTTABLE)NULL) { pHandleTable = (POBJECTTABLE) LocalAlloc(LMEM_FIXED, sizeof(OBJECTTABLE)); pMFRec->hObjectTable = (HANDLE) pHandleTable; } else if (iEmptySpace == (UINT) -1) { pHandleTable = (POBJECTTABLE) LocalReAlloc(pMFRec->hObjectTable, (pMFRec->metaHeader.mtNoObjects + 1) * sizeof(OBJECTTABLE), LMEM_MOVEABLE); if (pHandleTable) pMFRec->hObjectTable = (HANDLE) pHandleTable; } if (pHandleTable) { if (iEmptySpace == (UINT) -1) *pPosition = pMFRec->metaHeader.mtNoObjects++; else *pPosition = iEmptySpace; pHandleTable[*pPosition].fPreDeleted = FALSE; pHandleTable[*pPosition].CurHandle = hObject; status = 0; // the object is added to the table } } AddObjectToTable10: ERROR_ASSERT(status != (UINT) -1, "AddObjectToTable: Failing\n"); return(status); } /***************************** Internal Function **************************\ * HDC WINAPI CreateMetaFileW * * Creates a MetaFile DC * * The internal format for a MetaFileRecorder has two formats one * for a memory MetaFile and one for a disk based MetaFile * \***************************************************************************/ HDC WINAPI CreateMetaFileA(LPCSTR pszFileName) { UINT cch; WCHAR awch[MAX_PATH]; if (pszFileName) { cch = strlen(pszFileName)+1; if (cch > MAX_PATH) { ERROR_ASSERT(FALSE, "CreateMetaFileA filename too long"); GdiSetLastError(ERROR_FILENAME_EXCED_RANGE); return((HDC) 0); } vToUnicodeN(awch, MAX_PATH, pszFileName, cch); return (CreateMetaFileW(awch)); } else return (CreateMetaFileW((LPWSTR)NULL)); } HDC WINAPI CreateMetaFileW(LPCWSTR pwszFileName) { PMFRECORDER16 pMFRec; HDC hdc; PUTS("CreateMetaFileW\n"); if (!(pMFRec = (PMFRECORDER16) LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, sizeof(MFRECORDER16)))) goto CreateMetaFileW_error; // pMFRec->ident = ID_METADC16; // pMFRec->hMem = 0; pMFRec->hFile = INVALID_HANDLE_VALUE; pMFRec->cbBuffer = MF16_BUFSIZE_INIT; // pMFRec->ibBuffer = 0; pMFRec->metaHeader.mtHeaderSize = sizeof(METAHEADER)/sizeof(WORD); pMFRec->metaHeader.mtVersion = METAVERSION300; // pMFRec->metaHeader.mtSize = 0; // pMFRec->metaHeader.mtNoObjects = 0; // pMFRec->metaHeader.mtMaxRecord = 0; // pMFRec->metaHeader.mtNoParameters = 0; // pMFRec->recFlags = 0; // pMFRec->recCurObjects[] = 0; pMFRec->recCurObjects[OBJ_PEN - MIN_OBJ_TYPE] = GetStockObject(BLACK_PEN); pMFRec->recCurObjects[OBJ_BRUSH - MIN_OBJ_TYPE] = GetStockObject(WHITE_BRUSH); pMFRec->recCurObjects[OBJ_FONT - MIN_OBJ_TYPE] = GetStockObject(DEVICE_DEFAULT_FONT); pMFRec->recCurObjects[OBJ_BITMAP - MIN_OBJ_TYPE] = GetStockObject(PRIV_STOCK_BITMAP); pMFRec->recCurObjects[OBJ_REGION - MIN_OBJ_TYPE] = (HANDLE) NULL; pMFRec->recCurObjects[OBJ_PAL - MIN_OBJ_TYPE] = GetStockObject(DEFAULT_PALETTE); // pMFRec->iPalVer = 0; // Create a disk file if given. The filename is given in unicode. if (pwszFileName) { LPWSTR pwszFilePart; // not used DWORD cPathname; // Convert the filename to a fully qualified pathname. cPathname = GetFullPathNameW(pwszFileName, MAX_PATH, pMFRec->wszFullPathName, &pwszFilePart); if (!cPathname || cPathname > MAX_PATH) { ERROR_ASSERT(FALSE, "GetFullPathName failed"); if (cPathname > MAX_PATH) GdiSetLastError(ERROR_FILENAME_EXCED_RANGE); goto CreateMetaFileW_error; } pMFRec->wszFullPathName[cPathname] = 0; // Create the file. if ((pMFRec->hFile = CreateFileW(pMFRec->wszFullPathName,// Filename GENERIC_WRITE, // Write access 0L, // Non-shared (LPSECURITY_ATTRIBUTES) NULL, // No security CREATE_ALWAYS, // Always create FILE_ATTRIBUTE_NORMAL, // normal attributes (HANDLE) 0)) // no template file == INVALID_HANDLE_VALUE) { // Milestones, Etc. 3.1 creates the file for read/write access when // it calls CreateMetaFile. This causes the above CreateFile to // fail. However, we do not want to modify the above call since // it provides serialization and access to the metafile. Instead, // we add in this hack for Milestones. The only difference is // that the metafile is shared for read/write access. if ((pMFRec->hFile = CreateFileW(pMFRec->wszFullPathName, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, (LPSECURITY_ATTRIBUTES) NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE) 0)) == INVALID_HANDLE_VALUE) { ERROR_ASSERT(FALSE, "CreateFile failed"); goto CreateMetaFileW_error; } WARNING("CreateMetaFileW: Creating metafile with read/write share\n"); } pMFRec->metaHeader.mtType = DISKMETAFILE; } else { pMFRec->metaHeader.mtType = MEMORYMETAFILE; } // Allocate memory for metafile. // For disk metafile, it is used as a buffer. // For memory metafile, it is the storage for the metafile. if (!(pMFRec->hMem = LocalAlloc(LMEM_FIXED, MF16_BUFSIZE_INIT))) goto CreateMetaFileW_error; // Write the header. if (!AttemptWrite(pMFRec, sizeof(METAHEADER), (LPBYTE)&pMFRec->metaHeader)) goto CreateMetaFileW_error; // Finally, allocate a local handle for the metafile DC. It references // the metafile recorder info. hdc = hCreateClientObjLink(pMFRec,LO_METADC16_TYPE); if (!hdc) { ERROR_ASSERT(FALSE, "CreateMetaFileW: iAllocHandle failed\n"); goto CreateMetaFileW_error; } return(hdc); CreateMetaFileW_error: if (pMFRec) { if (pMFRec->hFile != INVALID_HANDLE_VALUE) { if (!CloseHandle(pMFRec->hFile)) ASSERTGDI(FALSE, "CloseHandle failed\n"); if (!DeleteFileW(pMFRec->wszFullPathName)) WARNING("CreateMetaFileW: DeleteFile failed\n"); } if (pMFRec->hMem) if (LocalFree(pMFRec->hMem)) ASSERTGDI(FALSE, "LocalFree failed"); if (LocalFree((HANDLE) pMFRec)) ASSERTGDI(FALSE, "CreateMetaFileW: LocalFree failed\n"); } ERROR_ASSERT(FALSE, "CreateMetaFileW failed\n"); return((HDC) 0); } /***************************** Internal Function **************************\ * HMETAFILE WINAPI CloseMetaFile * * The CloseMetaFile function closes the metafile device context and creates a * metafile handle that can be used to play the metafile by using the * PlayMetaFile function. * * The internal format for a MetaFile has two formats one * for a memory MetaFile and one for a disk based MetaFile * * Effects: * \***************************************************************************/ HMETAFILE WINAPI CloseMetaFile(HDC hdc) { PMFRECORDER16 pmfRec; HMETAFILE hmf = (HMETAFILE) 0; PUTS("CloseMetaFile\n"); GET_PMFRECORDER16(pmfRec,hdc); ASSERTGDI(pmfRec, "CloseMetaFile: pmfRec is NULL!"); if (!IS_METADC16_TYPE(hdc) || !pmfRec) { GdiSetLastError(ERROR_INVALID_HANDLE); return(hmf); } // If the metafile was aborted then free the MetaDC handle memory and go home. if (pmfRec->recFlags & METAFILEFAILURE) goto CLM_Cleanup; // Write the terminate Record if (!RecordParms(hdc, 0, 0, (LPWORD)NULL)) goto CLM_Cleanup; // Flush the buffer and update the header record. if (pmfRec->metaHeader.mtType == DISKMETAFILE) { BOOL fRet; DWORD dwT; ASSERTGDI(pmfRec->metaHeader.mtType == DISKMETAFILE, "CloseMetaFile: unknown metafile type"); // Flush the memory buffer fRet = WriteFile(pmfRec->hFile, (LPBYTE)pmfRec->hMem, pmfRec->ibBuffer, &dwT, (LPOVERLAPPED)NULL); if (!fRet || (dwT != pmfRec->ibBuffer)) { ERROR_ASSERT(FALSE, "CloseMetaFile: Write1 failed\n"); goto CLM_Cleanup; } // Rewind the file and write the header out if (SetFilePointer(pmfRec->hFile, 0, (LPLONG)NULL, FILE_BEGIN) != 0) { ERROR_ASSERT(FALSE, "CloseMetaFile: SetFilePointer failed\n"); goto CLM_Cleanup; } // Fix up data written to disk as a memory metafile pmfRec->metaHeader.mtType = MEMORYMETAFILE; fRet = WriteFile(pmfRec->hFile, (LPBYTE)& pmfRec->metaHeader, sizeof(METAHEADER), &dwT, (LPOVERLAPPED)NULL); pmfRec->metaHeader.mtType = DISKMETAFILE; // restore it if (!fRet || (dwT != sizeof(METAHEADER))) { ERROR_ASSERT(FALSE, "CloseMetaFile: Write2 failed\n"); goto CLM_Cleanup; } // Close the file. if (!CloseHandle(pmfRec->hFile)) ASSERTGDI(FALSE, "CloseMetaFile: FileError\n"); pmfRec->hFile = INVALID_HANDLE_VALUE; // don't close it again below } else { HANDLE hMemNew; // Flush the header record. *(PMETAHEADER) pmfRec->hMem = pmfRec->metaHeader; // Realloc memory metafile to exact size hMemNew = LocalReAlloc(pmfRec->hMem, pmfRec->metaHeader.mtSize * sizeof(WORD), LMEM_MOVEABLE); if (!hMemNew) { ASSERTGDI(FALSE, "LocalReAlloc failed"); goto CLM_Cleanup; } pmfRec->hMem = hMemNew; } // Allocate and initialize a metafile. if (pmfRec->metaHeader.mtType == DISKMETAFILE) { hmf = GetMetaFileW(pmfRec->wszFullPathName); } else { hmf = SetMetaFileBitsAlt((HLOCAL) pmfRec->hMem); if (hmf) pmfRec->hMem = 0; // don't free it below because it has been transfered } CLM_Cleanup: // Remove the MetaFile from the list of active metafiles if (pmfRec->hObjectTable) { UnlistObjects(hdc); if (LocalFree((HANDLE) pmfRec->hObjectTable)) ASSERTGDI( FALSE, "CloseMetaFile: LocalFree object table failed\n"); } // If the file handle exists at this time, we have an error. if (pmfRec->hFile != INVALID_HANDLE_VALUE) { if (!CloseHandle(pmfRec->hFile)) ASSERTGDI(FALSE, "CloseHandle failed\n"); if (!DeleteFileW(pmfRec->wszFullPathName)) WARNING("CloseMetaFile: DeleteFile failed\n"); } // Free the cache buffer. if (pmfRec->hMem) if (LocalFree(pmfRec->hMem)) ASSERTGDI(FALSE, "LocalFree failed"); // Free the memory for the metafile DC. if (LocalFree((HANDLE) pmfRec)) ASSERTGDI(FALSE, "CloseMetaFile: LocalFree failed\n"); // Free the handle for the metafile DC. if (!bDeleteClientObjLink(hdc)) RIP("CloseMetaFile - failed bDeleteClientObjLink\n"); ERROR_ASSERT(hmf != (HMETAFILE) 0, "CloseMetaFile failed\n"); return(hmf); } /***************************** Internal Function **************************\ * CopyMetaFile(hSrcMF, lpFileName) * * Copies the metafile (hSrcMF) to a new metafile with name lpFileName. The * function then returns a handle to this new metafile if the function was * successful. * * Retuns a handle to a new metafile, 0 iff failure * * IMPLEMENTATION: * The source and target metafiles are checked to see if they are both memory * metafile and if so a piece of Local memory is allocated and the metafile * is simply copied. * If this is not the case CreateMetaFile is called with lpFileName and then * records are pulled out of the source metafile (using GetEvent) and written * into the destination metafile one at a time (using RecordParms). * * Lock the source * if source is a memory metafile and the destination is a memory metafile * alloc the same size Local memory as the source * copy the bits directly * else * get a metafile handle by calling CreateMetaFile * while GetEvent returns records form the source * record the record in the new metafile * * close the metafile * * return the new metafile handle * \***************************************************************************/ HMETAFILE WINAPI CopyMetaFileA(HMETAFILE hmf, LPCSTR psz) { UINT cch; WCHAR awch[MAX_PATH]; if (psz != (LPSTR)NULL) { cch = strlen(psz)+1; if (cch > MAX_PATH) { ERROR_ASSERT(FALSE, "CopyMetaFileA filename too long"); GdiSetLastError(ERROR_FILENAME_EXCED_RANGE); return((HMETAFILE)0); } vToUnicodeN(awch, MAX_PATH, psz, cch); return (CopyMetaFileW(hmf, awch)); } else return (CopyMetaFileW(hmf, (LPWSTR)NULL)); } HMETAFILE WINAPI CopyMetaFileW(HMETAFILE hSrcMF, LPCWSTR pwszFileName) { PMETAFILE16 pMFSource; HMETAFILE hMFDest = (HMETAFILE) 0; HDC hMDCDest; UINT state; PUTS("CopyMetaFile\n"); pMFSource = GET_PMF16(hSrcMF); if (pMFSource == NULL) { GdiSetLastError(ERROR_INVALID_HANDLE); return(hMFDest); } state = (pMFSource->fl & MF16_DISKFILE) ? 2 : 0; state |= (pwszFileName) ? 1 : 0; switch (state) { case 0: /* memory -> memory */ hMFDest = SetMetaFileBitsEx ( pMFSource->metaHeader.mtSize * sizeof(WORD), (LPBYTE) pMFSource->hMem ); break; case 3: /* disk -> disk */ hMFDest = CopyFileW(pMFSource->wszFullPathName, pwszFileName, FALSE) ? GetMetaFileW(pwszFileName) : 0; ERROR_ASSERT(hMFDest, "CopyMetaFileW: GetMetaFile Failed\n"); break; case 1: case 2: if (hMDCDest = CreateMetaFileW(pwszFileName)) { PMFRECORDER16 pMFRecDest; PMETARECORD lpMR = NULL; GET_PMFRECORDER16(pMFRecDest,hMDCDest); while (lpMR = GetEvent(pMFSource, lpMR)) if ((lpMR == (PMETARECORD) -1) || !RecordParms(hMDCDest, (DWORD)lpMR->rdFunction, (DWORD)lpMR->rdSize - 3, (LPWORD) lpMR->rdParm)) { HMETAFILE hmfT; MarkMetaFile(pMFRecDest); if (hmfT = CloseMetaFile(hMDCDest)) DeleteMetaFile(hmfT); return((HMETAFILE) 0); } // touch up the destination metafile header before we close // the metafile! pMFRecDest->metaHeader.mtNoObjects = pMFSource->metaHeader.mtNoObjects; ASSERTGDI(sizeof(METAHEADER) == 18, "CopyMetaFile: METAHEADER has changed!"); hMFDest = CloseMetaFile(hMDCDest); } break; } ERROR_ASSERT(hMFDest, "CopyMetaFileW Failing\n"); return(hMFDest); } /***************************** Internal Function ***************************\ * HANDLE WINAPI GetMetaFileBitsEx * * The GetMetaFileBits function returns a handle to a Windows metafile that * contains the specified data describing the metafile. * * It does not invalidate the metafile handle like Windows! * * Effects: * \***************************************************************************/ UINT WINAPI GetMetaFileBitsEx(HMETAFILE hMF, UINT cbBuffer, LPVOID lpData) { DWORD cbHave; PMETAFILE16 pmf16; PUTS("GetMetaFileBitsEx\n"); pmf16 = GET_PMF16(hMF); if (pmf16 == NULL) { GdiSetLastError(ERROR_INVALID_HANDLE); return(0); } cbHave = pmf16->metaHeader.mtSize * sizeof(WORD); // If lpData is NULL, return the size necessary to hold the data. if (!lpData) return(cbHave); // Make sure the input buffer is large enough. if (cbBuffer < cbHave) return(0); // Copy the bits. Win3.1 returns the bits for memory metafile only! // We will do the right thing here. RtlCopyMemory(lpData, (PBYTE) pmf16->hMem, cbHave); // Return the number of bytes copied. return(cbHave); } /***************************** Internal Function **************************\ * HMETAFILE WINAPI SetMetaFileBitsEx * * Creates a memory based Windows 3.X metafile from the data provided * * Returns: HMETAFILE iff succesful. * * Effects: * \***************************************************************************/ HMETAFILE WINAPI SetMetaFileBitsEx(UINT cbBuffer, CONST BYTE *lpData) { PMETAFILE16 pmf16; HMETAFILE hmf; PUTS("SetMetaFileBitsEx\n"); // Verify the input data. if (cbBuffer < sizeof(METAHEADER) || !IsValidMetaHeader16((PMETAHEADER) lpData)) { ERROR_ASSERT(FALSE, "SetMetaFileBitsEx: Bad input data\n"); GdiSetLastError(ERROR_INVALID_DATA); return((HMETAFILE) 0); } ERROR_ASSERT(((PMETAHEADER) lpData)->mtType == MEMORYMETAFILE, "SetMetaFileBitsEx: Bad mtType\n"); // Allocate and initialize a metafile. // Some Windows metafiles contain bad values in mtSize. As a result, // we have to verify and fix the mtSize if neccessary. This is fixed // in the following call. if (!(pmf16 = pmf16AllocMF16(0, (DWORD) cbBuffer, (PDWORD)lpData, (LPWSTR) NULL))) return((HMETAFILE) 0); ASSERTGDI(pmf16->metaHeader.mtType == MEMORYMETAFILE, "SetMetaFileBitsEx: Bad mtType\n"); ((PMETAHEADER) pmf16->hMem)->mtType = MEMORYMETAFILE; // just in case // Allocate a local handle. hmf = hmf16Create(pmf16); if (hmf == NULL) { vFreeMF16(pmf16); } // Return the metafile handle. return(hmf); } // Similar to Win3.x SetMetaFileBits. // It is assumed that hMem is allocated with the LMEM_FIXED option. // For internal use only. HMETAFILE WINAPI SetMetaFileBitsAlt(HLOCAL hMem) { PMETAFILE16 pmf16; HMETAFILE hmf; PUTS("SetMetaFileBitsAlt\n"); // Allocate and initialize a metafile. if (!(pmf16 = pmf16AllocMF16(ALLOCMF16_TRANSFER_BUFFER, 0, (PDWORD) hMem, (LPWSTR) NULL))) return((HMETAFILE) 0); ASSERTGDI(pmf16->metaHeader.mtType == MEMORYMETAFILE, "SetMetaFileBitsAlt: Bad mtType\n"); ((PMETAHEADER) pmf16->hMem)->mtType = MEMORYMETAFILE; // just in case // Allocate a local handle. hmf = hmf16Create(pmf16); if (hmf == NULL) { pmf16->hMem = NULL; // let caller free the buffer! vFreeMF16(pmf16); } // Return the metafile handle. return(hmf); } /***************************** Internal Function **************************\ * GetObject16AndType * * Returns the object type, eg OBJ_FONT, as well as a the LogObject * in Windows 3.x Format * \***************************************************************************/ #define MAXOBJECTSIZE sizeof(LOGFONT) DWORD GetObject16AndType(HANDLE hObj, LPVOID lpObjBuf16) { BYTE objBuf32[MAXOBJECTSIZE]; int iObj; PUTS("GetObjectAndType\n"); ASSERTGDI(MAXOBJECTSIZE >= sizeof(LOGPEN), "GetObject16AndType MAXOBJECTSIZE wrong\n"); if (!GetObject(hObj, MAXOBJECTSIZE, &objBuf32)) { ERROR_ASSERT(FALSE, "GetObject16AndType GetObject Failed\n"); return(0); } switch(iObj = GetObjectType(hObj)) { case OBJ_PEN: LOGPEN16FROMLOGPEN32((PLOGPEN16)lpObjBuf16,(LPLOGPEN)objBuf32); break; case OBJ_FONT: LOGFONT16FROMLOGFONT32((PLOGFONT16)lpObjBuf16,(LPLOGFONT)objBuf32); break; default: ASSERTGDI(FALSE, "GetObject16AndType unknown object type\n"); return(0); break; } return((DWORD) iObj); } /***************************** Internal Function **************************\ * BOOL UnlistObjects(hMetaDC) * * Each object has a list of metafiles the object is associated with. * UnlistObjects removes hMetaDC from all of its object's metafile lists * * Effects: * \***************************************************************************/ BOOL UnlistObjects(HDC hMetaDC) { PMETALINK16 pml16; UINT iCurObject; UINT iCurMetaListEntry; POBJECTTABLE pht; PMFRECORDER16 pMFRec; PUTS("UnlistObjects\n"); GET_PMFRECORDER16(pMFRec,hMetaDC); if (!IS_METADC16_TYPE(hMetaDC) || !pMFRec) { GdiSetLastError(ERROR_INVALID_HANDLE); return((UINT)-1); } if (pMFRec->metaHeader.mtNoObjects) { pht = (POBJECTTABLE) pMFRec->hObjectTable; ASSERTGDI(pht, "UnlistObject: called even with no handle table"); // Loop through the objects and unlink this metafile for (iCurObject=0; iCurObject < (UINT) pMFRec->metaHeader.mtNoObjects; iCurObject++) { HANDLE hObj; if( (hObj = pht[iCurObject].CurHandle) && (!pht[iCurObject].fPreDeleted)) { if (IS_STOCKOBJ(hObj)) continue; ASSERTGDI(LO_TYPE(hObj) != LO_REGION_TYPE, "UnlistObjects: unexpected region object"); pml16 = pmetalink16Get(hObj); // It cannot be a empty list. ASSERTGDI(pml16, "UnlistObject: pml16 is NULL"); if (!pml16 || pml16->cMetaDC16 == 0) continue; // Find the metafile in the objects MetaLink16 list for (iCurMetaListEntry=0; iCurMetaListEntrycMetaDC16; iCurMetaListEntry++) { if(pml16->ahMetaDC16[iCurMetaListEntry] == hMetaDC) break; } ASSERTGDI(iCurMetaListEntry < pml16->cMetaDC16, "UnlistObject: Metafile not found"); // Slide the rest of the metafiles in the list "up" for(; iCurMetaListEntry < (pml16->cMetaDC16-1); iCurMetaListEntry++) pml16->ahMetaDC16[iCurMetaListEntry] = pml16->ahMetaDC16[iCurMetaListEntry+1]; pml16->cMetaDC16--; // just got rid of one if (pml16->cMetaDC16 == 0) { // We can only free the METALINK16 if the metalink field is not being used if (pml16->metalink) { pml16->cMetaDC16 = 0; pml16->ahMetaDC16[0] = (HDC)NULL; } else { if (!bDeleteMetalink16(hObj)) ASSERTGDI(FALSE,"UnlistObjects: LocalFree Failed\n"); } } else { pml16 = pmetalink16Resize(hObj,pml16->cMetaDC16); if (pml16 == NULL) { ASSERTGDI(FALSE,"UnlistObjects: LocalReAlloc Failed\n"); return (FALSE); } } } } // for } return(TRUE); } /******************************Public*Routine******************************\ * pmf16AllocMF16(fl, cb, pb, pwszFilename) * * This routine allocates memory for an METAFILE16 and initializes it. * Returns a pointer to the new METAFILE16. On error returns NULL. * It accepts only windows metafiles. * * This routine is called by API level METAFILE16 allocation routines * CloseMetaFile, GetMetaFile, SetMetaFileBitsEx. * * Arguments: * fl - ALLOCMF16_TRANSFER_BUFFER is set if storage for memory * metafile is to be set directly into METAFILE16. Otherwise, * a copy of the memory metafile is duplicated. * cb - Size of pb in bytes if given. This parameter is given * by SetMetaFileBitsEx only. It is used to compare and * fixup bad mtSize if necessary. * pb - Pointer to a memory metafile if non-null. * pwszFilename - Filename of a disk metafile if non-null. * * History: * Fri May 15 14:11:22 1992 -by- Hock San Lee [hockl] * Wrote it. \**************************************************************************/ PMETAFILE16 pmf16AllocMF16(DWORD fl, DWORD cb, CONST UNALIGNED DWORD *pb, LPCWSTR pwszFilename) { PMETAFILE16 pmf16, pmf16Rc = (PMETAFILE16) NULL; PUTS("pmf16AllocMF16\n"); ASSERTGDI(!(fl & ~ALLOCMF16_TRANSFER_BUFFER), "pmf16AllocMF16: Invalid fl"); // Allocate a new METAFILE16. if (!(pmf16 = (PMETAFILE16) LocalAlloc(LMEM_FIXED, sizeof(METAFILE16)))) goto pmf16AllocMF16_cleanup; // Initialize it. pmf16->ident = MF16_IDENTIFIER; pmf16->hFile = INVALID_HANDLE_VALUE; pmf16->hFileMap = (HANDLE) 0; pmf16->hMem = (HANDLE) 0; pmf16->iMem = 0; pmf16->hMetaFileRecord = (HANDLE) 0; pmf16->fl = 0L; // Memory mapped the disk file if given. if (pwszFilename) { LPWSTR pwszFilePart; // not used DWORD cPathname; pmf16->fl |= MF16_DISKFILE; // this must be first! See vFreeMF16. // Convert the filename to a fully qualified pathname. cPathname = GetFullPathNameW(pwszFilename, MAX_PATH, pmf16->wszFullPathName, &pwszFilePart); if (!cPathname || cPathname > MAX_PATH) { ERROR_ASSERT(FALSE, "GetFullPathName failed"); if (cPathname > MAX_PATH) GdiSetLastError(ERROR_FILENAME_EXCED_RANGE); goto pmf16AllocMF16_cleanup; } pmf16->wszFullPathName[cPathname] = 0; if ((pmf16->hFile = CreateFileW(pmf16->wszFullPathName, // Filename GENERIC_READ, // Read access FILE_SHARE_READ, // Share read (LPSECURITY_ATTRIBUTES) 0L,// No security OPEN_EXISTING, // Re-open 0, // file attributes ignored (HANDLE) 0)) // no template file == INVALID_HANDLE_VALUE) { // See the comment for Milestones in CreateMetaFileW. if ((pmf16->hFile = CreateFileW(pmf16->wszFullPathName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, (LPSECURITY_ATTRIBUTES) 0L, OPEN_EXISTING, 0, (HANDLE) 0)) == INVALID_HANDLE_VALUE) { ERROR_ASSERT(FALSE, "CreateFile failed"); goto pmf16AllocMF16_cleanup; } WARNING("pmf16AllocMF16: Opening metafile with read/write share\n"); } if (!(pmf16->hFileMap = CreateFileMappingW(pmf16->hFile, (LPSECURITY_ATTRIBUTES) 0L, PAGE_READONLY, 0L, 0L, (LPWSTR) NULL))) { ERROR_ASSERT(FALSE, "CreateFileMapping failed"); goto pmf16AllocMF16_cleanup; } if (!(pmf16->hMem = MapViewOfFile(pmf16->hFileMap, FILE_MAP_READ, 0, 0, 0))) { ERROR_ASSERT(FALSE, "MapViewOfFile failed"); goto pmf16AllocMF16_cleanup; } } else if (fl & ALLOCMF16_TRANSFER_BUFFER) { // If this is our memory metafile from MDC, transfer it to METAFILE16. pmf16->hMem = (BYTE *) pb; } else { // Otherwise, make a copy of memory metafile. // We get here only if the caller is SetMetaFileBitsEx. Since some metafiles // may contain a bad mtSize that is different than the actual file size, we // need to fix it up if necessary! DWORD mtSize = ((PMETAHEADER) pb)->mtSize; if ((mtSize * 2 > cb) // mtSize greater than filesize || (((PWORD) pb)[mtSize - 3] != 3) // EOF record should be 3,0,0 || (((PWORD) pb)[mtSize - 2] != 0) || (((PWORD) pb)[mtSize - 1] != 0)) { // Compute the correct mtSize! PMETARECORD pMR; DWORD mtSizeMax; PUTS("pmf16AllocMF16: verifying mtSize\n"); mtSize = ((PMETAHEADER) pb)->mtHeaderSize; pMR = (PMETARECORD) ((PWORD) pb + ((PMETAHEADER) pb)->mtHeaderSize); mtSizeMax = cb / 2 - 3; // max mtSize not counting EOF record while (mtSize <= mtSizeMax && pMR->rdFunction != 0) { mtSize += pMR->rdSize; pMR = (PMETARECORD) ((PWORD) pMR + pMR->rdSize); } if (mtSize > mtSizeMax) { ERROR_ASSERT(FALSE, "pmf16AllocMF16: incomplete metafile\n"); goto pmf16AllocMF16_cleanup; } // Powerpnt uses 0,0,0 for the EOF record! We will change it to 3,0,0 below. mtSize += 3; // include EOF record (pMR->rdSize may not be valid) if (((PMETAHEADER) pb)->mtSize != mtSize) { ERROR_ASSERT(FALSE, "pmf16AllocMF16: fixing up bad mtSize\n"); } } if (!(pmf16->hMem = LocalAlloc(LMEM_FIXED, mtSize * sizeof(WORD)))) goto pmf16AllocMF16_cleanup; RtlCopyMemory((PBYTE) pmf16->hMem, (PBYTE)pb, mtSize * sizeof(WORD)); ((PMETAHEADER) pmf16->hMem)->mtSize = mtSize; // update mtSize VERIFYGDI(((PWORD) pmf16->hMem)[mtSize - 3] == 3 && ((PWORD) pmf16->hMem)[mtSize - 2] == 0 && ((PWORD) pmf16->hMem)[mtSize - 1] == 0, "pmf16AllocMF16: fixing up bad EOF metafile record\n"); ((PWORD) pmf16->hMem)[mtSize - 3] = 3; // update EOF record ((PWORD) pmf16->hMem)[mtSize - 2] = 0; ((PWORD) pmf16->hMem)[mtSize - 1] = 0; } // Make a copy of the metafile header. pmf16->metaHeader = *(PMETAHEADER) pmf16->hMem; pmf16->metaHeader.mtType = (pmf16->fl & MF16_DISKFILE) ? DISKMETAFILE : MEMORYMETAFILE; // Verify metafile header if (!IsValidMetaHeader16(&pmf16->metaHeader)) { ERROR_ASSERT(FALSE, "pmf16AllocMF16: Metafile has an invalid header; Failing\n"); goto pmf16AllocMF16_cleanup; } // Everything is golden. pmf16Rc = pmf16; // Cleanup and go home. pmf16AllocMF16_cleanup: if (!pmf16Rc) if (pmf16) { if (fl & ALLOCMF16_TRANSFER_BUFFER) pmf16->hMem = NULL; // let caller free the buffer! vFreeMF16(pmf16); } ERROR_ASSERT(pmf16Rc, "pmf16AllocMF16 failed"); return(pmf16Rc); } /******************************Public*Routine******************************\ * vFreeMF16 (pmf16) * * This is a low level routine which frees the resouces in the METAFILE16. * * This function is intended to be called from the routines CloseMetaFile * and DeleteMetaFile. * * Arguments: * pmf16 - The METAFILE16 to be freed. * * History: * Fri May 15 14:11:22 1992 -by- Hock San Lee [hockl] * Wrote it. \**************************************************************************/ VOID vFreeMF16(PMETAFILE16 pmf16) { PUTS("vFreeMF16\n"); ASSERTGDI(pmf16->ident == MF16_IDENTIFIER, "Bad METAFILE16"); // Free the resources. if (!(pmf16->fl & MF16_DISKFILE)) { // Free memory metafile. if (pmf16->hMem) if (LocalFree(pmf16->hMem)) ASSERTGDI(FALSE, "LocalFree failed"); } else { // Unmap disk file. if (pmf16->hMem) if (!UnmapViewOfFile((LPVOID) pmf16->hMem)) ASSERTGDI(FALSE, "UmmapViewOfFile failed"); if (pmf16->hFileMap) if (!CloseHandle(pmf16->hFileMap)) ASSERTGDI(FALSE, "CloseHandle failed"); if (pmf16->hFile != INVALID_HANDLE_VALUE) if (!CloseHandle(pmf16->hFile)) ASSERTGDI(FALSE, "CloseHandle failed"); } // Smash the identifier. pmf16->ident = 0; // Free the memory. if (LocalFree(pmf16)) ASSERTGDI(FALSE, "LocalFree failed"); }