⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 scemf.cpp

📁 Source code for EMFexplorer 1.0
💻 CPP
📖 第 1 页 / 共 2 页
字号:
/*
*	This file is part of the EMFexplorer projet.
*	Copyright (C) 2004 Smith Charles.
*
*	This library is free software; you can redistribute it and/or
*	modify it under the terms of the GNU Lesser General Public
*	License as published by the Free Software Foundation; either
*	version 2.1 of the License, or (at your option) any later version.
*
*   This library is distributed in the hope that it will be useful,
*   but WITHOUT ANY WARRANTY; without even the implied warranty of
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
*   Lesser General Public License for more details.
*
*   You should have received a copy of the GNU Lesser General Public
*   License along with this library; if not, write to the Free Software
*   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
*
*	Extension: for commercial use, apply the Equity Public License, which
*	adds to the normal terms of the GLPL a condition of donation to the author.
*   If you are interested in support for this source code,
*   contact Smith Charles <smith.charles@free.fr> for more information.
*/


#include "stdafx.h"
#include "SCEMF.h"

#include "SCGenInclude.h"
#include SC_INC_WINLIB(SCBitmap.h)
#include SC_INC_WINLIB(SCMemDC.h)
#include SC_INC_WINLIB(SCRichEdit.h)
#include SC_INC_WINLIB(SCGDIUtils.h)
#include <wingdi.h>

//	#ifdef _DEBUG
//	#define new DEBUG_NEW
//	#undef THIS_FILE
//	static char THIS_FILE[] = __FILE__;
//	#endif

// 1 inch => 2540 0.01mm
#define L001MMPERINCH 2540L

//	#define SC_ADJUST_EMFDPI

#define SC_PRIVILEGE_EMFDPI

// Get DPI of EMF
BOOL SCGetEMFDPIs(HENHMETAFILE hEMF, long &lDPIX, long &lDPIY)
{
	long lEmfPaperCx;
	long lEmfPaperCy;
	return SCGetEMFInfos(hEMF, lDPIX, lDPIY, lEmfPaperCx, lEmfPaperCy);
}

// Get DPI and paper size of EMF
BOOL SCGetEMFInfos(HENHMETAFILE hEMF, long &lDPIX, long &lDPIY, long &lPaperCx, long &lPaperCy)
{
	ENHMETAHEADER EmfHeader;
	if (hEMF && ::GetEnhMetaFileHeader(hEMF, sizeof(ENHMETAHEADER), &EmfHeader))
	{
		// this paper size is not reliable (there are a lot of 1024x768 around there)
		lPaperCx = EmfHeader.szlDevice.cx;
		lPaperCy = EmfHeader.szlDevice.cy;

		// or this paper size (in mm) is not reliable (there are a lot of 320x240 around there,
		// yielding 81x81 DPI device)
		lDPIX = MulDiv(lPaperCx, 254, EmfHeader.szlMillimeters.cx*10L);
		lDPIY = MulDiv(lPaperCy, 254, EmfHeader.szlMillimeters.cy*10L);

#ifdef SC_ADJUST_EMFDPI
		// TODO: fix this or undef SC_ADJUST_EMFDPI
		// Ask Microsoft how to get 96 for EMF taylored on a 96 DPI device
		if (81==lDPIX)
			lDPIX = 96;
		if (81==lDPIY)
			lDPIY = 96;
#endif
		return TRUE;
	}
	
	return FALSE;
}


//------------------------------------
#define SC_KEEP_XY		0
#define SC_KEEP_Y		1
#define SC_KEEP_X		2
//	#define SC_USE_BOUNDS
///
/// Get size of the given EMF ( doesn't take page rotations into account).
/// (This function attempts to guess the best size to use in order to display an EMF
/// at actual size)
///
BOOL SCGetEMFPlaySize(HENHMETAFILE hEMF, CSize& sizeEMF, HDC hDC/*=NULL*/, float* pfScale/*=NULL*/)
{
	ASSERT(hEMF);

	// Collect information about the EMF
	ENHMETAHEADER EmfHeader;
	if (!::GetEnhMetaFileHeader(hEMF, sizeof(ENHMETAHEADER), &EmfHeader))
		return FALSE;

#ifdef SC_USE_BOUNDS
	sizeEMF.cx = EmfHeader.rclBounds.right - EmfHeader.rclBounds.left + 1;
	sizeEMF.cy = EmfHeader.rclBounds.bottom - EmfHeader.rclBounds.top + 1;
	if (sizeEMF.cx<=0)
		sizeEMF.cx = 1;
	if (sizeEMF.cy<=0)
		sizeEMF.cy = 1;
#else
	long lEmfDPIX = 0;
	long lEmfDPIY = 0;
	long lEmfPaperCx = 0;
	long lEmfPaperCy = 0;
	if (!SCGetEMFInfos(hEMF, lEmfDPIX, lEmfDPIY, lEmfPaperCx, lEmfPaperCy))
		return FALSE;

	int bKeep = SC_KEEP_XY;
	int iWidth, iHeight;

	{// Scale dimensions to ensure correct aspect ratio
		BOOL bTmpDC = (NULL==hDC);
		if (bTmpDC)
		{
			hDC = ::GetDC(NULL);
		}
		ASSERT(hDC);
		if (!hDC)
			return FALSE;

		// adjust paper dimensions for device play
		// (for DPI independence, in case EMF DPIs and DPIs are different)
		if (!SCScaleImgSurface(hDC, lEmfDPIX, lEmfDPIY, lEmfPaperCx, lEmfPaperCy))
		{
			ASSERT(0);
			return FALSE;
		}

		// compute which paper dimension to keep, while preserving aspect ratio
		// (always keep the smaller dimension)
#ifdef SC_PRIVILEGE_EMFDPI
		// Using supposed EMF DPI instead of device DPI
		iWidth = MulDiv(EmfHeader.rclFrame.right - EmfHeader.rclFrame.left + 1, lEmfDPIX, L001MMPERINCH);
		iHeight = MulDiv(EmfHeader.rclFrame.bottom - EmfHeader.rclFrame.top + 1, lEmfDPIY, L001MMPERINCH);
#else
		GetEMFDimension(hDC, EmfHeader, iWidth, iHeight);
#endif
		if (!SCScaleImgSurface(hDC, lEmfDPIX, lEmfDPIY, (long&)iWidth, (long&)iHeight))
		{
			ASSERT(0);
			return FALSE;
		}
		if (iWidth<=0)
			iWidth = 1;
		if (iHeight<=0)
			iHeight = 1;

		if ((iWidth>lEmfPaperCx) || (iHeight>lEmfPaperCy))
		{// EMF was taylored with different resolution
			if (lEmfPaperCx>=lEmfPaperCy)
			{// keep y, and reduce x
				bKeep = SC_KEEP_Y;
			} else
			{// keep x, and reduce y
				bKeep = SC_KEEP_X;
			}
		} else
		{
			lEmfPaperCx = iWidth;
			lEmfPaperCy = iHeight;
		}

		// done with DC
		if (bTmpDC)
			::ReleaseDC(NULL,hDC);
	}

	// Final dimensions
	if (SC_KEEP_XY==bKeep)
	{
		sizeEMF.cx = lEmfPaperCx;
		sizeEMF.cy = lEmfPaperCy;
		if (pfScale)
			*pfScale = 1;
	} else
	if (SC_KEEP_Y==bKeep)
	{
		sizeEMF.cy = min(lEmfPaperCy, iHeight);
		sizeEMF.cx = ::MulDiv(sizeEMF.cy, iWidth, iHeight);
		if (sizeEMF.cx<=0)
			sizeEMF.cx = 1;
		if (pfScale)
			*pfScale = float(sizeEMF.cx)/float(lEmfPaperCx);
	} else
	{
		sizeEMF.cx = min(lEmfPaperCx, iWidth);
		sizeEMF.cy = ::MulDiv(sizeEMF.cx, iHeight, iWidth);
		if (sizeEMF.cy<=0)
			sizeEMF.cy =1;
		if (pfScale)
			*pfScale = float(sizeEMF.cy)/float(lEmfPaperCy);
	}
	// Note: at this point sizeEMF may be equal to rclBounds, meaning it may exceed
	// paper size (most likely for thin, long, images that require floating points
	// calculation for proper scaling).
#endif
	return TRUE;
}

#undef SC_KEEP_XY
#undef SC_KEEP_Y
#undef SC_KEEP_X
//-------------------------------

///
/// Smallest rectangle surrounding the image.
///
BOOL SCGetEMFElemsRect(HENHMETAFILE hEMF, CRect& rcElems, fnSCPlayEnhMetaFile fnPlayEMF/*=NULL*/)
{
	ENHMETAHEADER EmfHeader;
	if (hEMF && ::GetEnhMetaFileHeader(hEMF, sizeof(ENHMETAHEADER), &EmfHeader))
	{
#if 0
	// the 81 DPI problem would cumulate
		// compute bounds in pixels
		HDC hDC = ::GetDC(NULL);
		ASSERT(hDC);
		if (!hDC)
			return FALSE;
		
		// compute actual EMF DPIs, based on the dimensions of its creation device
		long lEmfDPIX = MulDiv(EmfHeader.szlDevice.cx, 254, EmfHeader.szlMillimeters.cx*10L);
		long lEmfDPIY = MulDiv(EmfHeader.szlDevice.cy, 254, EmfHeader.szlMillimeters.cy*10L);

#ifdef SC_ADJUST_EMFDPI
		// see SCGetEMFInfos for documentation
		if (81==lEmfDPIX)
			lEmfDPIX = 96;
		if (81==lEmfDPIY)
			lEmfDPIY = 96;
		//
#endif
		
		// scale the surface for DPI independence
		long lDevDPIX = ::GetDeviceCaps(hDC, LOGPIXELSX);
		long lDevDPIY = ::GetDeviceCaps(hDC, LOGPIXELSY);
		::ReleaseDC(NULL,hDC);
		ASSERT(lDevDPIX);
		ASSERT(lDevDPIY);

		rcElems.left = MulDiv(EmfHeader.rclBounds.left, lDevDPIX, lEmfDPIX);
		rcElems.top = MulDiv(EmfHeader.rclBounds.top, lDevDPIY, lEmfDPIY);
		rcElems.right = MulDiv(EmfHeader.rclBounds.right, lDevDPIX, lEmfDPIX);
		rcElems.bottom = MulDiv(EmfHeader.rclBounds.bottom, lDevDPIY, lEmfDPIY);

		// Ckeck bounds
		int iWidth = rcElems.Width();
		int iHeight = rcElems.Height();
		if (iWidth && iHeight)
		{
			// Ok! This is my bug: I won't allow an EMF whose dimensions exceed its device
			// to display at actual size.
			if (iWidth<=EmfHeader.szlDevice.cx && iHeight<=EmfHeader.szlDevice.cy)
			{
				// We take whatever is given
				return TRUE;
			} // else big EMFs will be sized down
		}
#endif
		SCComputeEMFBlackBox(hEMF, rcElems, fnPlayEMF);
		return TRUE;
	}

	return FALSE;
}

///
///  Compute EMF frame dimension in pixels.
///
void GetEMFDimension(HDC hDC, ENHMETAHEADER &emfh, int & width, int & height)
{
	// Calculate the rect in which to draw the metafile based on the
	// intended size and the current output device resolution.
	// Remember that the intended size is given in 0.01 mm units, so
	// convert those to device units on the target device.

#ifdef SC_PRIVILEGE_EMFDPI
	// Compute actual EMF DPIs, based on the dimensions of its creation device,
	// and prefer these DPIs over output device DPI
	int iDpiX = MulDiv(emfh.szlDevice.cx, 254, emfh.szlMillimeters.cx*10L);
	int iDpiY = MulDiv(emfh.szlDevice.cy, 254, emfh.szlMillimeters.cy*10L);
#else
	// Get the characteristics of the output device.
	int iDpiX = GetDeviceCaps(hDC,  LOGPIXELSX);
	int iDpiY = GetDeviceCaps(hDC,  LOGPIXELSY);
#endif
	
	// Convert from 0.01 mm to pixels.
	RECTL& rRect = emfh.rclFrame;
	width = MulDiv(rRect.right - rRect.left, iDpiX, L001MMPERINCH);
	height = MulDiv(rRect.bottom - rRect.top, iDpiY, L001MMPERINCH);
}

// Scale an image surface according to a destination device resolution
BOOL SCScaleImgSurface(HDC hDC, long lImgDPIX, long lImgDPIY, long &lImgCx, long &lImgCy)
{
	ASSERT(hDC);
	if (!hDC)
		return FALSE;

	long lDevDPIX = ::GetDeviceCaps(hDC, LOGPIXELSX);
	long lDevDPIY = ::GetDeviceCaps(hDC, LOGPIXELSY);

	ASSERT(lDevDPIX);
	ASSERT(lDevDPIY);
	if (lDevDPIX && lDevDPIY)
	{
		lImgCx = MulDiv(lImgCx, lDevDPIX, lImgDPIX);
		lImgCy = MulDiv(lImgCy, lDevDPIY, lImgDPIY);
		return TRUE;
	}
	return FALSE;
}

//
// Compute the dimensions, in device units, of the smallest rectangle that can be drawn
// around the picture stored in the metafile.
// Note: this is the job of Windows. But rclBounds is not always accurate. So we are
// taking an actual snapshot of the black box on a memory DC compatible with the screen.
//
// Note: Beware that this function clips any white-on-white element laying on the EMF's
// boundaries. That's the reason why ElemsRect may be smaller than EmfHeader.rclBounds.
// Empty lines are good examples of white-on-white elements.
//
BOOL SCComputeEMFBlackBox(HENHMETAFILE hEmf, CRect& ElemsRect, fnSCPlayEnhMetaFile fnPlayEMF/*=NULL*/)
{
	// We try to create the emf handle
	ASSERT(hEmf);
	ENHMETAHEADER EmfHeader;
	if (!hEmf || !::GetEnhMetaFileHeader(hEmf, sizeof(ENHMETAHEADER), &EmfHeader))
		return FALSE;
#ifdef SC_USE_BOUNDS
	// If we could rely on rclBounds
	CopyRect(&ElemsRect, (LPCRECT)&EmfHeader.rclBounds);
#else
	// Collect information from the EMF
	CSize sizeEMF;
	SCGetEMFPlaySize(hEmf, sizeEMF);
	CRect rcPlay(0, 0, sizeEMF.cx, sizeEMF.cy);

	// Draw to extract bounds (use a large surface)
	ElemsRect.CopyRect((LPRECT)&rcPlay);

	CSCMemDC MemDC;
	MemDC.SCPrepareSurface(sizeEMF.cx, sizeEMF.cy, hEmf);
	//MemDC.PatBlt(0, 0, sizeEMF.cx, sizeEMF.cy, WHITENESS); // background is white

	// Play in rcPlay to conform to aspect ratio
	BOOL bOk;
	if (NULL==fnPlayEMF)
		bOk = ::PlayEnhMetaFile(MemDC.m_hDC, hEmf, (LPRECT)&rcPlay);
	else
		bOk = fnPlayEMF(MemDC.m_hDC, hEmf, (LPRECT)&rcPlay);

	// Extract bounding box (start out with the whole bitmap surface)
	// final ElemsRect should be smaller or equal to this rcPlay
	CRect& R = ElemsRect;
	
	PBITMAPINFOHEADER pbih;     // bitmap info-header
	HBITMAP hDib = MemDC.STGetDCDIB();
	ASSERT(hDib);
	
	// Retrieve the information describing the DIB.
	DIBSECTION ds;
	::GetObject(hDib, sizeof(DIBSECTION), &ds);
	pbih = (PBITMAPINFOHEADER)&ds.dsBmih;
	ASSERT(ds.dsBm.bmBits); // must be a DIB
	
	// Compute image size in pixels
	DWORD dwBitsSize = (pbih->biSizeImage!=0) ?
		pbih->biSizeImage : abs(pbih->biHeight)*(((pbih->biWidth*pbih->biBitCount + 31) & ~31) >> 3);

	BYTE *pBottom = SCExtractMemBoundingBox((LPBYTE)ds.dsBm.bmBits, &R, dwBitsSize,
							ds.dsBm.bmWidthBytes, (short)ds.dsBm.bmBitsPixel);
	ASSERT(pBottom);
	
	if (!::IsRectEmpty(&R))
	{
		// adjustment for bottom-up image
		if (pbih->biHeight>0)
		{
			long t = rcPlay.bottom - R.bottom - 1;	  // new top = old height - pos of bottom pixel
			long b = t + (R.bottom - R.top);      // new top + new height
			// security
			ASSERT(t>=-1);
			if (t<0)
				t = 0;
			
			ASSERT(b>=-1);
			if (b<0)
				b = 0;
			
			// adjust rect
			R.top = t;		// number of blank pixels at the top
			R.bottom = b+1;	// below the bottom pixel
			R.right++;		// at right of the rightmost pixel
			// R.left = number of blank pixels on the left
		} else
		{ // adjust rect, for pixels on the left column and at the bottom line
			R.bottom++;
			R.right++;
		}
		
		ASSERT(R.left>=0);
		ASSERT(R.top>=0);
		ASSERT(R.right<=rcPlay.right);
		ASSERT(R.bottom<=rcPlay.bottom);
	}
#endif
	return TRUE;
}

///
/// Metafile to Enhanced Metafile format
///
HENHMETAFILE SCConvertWMFtoEMF(LPCTSTR lpszFname)
{
	// Open the file for reading. 
	CFile sourceFile;
	CFileException ex;
	if (!sourceFile.Open(lpszFname, CFile::modeRead, &ex))
		return NULL;

	UINT uiSize = sourceFile.GetLength();
	LPBYTE pBuffer = new BYTE[uiSize];
	if (!pBuffer)
	{
		sourceFile.Close();
		return NULL;
	}
	DWORD dwNbRead = sourceFile.Read(pBuffer, uiSize);
	sourceFile.Close();

	HENHMETAFILE hEMF = SCConvertWMFtoEMF(dwNbRead, pBuffer);

	// Cleanup objects.
	delete [] pBuffer;
	return hEMF;
}

HENHMETAFILE SCConvertWMFtoEMF(DWORD dwSize, LPBYTE pBuffer)
{
	LPBYTE pData = pBuffer;
	UINT uiSize = dwSize;
    APMFILEHEADER*	pAPMHeader = (APMFILEHEADER*)pBuffer;
	APMFILEHEADER	winWMFHeader;

	if (WMFMETA_PLACEABLEKEY==pAPMHeader->key)
	{// aldus
		pData += sizeof(APMFILEHEADER);
		uiSize -= sizeof(APMFILEHEADER);
	} else
	{// windows
		// Oh! You didn't place your metafile? Good for you!
		pAPMHeader = &winWMFHeader;
		memset(pAPMHeader, 0, sizeof(winWMFHeader));
	}
	HWND	ghwndDrawSurf = NULL;
	HDC hDCDrawSurf = GetDC(ghwndDrawSurf);
	
	METAFILEPICT mfPict;
	mfPict.hMF = 0;
	mfPict.mm = MM_ANISOTROPIC;

	METAFILEPICT *lpmfp   = NULL;
	if (pAPMHeader->inch)
	{
		lpmfp   = &mfPict;
		SMALL_RECT* prcPict = &pAPMHeader->bbox;
#if 1 
		mfPict.xExt = MulDiv((long)(prcPict->Right - prcPict->Left), 2540, pAPMHeader->inch);
		mfPict.yExt = MulDiv((long)(prcPict->Bottom - prcPict->Top), 2540, pAPMHeader->inch);
#else
		mfPict.xExt = 0;
		mfPict.yExt = 0;
		// Code from Microsoft's mfedit.c ==> BAD RESULT
		switch (pAPMHeader->inch) { 
			// !!! End up in an upside down image 
			// 
		case 1440: 
			mfPict.mm = MM_TWIPS; 
			break; 
		case 2540: 
			mfPict.mm = MM_HIMETRIC; 
			break; 
		case 254: 
			mfPict.mm = MM_LOMETRIC; 
			break; 
		case 1000: 
			mfPict.mm = MM_HIENGLISH; 
			break; 
		case 100: 
			mfPict.mm = MM_LOENGLISH; 
			break; 
		default: 
			// !!! In addition, text is too small 
			// 
			mfPict.mm = MM_ANISOTROPIC; 
			mfPict.xExt = ((long)(prcPict->Right - prcPict->Left)) * pAPMHeader->inch * 2560; 
			mfPict.yExt = ((long)(prcPict->Bottom - prcPict->Top)) * pAPMHeader->inch * 2560; 
			break; 
		}
#endif
	}
	
	// Generate EMF
	HENHMETAFILE hEMF = SetWinMetaFileBits(uiSize, pData, hDCDrawSurf, lpmfp);

	// Cleanup
	ReleaseDC(ghwndDrawSurf ,hDCDrawSurf);
	return hEMF;
}

///
/// Enhanced Metafile to Windows Metafile format on disk
///
void SCConvertEMFtoWMF(HENHMETAFILE hEMF, LPCTSTR lpszFileName)
{
	LPBYTE       lpEMFBits;
	UINT         uiSizeBuf;
	HDC			 hrefDC = GetDC(NULL);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -