/****************************** Module Header ******************************\
* Module Name:EMF.C (Extensible Compound Documents - EnhancedMetafile)
*
* PURPOSE:Handles all API routines for the metafile sub-dll of the ole dll.
*
* Created: 1990
*
* Copyright (c) 1990, 1991  Microsoft Corporation
*
* History:
*   cloned mf.c and banged into form curts March 92
*
* Comments:
*   fun, fun, until hockl takes the enhanced metafile api away
*
\***************************************************************************/

#include <windows.h>
#include "dll.h"
#include "pict.h"

#define RECORD_COUNT    16

OLEOBJECTVTBL    vtblEMF = {

        ErrQueryProtocol,   // check whether the speced protocol is supported

        EmfRelease,         // Release
        ErrShow,            // show
        ErrPlay,            // play
        EmfGetData,         // Get the object data
        ErrSetData,         // Set the object data
        ErrSetTargetDevice, //
        ErrSetBounds,       // set viewport bounds
        EmfEnumFormat,      // enumerate supported formats
        ErrSetColorScheme,  //   
        EmfRelease,         // delete
        ErrSetHostNames,    //
            
        EmfSaveToStream,    // write to file
        EmfClone,           // clone object
        ErrCopyFromLink,    // Create embedded from Lnk

        EmfEqual,           // compares the given objects for data equality

        EmfCopy,            // copy to clip

        EmfDraw,            // draw the object

        ErrActivate,        // open
        ErrExecute,         // excute 
        ErrClose,           // stop 
        ErrUpdate,          // Update
        ErrReconnect,       // Reconnect

        ErrObjectConvert,   // convert object to specified type
            
        ErrGetUpdateOptions, // update options
        ErrSetUpdateOptions, // update options

        ObjRename,          // Change Object name
        ObjQueryName,       // Get current object name
        ObjQueryType,       // Object type
        EmfQueryBounds,     // QueryBounds
        ObjQuerySize,       // Find the size of the object
        ErrQueryOpen,       // Query open
        ErrQueryOutOfDate,  // query whether object is current
            
        ErrQueryRelease,    // release related stuff
        ErrQueryRelease,
        ErrQueryReleaseMethod,

        ErrRequestData,     // requestdata
        ErrObjectLong,      // objectLong
        EmfChangeData       // change data of the existing object
};


OLESTATUS FARINTERNAL  EmfRelease (LPOLEOBJECT lpoleobj)
{
    LPOBJECT_EMF lpobj = (LPOBJECT_EMF)lpoleobj;
    HOBJECT hobj;

    if (lpobj->hemf) {
        DeleteEnhMetaFile ((HENHMETAFILE)lpobj->hemf);
        lpobj->hemf = NULL;
    }
    
    if (lpobj->head.lhclientdoc)
        DocDeleteObject ((LPOLEOBJECT)lpobj);
    
    if (hobj = lpobj->head.hobj) {
        lpobj->head.hobj = NULL;
        GlobalUnlock (hobj);
        GlobalFree (hobj);
    }

    return OLE_OK;
}


OLESTATUS FARINTERNAL  SaveEmfAsMfToStream (
    LPOLEOBJECT lpoleobj,
    LPOLESTREAM lpstream 
){                            
    DWORD             dwFileVer = (DWORD)MAKELONG(wReleaseVer,OS_WIN32);
    LPOBJECT_EMF      lpobj     = (LPOBJECT_EMF)lpoleobj;
    OLESTATUS         retval    = OLE_ERROR_MEMORY;
    HDC               hdc       = NULL ;
    LPBYTE            lpBytes   = NULL ;
    HANDLE            hBytes    = NULL ;
    WIN16METAFILEPICT w16mfp;
    UINT              lSizeBytes;

    w16mfp.mm   = MM_ANISOTROPIC;
    w16mfp.xExt = (short)lpobj->head.cx;

    if ((short)lpobj->head.cy <0 ) {
       w16mfp.yExt = -(short)lpobj->head.cy;
    } else {
       w16mfp.yExt = (short)lpobj->head.cy;
    }

    if (PutBytes (lpstream, (LPSTR) &dwFileVer, sizeof(LONG)))
        return OLE_ERROR_STREAM;

    if (PutBytes (lpstream, (LPSTR) &lpobj->head.ctype, sizeof(LONG)))
        return  OLE_ERROR_STREAM;

    if (PutStrWithLen(lpstream, (LPSTR)"METAFILEPICT"))
        return  OLE_ERROR_STREAM;

    if (PutBytes (lpstream, (LPSTR) &lpobj->head.cx, sizeof(LONG))) 
        return  OLE_ERROR_STREAM;
    
    if (PutBytes (lpstream, (LPSTR) &lpobj->head.cy, sizeof(LONG)))
        return  OLE_ERROR_STREAM;

    hdc = GetDC(NULL);    
    if (!(lSizeBytes = GetWinMetaFileBits((HENHMETAFILE)lpobj->hemf, 0, NULL, MM_ANISOTROPIC, hdc)) ) {
        ReleaseDC(NULL, hdc);
        return OLE_ERROR_METAFILE;
    }

    if (!(hBytes = GlobalAlloc(GHND, lSizeBytes)) )
        goto error; 

    if (!(lpBytes = (LPBYTE)GlobalLock(hBytes)) )
        goto error; 

    if (GetWinMetaFileBits((HENHMETAFILE)lpobj->hemf, lSizeBytes, lpBytes, MM_ANISOTROPIC, hdc) != lSizeBytes) {
        retval = OLE_ERROR_METAFILE;
        goto error; 
    }

    lSizeBytes += sizeof(WIN16METAFILEPICT);

    if (PutBytes (lpstream, (LPSTR) &lSizeBytes, sizeof(UINT)))
        return  OLE_ERROR_STREAM;
    
    if (PutBytes (lpstream, (LPSTR)&w16mfp, sizeof(WIN16METAFILEPICT)))
        goto error;

    if (!PutBytes (lpstream, (LPSTR)lpBytes, lSizeBytes - sizeof(WIN16METAFILEPICT)))
        retval = OLE_OK;

error:
    if (lpBytes)
        GlobalUnlock(hBytes);
    if (hBytes)
        GlobalFree(hBytes);

    if (hdc)
      ReleaseDC(NULL, hdc);

    return retval;

}

OLESTATUS FARINTERNAL  EmfSaveToStream (
    LPOLEOBJECT lpoleobj,
    LPOLESTREAM lpstream 
){
    DWORD        dwFileVer = GetFileVersion(lpoleobj);
    LPOBJECT_EMF lpobj     = (LPOBJECT_EMF)lpoleobj;
    OLESTATUS    retval    = OLE_ERROR_MEMORY;
    LPBYTE       lpBytes   = NULL ;
    HANDLE       hBytes    = NULL ;

    
    if (!lpobj->hemf)
        return OLE_ERROR_BLANK;

    if (HIWORD(dwFileVer) == OS_WIN16) 
      if (!SaveEmfAsMfToStream(lpoleobj,lpstream))
         return OLE_OK;

    if (PutBytes (lpstream, (LPSTR) &dwFileVer, sizeof(LONG)))
        return OLE_ERROR_STREAM;

    if (PutBytes (lpstream, (LPSTR) &lpobj->head.ctype, sizeof(LONG)))
        return  OLE_ERROR_STREAM;

    if (PutStrWithLen(lpstream, (LPSTR)"ENHMETAFILE"))
        return  OLE_ERROR_STREAM;

    if (PutBytes (lpstream, (LPSTR) &lpobj->head.cx, sizeof(LONG))) 
        return  OLE_ERROR_STREAM;
    
    if (PutBytes (lpstream, (LPSTR) &lpobj->head.cy, sizeof(LONG)))
        return  OLE_ERROR_STREAM;
    
    if (PutBytes (lpstream, (LPSTR) &lpobj->sizeBytes, sizeof(LONG)))
        return  OLE_ERROR_STREAM;
    
    if (!(hBytes = GlobalAlloc(GHND, lpobj->sizeBytes)) )
        goto error; 

    if (!(lpBytes = (LPBYTE)GlobalLock(hBytes)) )
        goto error; 

    retval = OLE_ERROR_METAFILE;

    if (GetEnhMetaFileBits((HENHMETAFILE)lpobj->hemf, lpobj->sizeBytes, lpBytes) != lpobj->sizeBytes ) 
        goto error; 

    if (!PutBytes (lpstream, (LPSTR)lpBytes, lpobj->sizeBytes)) 
        retval = OLE_OK;

error:
    if (lpBytes)
        GlobalUnlock(hBytes);
    if (hBytes)
        GlobalFree(hBytes);
   
    return retval;

}

OLESTATUS FARINTERNAL  EmfClone (
    LPOLEOBJECT       lpoleobjsrc,
    LPOLECLIENT       lpclient,
    LHCLIENTDOC       lhclientdoc,
    OLE_LPCSTR        lpobjname,
    LPOLEOBJECT FAR * lplpoleobj
){
    LPOBJECT_EMF lpobjsrc = (LPOBJECT_EMF)lpoleobjsrc;
    LPOBJECT_EMF lpobjEmf;
    HENHMETAFILE hemf;

    *lplpoleobj = (LPOLEOBJECT)NULL;
    
    if (!CheckClientDoc ((LPCLIENTDOC) lhclientdoc))
        return OLE_ERROR_HANDLE;

    
    if (!((HENHMETAFILE)hemf = CopyEnhMetaFile ((HENHMETAFILE)lpobjsrc->hemf, NULL)))
        return OLE_ERROR_MEMORY;

    if (lpobjEmf = EmfCreateBlank (lhclientdoc, (LPSTR)lpobjname, 
                        lpobjsrc->head.ctype)) {
        lpobjEmf->sizeBytes      = lpobjsrc->sizeBytes;
        lpobjEmf->head.lpclient  = lpclient;
		  lpobjEmf->hemf           = hemf;
        EmfSetExtents (lpobjEmf);

        *lplpoleobj = (LPOLEOBJECT)lpobjEmf;
        return OLE_OK;
    }
    
    return OLE_ERROR_MEMORY;
}



OLESTATUS FARINTERNAL  EmfEqual (
    LPOLEOBJECT lpoleobj1,
    LPOLEOBJECT lpoleobj2
){
    LPOBJECT_EMF lpobj1   = (LPOBJECT_EMF)lpoleobj1;
    HANDLE       hBytes1  = NULL;
    LPBYTE       lpBytes1 = NULL;
    LPOBJECT_EMF lpobj2   = (LPOBJECT_EMF)lpoleobj2;
    HANDLE       hBytes2  = NULL;
    LPBYTE       lpBytes2 = NULL;
    OLESTATUS    retval   = OLE_ERROR_MEMORY;


    if (lpobj1->sizeBytes != lpobj2->sizeBytes)
        return OLE_ERROR_NOT_EQUAL;

    if (!(hBytes1 = GlobalAlloc(GHND, lpobj1->sizeBytes)) )
        goto errMemory;

    if (!(lpBytes1 = (LPBYTE)GlobalLock(hBytes1)) )
        goto errMemory;

    if (!(hBytes2 = GlobalAlloc(GHND, lpobj2->sizeBytes)) )
        goto errMemory;

    if (!(lpBytes2 = (LPBYTE)GlobalLock(hBytes2)) )
        goto errMemory;

    if (GetEnhMetaFileBits((HENHMETAFILE)lpobj1->hemf, lpobj1->sizeBytes, lpBytes1) != lpobj1->sizeBytes)
        goto errMemory;
    
    if (GetEnhMetaFileBits((HENHMETAFILE)lpobj2->hemf, lpobj2->sizeBytes, lpBytes2) != lpobj2->sizeBytes)
        goto errMemory;
    
    if (CmpGlobals (hBytes1, hBytes2))
        retval = OLE_OK;
    else
        retval = OLE_ERROR_NOT_EQUAL;
    
errMemory:   
   if (lpBytes1)
      GlobalUnlock(hBytes1);
   if (hBytes1)
      GlobalFree(hBytes1);

   if (lpBytes2)
      GlobalUnlock(hBytes1);
   if (hBytes2)
      GlobalFree(hBytes1);

   return retval;
}


OLESTATUS FARINTERNAL  EmfCopy (LPOLEOBJECT lpoleobj)
{
    LPOBJECT_EMF lpobj = (LPOBJECT_EMF)lpoleobj;
    HENHMETAFILE hemf;

    if (!((HENHMETAFILE)hemf = CopyEnhMetaFile ((HENHMETAFILE)lpobj->hemf, NULL)))
        return OLE_ERROR_MEMORY;

    SetClipboardData(CF_ENHMETAFILE, hemf);
    
    return OLE_OK;
}



OLESTATUS FARINTERNAL EmfQueryBounds (
    LPOLEOBJECT lpoleobj,
    LPRECT      lpRc
){
    LPOBJECT_EMF lpobj = (LPOBJECT_EMF)lpoleobj;

    Puts("EmfQueryBounds");

    if (!lpobj->hemf)
        return OLE_ERROR_BLANK;
    
    // Bounds are given in MM_HIMETRIC mode. 
        
    lpRc->left      = 0;  
    lpRc->top       = 0;  
    lpRc->right     = (int) lpobj->head.cx;
    lpRc->bottom    = (int) lpobj->head.cy;
    return OLE_OK;

}

OLECLIPFORMAT FARINTERNAL  EmfEnumFormat (
    LPOLEOBJECT   lpoleobj,
    OLECLIPFORMAT cfFormat
){
    UNREFERENCED_PARAMETER(lpoleobj);

    if (!cfFormat)
        return CF_ENHMETAFILE;

    return 0;
}


OLESTATUS FARINTERNAL EmfGetData (
    LPOLEOBJECT   lpoleobj,
    OLECLIPFORMAT cfFormat,
    LPHANDLE      lphandle
){
    LPOBJECT_EMF lpobj = (LPOBJECT_EMF)lpoleobj;
    
    if (cfFormat != CF_ENHMETAFILE) 
        return OLE_ERROR_FORMAT;
    
    if (!(*lphandle = lpobj->hemf))
        return OLE_ERROR_BLANK;
    
    return OLE_OK;
}


LPOBJECT_EMF FARINTERNAL  EmfCreateObject (
    HANDLE          hMeta,
    LPOLECLIENT     lpclient,
    BOOL            fDelete,
    LHCLIENTDOC     lhclientdoc,
    LPCSTR          lpobjname,
    LONG            objType
){
    LPOBJECT_EMF     lpobj;

    if (lpobj = EmfCreateBlank (lhclientdoc, (LPSTR)lpobjname, objType)) {
        if (EmfChangeData ((LPOLEOBJECT)lpobj, hMeta, lpclient, fDelete) != OLE_OK) {
            EmfRelease ((LPOLEOBJECT)lpobj);
            lpobj = NULL;
        }
    }
    
    return lpobj;
}

// If the routine fails then the object will be left with it's old data.
// If fDelete is TRUE, then hMeta, and the hMF it contains will be deleted
// whether the routine is successful or not.

OLESTATUS FARINTERNAL EmfChangeData (
    LPOLEOBJECT     lpoleobj,
    HANDLE          hMeta,
    LPOLECLIENT     lpclient,
    BOOL            fDelete
){
    LPOBJECT_EMF    lpobj   = (LPOBJECT_EMF)lpoleobj;
    DWORD           dwSizeBytes;
	 
	 Puts("EmfChangeData");

    if (hMeta) {
       dwSizeBytes = lpobj->sizeBytes;
       if (lpobj->sizeBytes = GetEnhMetaFileBits(hMeta, 0, NULL)) {
         if (lpobj->hemf)
            DeleteEnhMetaFile ((HENHMETAFILE)lpobj->hemf);
         if (fDelete)
            lpobj->hemf = hMeta;
         else 
            (HENHMETAFILE)lpobj->hemf = CopyEnhMetaFile(hMeta,NULL);
         lpobj->head.lpclient = lpclient;
         EmfSetExtents (lpobj);  
         return OLE_OK;
       }
       else 
         lpobj->sizeBytes = dwSizeBytes;
    }

    return OLE_ERROR_METAFILE;  
 
}


LPOBJECT_EMF FARINTERNAL EmfCreateBlank(
    LHCLIENTDOC lhclientdoc,
    LPSTR       lpobjname,
    LONG        objType
){
    HOBJECT     hobj;
    LPOBJECT_EMF lpobj;

    if(!(hobj = GlobalAlloc (GHND, sizeof(OBJECT_EMF))))
        return NULL;

    if (!(lpobj = (LPOBJECT_EMF) GlobalLock (hobj))){
        GlobalFree (hobj);
        return NULL;
    }

    lpobj->head.objId[0] = 'L';
    lpobj->head.objId[1] = 'E';
    lpobj->head.ctype    = objType;
    lpobj->head.lpvtbl   = (LPOLEOBJECTVTBL)&vtblEMF;
    lpobj->head.iTable   = INVALID_INDEX;
    lpobj->head.hobj     = hobj;

    if (objType == CT_STATIC)
        DocAddObject ((LPCLIENTDOC) lhclientdoc, 
                    (LPOLEOBJECT) lpobj, lpobjname);
                
    // Unlock will be done at object deletion time.
    return lpobj;
}


OLESTATUS  FARINTERNAL  EmfLoadFromStream (
    LPOLESTREAM         lpstream,
    LPOLECLIENT         lpclient,
    LHCLIENTDOC         lhclientdoc,
    LPSTR               lpobjname,
    LPOLEOBJECT FAR *   lplpobj,
    LONG                objType
){
    LPOBJECT_EMF lpobj   = NULL;
    OLESTATUS    retval  = OLE_ERROR_STREAM;
    HANDLE       hBytes  = NULL;
    LPBYTE       lpBytes = NULL;
    
    // Class name would've been read by this time.
        
    *lplpobj = NULL;        
    
    if (!(lpobj = EmfCreateBlank (lhclientdoc, lpobjname, objType)))
        return OLE_ERROR_MEMORY;

    lpobj->head.lpclient = lpclient;

    if (GetBytes (lpstream, (LPSTR) &lpobj->head.cx, sizeof(LONG)))
        goto error;
    
    if (GetBytes (lpstream, (LPSTR) &lpobj->head.cy, sizeof(LONG))) 
        goto error;
    
    if (GetBytes (lpstream, (LPSTR) &lpobj->sizeBytes, sizeof(LONG)))
        goto error;
    
    if (!lpobj->sizeBytes) {
        retval = OLE_ERROR_BLANK;
        goto error;
    }
    
    retval = OLE_ERROR_MEMORY;
    if (!(hBytes = GlobalAlloc (GHND, lpobj->sizeBytes)))
        goto error;
    
    if (!(lpBytes = (LPBYTE)GlobalLock (hBytes))) 
        goto error;
    
    if (GetBytes (lpstream, (LPSTR)lpBytes, lpobj->sizeBytes)) 
        goto error;
    
    if (!((HENHMETAFILE)lpobj->hemf = SetEnhMetaFileBits (lpobj->sizeBytes,lpBytes)) )
        goto error;
        
    EmfSetExtents (lpobj);

    *lplpobj = (LPOLEOBJECT) lpobj;
    GlobalUnlock(hBytes);
    GlobalFree (hBytes);
    return OLE_OK;

error: 
    if (lpBytes)
      GlobalUnlock(hBytes);
    if (hBytes)
      GlobalFree (hBytes);
    
    OleDelete ((LPOLEOBJECT)lpobj);
    return retval;
}

OLESTATUS FARINTERNAL  EmfPaste (
    LPOLECLIENT         lpclient,
    LHCLIENTDOC         lhclientdoc,
    LPSTR               lpobjname,
    LPOLEOBJECT FAR *   lplpoleobject,
    LONG                objType
){
    HANDLE      hMeta;

    *lplpoleobject = NULL;
    
    if((hMeta = GetClipboardData (CF_ENHMETAFILE)) == NULL)
        return OLE_ERROR_MEMORY;

    if (!(*lplpoleobject = (LPOLEOBJECT) EmfCreateObject (hMeta, lpclient, 
                                                FALSE, lhclientdoc, 
                                                lpobjname, objType)))
        return OLE_ERROR_MEMORY;

    return OLE_OK;
}

void FARINTERNAL EmfSetExtents (LPOBJECT_EMF lpobj)
{
    ENHMETAHEADER enhmetaheader;

    GetEnhMetaFileHeader((HENHMETAFILE)lpobj->hemf, sizeof(enhmetaheader), &enhmetaheader);    

    lpobj->head.cx = enhmetaheader.rclFrame.right - enhmetaheader.rclFrame.left;
    lpobj->head.cy = enhmetaheader.rclFrame.top - enhmetaheader.rclFrame.bottom;

}