Click here to Skip to main content
15,886,075 members
Articles / Multimedia / GDI+

Presenting EMFexplorer, a GDI+ experiment

Rate me:
Please Sign up or sign in to vote.
4.90/5 (28 votes)
29 Sep 20047 min read 128.9K   3.2K   49  
High quality EMF rendering, using GDI+
/*
*	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 "SCEMFDoc.h"

#include "SCGenInclude.h"
#include SC_INC_COMMON(kSCProdDefs.h)
#include SC_INC_WINLIB(SCWinFile.h)
#include SC_INC_ERRLIB(wErr.h)
#include SC_INC_SHARED(SCZipFile.h)
#include SC_INC_WINLIB(SCRegistry.h)


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

#define SC_EMFX_TMPFONT_EXT		_T(".eft")
#define SC_EMFX_TMPFRES_EXT		_T(".efr")
#define SC_EMFX_TEMPSUBDIR		SC_PRODUCTNAME
#define SC_EMFX_CLIP_FNAME		_T("Clipboard")


SCEMFDoc::SCEMFDoc():
	m_uiNbFiles(0),
	m_bDocModified(FALSE),
	m_bForgedName(FALSE)
{
}

SCEMFDoc::~SCEMFDoc()
{
	SCCleanup(TRUE);
	SCDeleteLockedFontFiles(TRUE);
}

void SCEMFDoc::SCCleanup(BOOL bClosing /*=FALSE*/)
{
	UINT uiNbPages = m_uiNbFiles;
	m_uiNbFiles = 0; // Won't serve pages after this, as we are deleting them

	// Proceed with clean up
	for (UINT i=0; (i<uiNbPages); i++)
	{
		delete m_vDocPages[i];
	}
	m_vDocPages.clear();
	SCUnInstallFonts();
	m_strUniDocName.Empty();
	m_bForgedName = FALSE;
}

void SCEMFDoc::SCSetPageURL(UINT uiPage, LPCTSTR lpszURL)
{
	ASSERT(uiPage<m_uiNbFiles);
	if (uiPage<m_uiNbFiles)
		m_vDocPages[uiPage]->m_strCreditURL = lpszURL;
}

void SCEMFDoc::SCSetPageCredit(UINT uiPage, LPCTSTR lpszCredit)
{
	ASSERT(uiPage<m_uiNbFiles);
	if (uiPage<m_uiNbFiles)
		m_vDocPages[uiPage]->m_strCredit = lpszCredit;
}

void SCEMFDoc::SCSetPageComment(UINT uiPage, LPCTSTR lpszComment)
{
	ASSERT(uiPage<m_uiNbFiles);
	if (uiPage<m_uiNbFiles)
		m_vDocPages[uiPage]->m_strComment = lpszComment;
}

PSCEMFDocPage SCEMFDoc::SCGetDocPage(UINT uiPage, BOOL bGetCopy/*=FALSE*/)
{
	ASSERT(uiPage<m_uiNbFiles);
	PSCEMFDocPage pDocPage = NULL;
	if (uiPage<m_uiNbFiles)
	{
		ASSERT(m_vDocPages[uiPage]);
		if (bGetCopy)
		{
			pDocPage = new SCEMFDocPage(this);
			pDocPage->SCCopyFrom(*m_vDocPages[uiPage]);
		} else
			pDocPage = m_vDocPages[uiPage];
	}
	return pDocPage;
}

HENHMETAFILE SCEMFDoc::SCUnlockEMF(UINT uiPage, PSCEMFDocPage pShare/*=NULL*/)
{
	ASSERT(uiPage<m_uiNbFiles);
	PSCEMFDocPage pDocPage = m_vDocPages[uiPage];
	ASSERT(pDocPage);
	HENHMETAFILE hMemEMF = pDocPage->SCUnlockEMF();
	if (pShare)
		pShare->m_hEMF = hMemEMF;
	return hMemEMF;
}

void SCEMFDoc::SCForgeDocName(LPCTSTR lpszFname)
{// forge a doc name
	m_strUniDocName = lpszFname;
	int iPos = m_strUniDocName.ReverseFind(_T('.'));
	if (iPos!=-1)
		m_strUniDocName = m_strUniDocName.Left(iPos);
	m_strUniDocName += SC_DOC_EXTENSION;
	m_bForgedName = TRUE;
}

void SCEMFDoc::SCRemovePage(UINT uiPage)
{
	ASSERT(uiPage<m_uiNbFiles);

	delete m_vDocPages[uiPage];
	m_vDocPages.erase(m_vDocPages.begin() + uiPage);
	m_uiNbFiles--;
	if ((0==m_uiNbFiles) && m_bForgedName)
		SCCleanup();
	m_bDocModified = TRUE;
}

BOOL SCEMFDoc::SCReflow(DOCPAGEVECTOR& rvectReflow, INTVECTOR& rvectDescrip, LPCTSTR lpszDocDir, int iDlftCrdPage)
{
	UINT uiNbElems = rvectReflow.size();

	ASSERT(rvectDescrip.size()==uiNbElems);
	ASSERT(lpszDocDir);
	ASSERT(iDlftCrdPage<(int)uiNbElems);
	{
		CString strOldDocDir;
		TCHAR szDrive[_MAX_DRIVE];
		TCHAR szDir[_MAX_DIR];
		TCHAR szFname[_MAX_FNAME];
		TCHAR szExt[_MAX_EXT];
		SCSplitPath(m_strUniDocName, szDrive, szDir, szFname, szExt);
		
		strOldDocDir.Format(_T("%s%s"), szDrive, szDir);
		if (0!=strOldDocDir.CompareNoCase(lpszDocDir))
			m_strUniDocName.Format(_T("%s%s%s"), lpszDocDir, szFname, szExt);
	}

	PSCEMFDocPage pEPage;
	// Detach old EMFs
	for (UINT uiPage=0; (uiPage<uiNbElems); uiPage++)
	{
		int iIdx = rvectDescrip[uiPage];
		if (iIdx>=0)
		{
			ASSERT(iIdx<m_uiNbFiles);
			pEPage=m_vDocPages[iIdx];
			ASSERT(pEPage);
			if (pEPage)
				pEPage->SCDetachEMF();
		}
	}
	// Delete old objects
	for (uiPage=0; (uiPage<m_uiNbFiles); uiPage++)
	{
		if (pEPage=m_vDocPages[uiPage])
			delete pEPage;
	}
	m_vDocPages.clear();
	// Copy new objects
	m_vDocPages.resize(uiNbElems);
	m_vDocPages.assign(rvectReflow.begin(), rvectReflow.end());
	rvectReflow.clear();

	m_uiNbFiles = m_vDocPages.size();

	if (iDlftCrdPage<0)
	{
		m_Properties.strCreditURL.Empty();
		m_Properties.strCredit.Empty();
	} else
	{
		pEPage=m_vDocPages[iDlftCrdPage];
		m_Properties.strCreditURL = pEPage->m_strCreditURL;
		m_Properties.strCredit = pEPage->m_strCredit;
	}

	m_bDocModified = TRUE;
	return TRUE;
}

void SCEMFDoc::SCBeginAddFiles(UINT uiNbFiles, LPCTSTR lpszFName/*=NULL*/)
{
	SCCleanup();
	if (lpszFName)
		m_strUniDocName = lpszFName;
	
	// pages bank
	if (uiNbFiles)
	{
		m_vDocPages.resize(uiNbFiles);
		m_uiNbFiles = uiNbFiles;
		m_vDocPages.assign(m_uiNbFiles, NULL);
	}
}

void SCEMFDoc::SCEndAddFiles()
{
	SCInstallFonts();
}

void SCEMFDoc::SCSetDocDir(LPCTSTR lpszDocDir)
{
	DWORD dwLen = (lpszDocDir) ? _tcslen(lpszDocDir) : 0;
	ASSERT(dwLen);
	ASSERT(!m_strUniDocName.IsEmpty());
	CString strOldDocDir = SCMakeupDocDir(m_strUniDocName);

	ASSERT(strOldDocDir.IsEmpty()||
		strOldDocDir.CompareNoCase(lpszDocDir)==0);
	if (strOldDocDir.IsEmpty())
	{
		ASSERT(m_bForgedName);
		if (lpszDocDir[dwLen-1]!=_T('\\'))
			m_strUniDocName.Insert(0, _T('\\'));
		m_strUniDocName.Insert(0, lpszDocDir);
	}
	SCInstallFonts();
}

BOOL SCEMFDoc::SCAddFile(LPCTSTR lpszFname, UINT uiIdx, BOOL bCopy/*=TRUE*/, BOOL bDelayed/*=FALSE*/)
{
	ASSERT(uiIdx<m_uiNbFiles);
	if (uiIdx>=m_uiNbFiles)
		return FALSE;

	BOOL bError = FALSE;
	// check filetype
	UINT uiFyleType = SC_FSTATE_DELAYED; 
	if (!bDelayed)
	{
		uiFyleType = SCGetFileType(lpszFname, FALSE);
		switch (uiFyleType & SC_FTYPE_MASK)
		{
		case SC_FTYPE_EMF:
		case SC_FTYPE_WMF:
		case SC_FTYPE_EMZ:
		case SC_FTYPE_WMZ:
		case SC_FTYPE_IMG:
		case SC_FTYPE_TXT:
			break;
			
		case SC_FTYPE_UKN:
		default:
			bError = TRUE;
			// delay loading, so that user can use reflow to replace the file
			uiFyleType = SC_FSTATE_DELAYED;
		}
	}
	SCEMFDocPage* pDocPage = new SCEMFDocPage(this);
	m_vDocPages[uiIdx] = pDocPage;

	// set information for delayed loading
	pDocPage->m_uiType = uiFyleType;
	pDocPage->m_strPath = lpszFname;

	if (!bError && m_strUniDocName.IsEmpty())
		SCForgeDocName(lpszFname);
	return TRUE;
}

BOOL SCEMFDoc::SCLoadRTFPages(LPCTSTR lpszFname, BOOL bPaste/*=FALSE*/, BOOL bRTF/*=TRUE*/)
{
	HEMFVECTOR vectHandles;
	UINT uiNbPages = SCConvertRTFtoEMF(lpszFname, vectHandles, bRTF);
	if (0==uiNbPages)
		return FALSE;

	UINT uiStartIdx=0;
	if (bPaste)
	{
		uiStartIdx = m_uiNbFiles;
		m_uiNbFiles += uiNbPages;
		m_vDocPages.resize(m_uiNbFiles);
	} else
		SCBeginAddFiles(uiNbPages);

	// EMFs are just in memory, like a paste: don't forge unidoc name
	CString strPrefix = SCFNameExtFromPath(lpszFname);
	int iPos = strPrefix.ReverseFind(_T('.'));
	if (iPos!=-1)
		strPrefix.SetAt(iPos, _T('_'));
	if (!m_strUniDocName.IsEmpty())
	{
		CString strDir;
		TCHAR drive[_MAX_DRIVE];	// drive of the playlist
		TCHAR dir[_MAX_DIR];		// dir of the playlist
		_tsplitpath(LPCTSTR(m_strUniDocName), drive, dir, NULL, NULL);
		strDir.Format(_T("%s%s"), drive, dir);
		strPrefix = strDir + strPrefix;
	}

	CString strFname;
	for (UINT uiIdx=uiStartIdx; (uiIdx<m_uiNbFiles); uiIdx++)
	{
		SCEMFDocPage* pDocPage = new SCEMFDocPage(this);
		m_vDocPages[uiIdx] = pDocPage;

		strFname.Format(_T("%s_p%d.emf"), strPrefix, (uiIdx - uiStartIdx)+1);
		pDocPage->m_bDirty = TRUE;
		pDocPage->SCAttachEMF(vectHandles[uiIdx - uiStartIdx],
			strFname, SC_FILETYPE_EMFONLY);
	}

	if (!bPaste)
		SCEndAddFiles();
	m_bDocModified = TRUE; // pages are just in memory
	return TRUE;
}

BOOL SCEMFDoc::SCGrowBank(int iNbPages)
{
	m_uiNbFiles += iNbPages;
	m_vDocPages.resize(m_uiNbFiles);
	return TRUE;
}

BOOL SCEMFDoc::SCPasteEMFPages(HEMFVECTOR& rVector)
{
	int iNbPages = rVector.size();
	if (0==iNbPages)
		return FALSE;

	UINT uiNewNbPages = m_uiNbFiles + iNbPages;
	m_vDocPages.resize(uiNewNbPages);

	// Folder of the new files
	CString strDir;
	if (!m_strUniDocName.IsEmpty())
	{
		TCHAR drive[_MAX_DRIVE];	// drive of the playlist
		TCHAR dir[_MAX_DIR];		// dir of the playlist
		_tsplitpath(LPCTSTR(m_strUniDocName), drive, dir, NULL, NULL);
		strDir.Format(_T("%s%s"), drive, dir);
	}

	// Name and paste the files
	static UINT s_uiClipNum = 0;
	CString strClipFname;
	for (UINT uiIdx=m_uiNbFiles; (uiIdx<uiNewNbPages); uiIdx++)
	{
		SCEMFDocPage* pDocPage = new SCEMFDocPage(this);
		m_vDocPages[uiIdx] = pDocPage;

		strClipFname.Format(_T("%s%s%d.emf"), strDir, SC_EMFX_CLIP_FNAME, ++s_uiClipNum);
		pDocPage->m_bDirty = TRUE;
		pDocPage->SCAttachEMF(rVector[uiIdx - m_uiNbFiles],
			strClipFname, SC_FILETYPE_EMFONLY);
	}

	m_uiNbFiles = uiNewNbPages;
	m_bDocModified = TRUE; // some pages are just in memory
	return TRUE;
}


HENHMETAFILE SCEMFDoc::SCGetPageEMF(UINT uiPage)
{
	ASSERT(uiPage<m_uiNbFiles);
	if (uiPage>=m_uiNbFiles)
		return NULL;
	return m_vDocPages[uiPage]->SCGetPageEMF();
}

void SCEMFDoc::SCResizeAllPages()
{
	m_Properties.bSizeAllPages = !m_Properties.bSizeAllPages;
	if (!m_Properties.rectDocument.IsRectEmpty())
	{
		if (m_Properties.bSizeAllPages)
		{// apply
			for (UINT uiPage=0; (uiPage<m_uiNbFiles); uiPage++)
			{
				PSCEMFDocPage pDocPage = m_vDocPages[uiPage];
				ASSERT(pDocPage);
				pDocPage->m_rectSize = m_Properties.rectDocument;
			}
		} else
		{// restore
			for (UINT uiPage=0; (uiPage<m_uiNbFiles); uiPage++)
			{
				PSCEMFDocPage pDocPage = m_vDocPages[uiPage];
				ASSERT(pDocPage);
				pDocPage->SCRecomputeElemsRect();
				if (pDocPage->m_bInflate)
					pDocPage->m_rectSize.InflateRect(pDocPage->m_rectInflate);
			}
		}
		return;
	}

	ASSERT(m_Properties.bSizeAllPages);
	int iMinX = INT_MAX;
	int iMinY = INT_MAX;
	int iMaxX = INT_MIN;
	int iMaxY = INT_MIN;

	for (UINT uiPage=0; (uiPage<m_uiNbFiles); uiPage++)
	{
		PSCEMFDocPage pDocPage = m_vDocPages[uiPage];
		ASSERT(pDocPage);
		HENHMETAFILE hEMF = pDocPage->SCGetPageEMF();
		if (hEMF)
		{
			CRect& ElemsRect = pDocPage->m_rectSize;
			// deflate
			CRect& rectI = pDocPage->m_rectInflate;
			ElemsRect.InflateRect(-rectI.left, -rectI.top, -rectI.right, -rectI.bottom);
			//
			
			if (ElemsRect.left<iMinX)
				iMinX = ElemsRect.left;
			if (ElemsRect.top<iMinY)
				iMinY = ElemsRect.top;

			if (ElemsRect.right>iMaxX)
				iMaxX = ElemsRect.right;
			if (ElemsRect.bottom>iMaxY)
				iMaxY = ElemsRect.bottom;
		}
	}

	ASSERT(iMinX != INT_MAX);
	ASSERT(iMinY != INT_MAX);
	ASSERT(iMaxX != INT_MIN);
	ASSERT(iMaxY != INT_MIN);

	for (uiPage=0; (uiPage<m_uiNbFiles); uiPage++)
	{
		m_vDocPages[uiPage]->m_rectSize.SetRect(iMinX, iMinY, iMaxX, iMaxY);
	}
	m_Properties.rectDocument.SetRect(iMinX, iMinY, iMaxX, iMaxY);
}

void SCEMFDoc::SCInflateElemsRect(UINT uiPage, int iLeft, int iTop, int iRight, int iBottom)
{
	ASSERT(uiPage<m_uiNbFiles);
	if (uiPage>=m_uiNbFiles)
		return;
	m_vDocPages[uiPage]->SCInflateElemsRect(iLeft, iTop, iRight, iBottom);
}

///
/// Install all fonts found in the 'Fonts' subdirectory (of the document's directory)
///
void SCEMFDoc::SCInstallFonts()
{
	SCDeleteLockedFontFiles(); // delayed clean up

	// For now, just install what is found in the Fonts subdirectory if any
	if (m_strUniDocName.IsEmpty())
		return;

	TCHAR drive[_MAX_DRIVE];	// drive of the playlist
	TCHAR dir[_MAX_DIR];		// dir of the playlist
	_tsplitpath(LPCTSTR(m_strUniDocName), drive, dir, NULL, NULL);

	// Look for TT fonts
	CString strFile;
	strFile.Format(_T("%s%s%s"), drive, dir, _T("Fonts\\*.*"));

	CFileFind finder;
	BOOL bFound = finder.FindFile((LPCTSTR)strFile);
	BOOL bTemp = FALSE;
	CString strTempPath;
	if (bFound)
	{
		bTemp = (!SCIsWriteableMedia(m_strUniDocName));
		if (bTemp)
		{// create temp directory
			if (!::SCCreateTempDir(strTempPath, SC_EMFX_TEMPSUBDIR))
			{
				ASSERT(0);
				finder.Close();
				return;
			}
		}
	}
	
	while (bFound)
	{
		bFound = finder.FindNextFile();
		if (!finder.IsDirectory())
		{
			CString strFilePath = finder.GetFilePath();
			if (!SCIsTrueTypeFontFile(strFilePath))
				continue;

			// TODO: Check that font is not already installed
			// if (m_pSysFontsHolder && m_pSysFontsHolder->SCIsFontInstalled(strFilePath))
			// continue;

			if (bTemp)
			{// copy to temp directory
				TCHAR szFname[_MAX_FNAME];
				SCSplitPath((LPTSTR)LPCTSTR(strFilePath), NULL, NULL, szFname, NULL);
				
				CString strFilename;
				strFilename.Format(_T("%s%s%s"), strTempPath, szFname, SC_EMFX_TMPFONT_EXT);
#ifdef _DEBUG
				ASSERT(!SCExistFile(strFilename));
#endif
				BOOL bOk = CopyFile(strFilePath, strFilename, TRUE);
				ASSERT(bOk);
				strFilePath = strFilename;
			}
			
			SCInstallFontFile(strFilePath, bTemp);
		}
	}	
	finder.Close();
#ifdef _DEBUG
	int iCount = m_FontRes.GetCount();
#endif
}

///
/// Uninstall fonts, and delete temporary files.
///
void SCEMFDoc::SCUnInstallFonts()
{
	CString strTempPath;
	::SCGetFullTempDirName(strTempPath, SC_EMFX_TEMPSUBDIR);
	BOOL bTempCheck = FALSE;
	BOOL bTempFiles = FALSE;

	POSITION pos = m_FontRes.GetHeadPosition();
	CString strFilename;
	BOOL bOK;
	while(pos)
	{
		strFilename = m_FontRes.GetNext(pos);

		ASSERT(!strFilename.IsEmpty());
		bOK = RemoveFontResource(strFilename);

		ASSERT(bOK);
		if (!bOK)
			continue;
		
		// delete the scalable resource file
		bOK = DeleteFile(strFilename);
		ASSERT(bOK);
		
		if (!bTempCheck)
		{// check if TT files are in temp directory
			TCHAR szDrive[SC_MAX_UNC_DRIVE];
			TCHAR szDir[_MAX_DIR];
			SCSplitPath(strFilename, szDrive, szDir, NULL, NULL);
			CString strFilesDir;
			strFilesDir.Format(_T("%s%s"), szDrive, szDir);
			
			if (0==strTempPath.Compare(strFilesDir))
			{
				int iPos = strFilename.ReverseFind(_T('.'));
				if (iPos>0)
				{
					CString strTTFName = strFilename.Left(iPos) + SC_EMFX_TMPFONT_EXT;
					bTempFiles = SCExistFile(strTTFName);
				}
			}
			
			bTempCheck = TRUE;
		}

		// delete temporary font file
		if (bTempFiles)
		{
			int iPos = strFilename.ReverseFind(_T('.'));
			if (iPos>0)
			{
				CString strTTFName = strFilename.Left(iPos) + SC_EMFX_TMPFONT_EXT;
				bOK = SCRemoveFileAttributes(strTTFName, FILE_ATTRIBUTE_READONLY);
				ASSERT(bOK);

				bOK = DeleteFile(strTTFName);
				
				// Deceptively, Windows locks some fonts AFTER they have passed through
				// metafile playing, though they are OUR private fonts.
				// TODO: find reason.
				if (!bOK)
					m_LockedFontFiles.AddTail(strTTFName); // font to delete later
			}
		}
	}
	m_FontRes.RemoveAll();
}

///
/// Delete fonts that were locked in a previous attempt to delete them.
///
void SCEMFDoc::SCDeleteLockedFontFiles(BOOL bClosing/*=FALSE*/)
{
	POSITION pos = m_LockedFontFiles.GetHeadPosition();
	CString strFilename;

	if (bClosing)
	{// Just delete the files. They should be deleteable at this point.
		while(pos)
		{
			strFilename = m_LockedFontFiles.GetNext(pos);
			ASSERT(!strFilename.IsEmpty());

			BOOL bOK = SCRemoveFileAttributes(strFilename, FILE_ATTRIBUTE_READONLY);
			ASSERT(bOK);

			bOK = DeleteFile(strFilename);
			ASSERT(bOK);
		}
		return;
	}

	// Attemp to delete. Keep those files that are still locked.
	CStringList RemainList;
	while(pos)
	{
		strFilename = m_LockedFontFiles.GetNext(pos);
		ASSERT(!strFilename.IsEmpty());
		if (!DeleteFile(strFilename))
			RemainList.AddTail(strFilename);
	}
	m_LockedFontFiles.RemoveAll();
	pos = RemainList.GetHeadPosition();
	while(pos)
	{
		m_LockedFontFiles.AddTail(RemainList.GetNext(pos));
	}
}


///
/// Install a TrueType read-only embedded font from memory data
///
BOOL SCEMFDoc::SCInstallMemoryFont(CString strFaceName, BYTE* lpData, long lDataSize)
{
	// 1. check if its a TrueType

	// 2. avoid installing fonts on the system (copy their files
	// into temp directory)
	CString strTempPath;
	if (!::SCCreateTempDir(strTempPath, SC_EMFX_TEMPSUBDIR))
	{
		ASSERT(0);
		return FALSE;
	}

	int iPos = strFaceName.ReverseFind(_T('.'));
	if (-1==iPos)
		strFaceName += SC_EMFX_TMPFONT_EXT;
	else
		strFaceName = strFaceName.Left(iPos) + SC_EMFX_TMPFONT_EXT;

	CString strFilename = strTempPath + strFaceName;
#ifdef _DEBUG
	ASSERT(!SCExistFile(strFilename)); // it should not exist
#endif

	CFile file;
	CFileException fe;
	// create new file, 'Open' won't throw exception
	if (file.Open(strFilename, CFile::modeCreate |
	  CFile::modeWrite | CFile::shareExclusive, &fe))		
	{
		// save the data
		try
		{
			file.Write(lpData, lDataSize);
		}
		catch(CFileException* pEx)
		{
			pEx->Delete();
			ASSERT(0);
			file.Close();
			return FALSE;
		}

		file.Close();
	} else
	{
		ASSERT(0);
		return FALSE;
	}

	// 3. install and store filename
	return SCInstallFontFile(strFilename, TRUE);
}

///
/// Install a TrueType read-only embedded font from a file
///
BOOL SCEMFDoc::SCInstallFontFile(CString strFilename, BOOL bTemporary)
{
	CString strFontRes;
	int iPos = strFilename.ReverseFind(_T('.'));
	if (iPos>0)
	{
		strFontRes = strFilename.Left(iPos) + SC_EMFX_TMPFRES_EXT;
	} else
		strFontRes = strFilename + SC_EMFX_TMPFRES_EXT;
	
	DeleteFile(strFontRes);
	BOOL bCreated = CreateScalableFontResource(
		1,						// 0/1=flag for read-only embedded font
		LPCTSTR(strFontRes),	// pointer to filename for font resource
		LPCTSTR(strFilename),	// pointer to filename for scalable font
		LPCTSTR(NULL)    // pointer to path to font file
		);
	DWORD dwError = GetLastError();

	//ASSERT(bCreated); // Font file must be TrueType

	if (!bCreated)
	{
		if (bTemporary)
			DeleteFile(strFilename);
		return FALSE;
	}
	int iInstalled = AddFontResource(strFontRes);
	ASSERT(iInstalled);

	if (!iInstalled)
	{
		BOOL bOK = RemoveFontResource(strFontRes);
		ASSERT(bOK);
		if (bTemporary)
			DeleteFile(strFilename);
		return FALSE;
	}
	m_FontRes.AddTail(strFontRes);

	return TRUE;
}

void SCEMFDoc::SCNewDocument()
{
	SCBeginAddFiles(0);
	SCEndAddFiles();
}

UINT SCEMFDoc::SCOpenDocument(LPCTSTR lpszPathName)
{
	UINT uiFileType = SCEMFDoc::SCGetFileType(lpszPathName);
	switch(uiFileType & SC_FTYPE_MASK)
	{
	case SC_FTYPE_UKN :
		return uiFileType;

	case SC_FTYPE_BGP :
		SCLoadMaster(lpszPathName);
		break;
		
	case SC_FTYPE_EMF:
	case SC_FTYPE_WMF:
	case SC_FTYPE_EMZ:
	case SC_FTYPE_WMZ:
	case SC_FTYPE_IMG:
		SCLoadSinglePage(lpszPathName, uiFileType);
		break;

	case SC_FTYPE_TXT:
		if (!SCLoadRTFPages(lpszPathName, FALSE,
			(SC_SUBTYPE_TXT_RTF==(uiFileType & SC_SUBTYPE_MASK))))
			return SC_FTYPE_UKN;
		break;

	default:
		ASSERT(0);
	}

	return uiFileType;
}

BOOL SCEMFDoc::SCInsertFile(LPCTSTR lpszPathName, UINT* puiType/*=NULL*/)
{
	UINT uiFileType = SCEMFDoc::SCGetFileType(lpszPathName);
	if (puiType)
		*puiType = uiFileType;
	BOOL bInserted = FALSE;
	switch(uiFileType & SC_FTYPE_MASK)
	{
	case SC_FTYPE_UKN :
		return FALSE;

	case SC_FTYPE_BGP :
		// Merge isn't supported in this version
		//return SCLoadMaster(lpszPathName, TRUE);
		return FALSE;
		
	case SC_FTYPE_EMF:
	case SC_FTYPE_WMF:
	case SC_FTYPE_EMZ:
	case SC_FTYPE_WMZ:
	case SC_FTYPE_IMG:
		bInserted = SCLoadSinglePage(lpszPathName, uiFileType, TRUE);
		break;

	case SC_FTYPE_TXT:
		bInserted = SCLoadRTFPages(lpszPathName, TRUE,
			(SC_SUBTYPE_TXT_RTF==(uiFileType & SC_SUBTYPE_MASK)));
		break;

	default:
		ASSERT(0);
	}

	if (bInserted)
		m_bDocModified = TRUE;

	return bInserted;
}

///
/// Load the main data format supported by this class (enlist pages)
///
BOOL SCEMFDoc::SCLoadMaster(LPCTSTR lpszPathName, BOOL bInsert/*=FALSE*/)
{
	ASSERT(lpszPathName);

	ASSERT(FALSE==bInsert);// not supported yet

	// Note: in this version, absolute file names are not supported even if they appear
	// in the master. So directory information is stripped out.
	TCHAR szDocDrive[_MAX_DRIVE];	// drive of the playlist
	TCHAR szDocDir[_MAX_DIR];		// dir of the playlist
	TCHAR szFname[_MAX_FNAME];	// name of a file
	TCHAR szExt[_MAX_EXT];		// ext of a file
	_tsplitpath(lpszPathName, szDocDrive, szDocDir, NULL, NULL);
	
	TCHAR szBuff[MAX_PATH];
	// Number of files
	::GetPrivateProfileString(SC_DOC_GEN_SECTION, SC_DOC_NBFILES_KEY, _T(""),
		szBuff, MAX_PATH, lpszPathName);
	TCHAR szTxtBuff[SC_PGCOMMENT_MAXSIZE];
	
	UINT uiNbFiles = _ttoi(szBuff);
	SCBeginAddFiles(uiNbFiles, lpszPathName);
	for (UINT nFile=0; (nFile<uiNbFiles); nFile++)
	{
		CString strKey;
		strKey.Format(SC_DOC_FILEKEY_FORMAT, nFile);
		::GetPrivateProfileString(SC_DOC_FILES_SECTION, strKey, _T(""),
			szBuff, MAX_PATH, lpszPathName);
		
		_tsplitpath(szBuff, NULL, NULL, szFname, szExt);
		strKey.Format(_T("%s%s%s%s"), szDocDrive, szDocDir, szFname, szExt);
		SCAddFile(LPCTSTR(strKey), nFile, TRUE, (nFile>0));
		
		// Page attributes
		CString strPgSection;
		
		strPgSection.Format(SC_DOC_PGSECTION_FORMAT, nFile);
		::GetPrivateProfileString(strPgSection, SC_DOC_URL_KEY, _T(""),
			szTxtBuff, SC_PGCOMMENT_MAXSIZE, lpszPathName);
		SCSetPageURL(nFile, szTxtBuff);
		
		::GetPrivateProfileString(strPgSection, SC_DOC_CREDIT_KEY, _T(""),
			szTxtBuff, SC_PGCOMMENT_MAXSIZE, lpszPathName);
		SCSetPageCredit(nFile, szTxtBuff);
		
		::GetPrivateProfileString(strPgSection, SC_DOC_COMMENT_KEY, _T(""),
			szTxtBuff, SC_PGCOMMENT_MAXSIZE, lpszPathName);
		SCSetPageComment(nFile, szTxtBuff);
	}
	SCEndAddFiles();

	SCLoadSettings();
	return TRUE;
}

// Load a single file (enlist one file)
BOOL SCEMFDoc::SCLoadSinglePage(LPCTSTR lpszFname, UINT uiFyleType, BOOL bInsert/*=FALSE*/)
{
	ASSERT(lpszFname);
	ASSERT(uiFyleType!=SC_FTYPE_UKN);
	if (bInsert)
	{
		UINT uiIdx = m_uiNbFiles;
		if (!SCGrowBank(1))
			return FALSE;
		SCAddFile(lpszFname, uiIdx, TRUE, TRUE);
		m_vDocPages[uiIdx]->m_uiType = uiFyleType;
	} else
	{
		SCBeginAddFiles(1);
		SCAddFile(lpszFname, 0, TRUE, TRUE);
		m_vDocPages[0]->m_uiType = uiFyleType;
		SCEndAddFiles();
	}
	return TRUE;
}

///
/// Save the main data format supported by this class
///
BOOL SCEMFDoc::SCSaveDocument(LPCTSTR lpszPathName/*==NULL*/)
{
	CString strPathName = (lpszPathName) ? lpszPathName : m_strUniDocName;

	ASSERT(!strPathName.IsEmpty());
	CString strDocDir = SCMakeupDocDir(strPathName);

	CString strOldDocDir = SCMakeupDocDir(m_strUniDocName);
	BOOL bCopyFiles = (strDocDir!=strOldDocDir);

	// Main section
		// erase sections
	::WritePrivateProfileString(SC_DOC_GEN_SECTION, NULL, NULL, strPathName);
	::WritePrivateProfileString(SC_DOC_FILES_SECTION, NULL, NULL, strPathName);
		// ID
	::WritePrivateProfileString(SC_DOC_GEN_SECTION, SC_DOC_FILEID_KEY, SC_DOC_FILEID_VALUE, strPathName);
		// NbFiles
	SCWritePrivateProfileInt(SC_DOC_GEN_SECTION, SC_DOC_NBFILES_KEY, m_uiNbFiles, strPathName);

	// Files
	BOOL bOneDirty = FALSE;
	for (UINT uiPage=0; (uiPage<m_uiNbFiles); uiPage++)
	{
		PSCEMFDocPage pPage = m_vDocPages[uiPage];
		ASSERT(pPage);
		ASSERT(pPage->m_strPath);
		CString strPagePath;
		{// Note: in this version, absolute paths are not supported. Strip them out
			strPagePath = SCFNameExtFromPath(pPage->m_strPath);
			ASSERT(!strPagePath.IsEmpty());
		}

		// File EMF
		if (pPage->m_bDirty)
		{
			ASSERT(pPage->m_hEMF);
			if (pPage->m_hEMF)
			{
				CString strSavePath = strDocDir + strPagePath;
				BOOL bExist = SCExistFile(strSavePath);
				{// Note: in this version, bDirty means "new page"
					// Ensure that the file is unique
					int iPos = strSavePath.ReverseFind(_T('.'));
					CString strPrefix;
					CString strExt;
					if (iPos!=-1)
					{
						strPrefix = strSavePath.Left(iPos); // exclude dot
						strExt = strSavePath.Right(strSavePath.GetLength()-iPos); // include dot
					} else
					{
						ASSERT(0);
						strPrefix = strSavePath;
					}

					for (UINT i=1; (bExist && i<65635); i++)
					{
						strSavePath.Format(_T("%s_%d%s"), strPrefix, i, strExt);
						bExist = SCExistFile(strSavePath);
					}
					strPagePath = SCFNameExtFromPath(strSavePath);
					pPage->m_strPath = strPagePath;
				}
				if (SCWriteEMFtoDisk(pPage->m_hEMF, strSavePath))
					pPage->m_bDirty = FALSE;
			}
			if (pPage->m_bDirty)
			{
				bOneDirty = TRUE;
				continue;
			}
		} else
		if (bCopyFiles)
		{// copy file
			CString strSavePath = strDocDir + strPagePath;
			CString strOldPath = strOldDocDir + strPagePath;
			// Note: we are overwriting files!
			if (!::CopyFile(strOldPath, strSavePath, FALSE))
				bOneDirty = TRUE;
		}

		// File path
		CString strKey;
		strKey.Format(SC_DOC_FILEKEY_FORMAT, uiPage);
		::WritePrivateProfileString(SC_DOC_FILES_SECTION, strKey,
			strPagePath, strPathName);

		// Page attributes
		CString strPgSection;
		strPgSection.Format(SC_DOC_PGSECTION_FORMAT, uiPage);
		::WritePrivateProfileString(strPgSection, NULL,
							NULL, strPathName);

		if (!pPage->m_strCreditURL.IsEmpty())
			::WritePrivateProfileString(strPgSection, SC_DOC_URL_KEY,
							pPage->m_strCreditURL, strPathName);
		
		if (!pPage->m_strCredit.IsEmpty())
			::WritePrivateProfileString(strPgSection, SC_DOC_CREDIT_KEY,
							pPage->m_strCredit, strPathName);
		
		if (!pPage->m_strComment.IsEmpty())
			::WritePrivateProfileString(strPgSection, SC_DOC_COMMENT_KEY,
							pPage->m_strComment, strPathName);
	}

	// Settings
	SCSaveSettings(strPathName);

	// Is it clean?
	m_bDocModified = bOneDirty;
	m_bForgedName = FALSE;
	return (!m_bDocModified);
}

//////////////////////////////////////////////////////////////////////////////////
// Static

BOOL SCEMFDoc::SCIsNativeDoc(LPCTSTR lpszFname)
{
	TCHAR szBuff[MAX_PATH];
	::GetPrivateProfileString(SC_DOC_GEN_SECTION, SC_DOC_FILEID_KEY, _T(""), szBuff, MAX_PATH, lpszFname);
	CString strValue = szBuff;
	if (!strValue.IsEmpty() &&
		(strValue.CompareNoCase(SC_DOC_FILEID_VALUE)==0))
		return TRUE;
	
	return FALSE;
}

CString SCEMFDoc::SCDocFileFromList(CStringList& rLFiles)
{
	POSITION pos = rLFiles.GetHeadPosition();
	int iNumFiles = rLFiles.GetCount();
	if (1==iNumFiles)
	{// If one file, do not create playlist file
		return	rLFiles.GetNext(pos);
	}

	CString strDocName;
	WORD	nFound = 0;
    while (pos)
    { 
		CString strFile = rLFiles.GetNext(pos);
		if (SCEMFDoc::SCIsNativeDoc(strFile))
		{// playlist found in the group
			if (1==iNumFiles)
			{
				strDocName = strFile;
				break;
			}
			// dont mix: ignore playlist
			continue;
		}

		if (strDocName.IsEmpty())
		{// Build the doc name (new playlist) and write the number of files
			strDocName = SCEMFDoc::SCDocNameFromFilename(strFile);
			if (strDocName.IsEmpty())
				break; // could not create temp file

			// erase sections
			::WritePrivateProfileString(SC_DOC_GEN_SECTION, NULL, NULL, strDocName);
			::WritePrivateProfileString(SC_DOC_FILES_SECTION, NULL, NULL, strDocName);

			// ID
			::WritePrivateProfileString(SC_DOC_GEN_SECTION, SC_DOC_FILEID_KEY, SC_DOC_FILEID_VALUE, strDocName);
		}

		// Store the file path
		{
			CString strKey;
			strKey.Format(SC_DOC_FILEKEY_FORMAT, nFound);
			::WritePrivateProfileString(SC_DOC_FILES_SECTION, strKey, strFile, strDocName);
			nFound++;
		}
    }
	if (!strDocName.IsEmpty())
	{// Open the document
		// Update nb files only if a new playlist was created
		if (nFound)
		{
			ASSERT(nFound<=iNumFiles);
			CString strValue;
			strValue.Format(_T("%d"), nFound);
			::WritePrivateProfileString(SC_DOC_GEN_SECTION, SC_DOC_NBFILES_KEY, strValue, strDocName);
		}
	}
	return strDocName;
}

UINT SCEMFDoc::SCGetFileType(LPCTSTR lpszFname, BOOL bShowError/*=TRUE*/)
{
	{// Check special headers (bgp)
		TCHAR szBuff[MAX_PATH];
		::GetPrivateProfileString(SC_DOC_GEN_SECTION, SC_DOC_FILEID_KEY, _T(""), szBuff, MAX_PATH, lpszFname);
		CString strValue = szBuff;
		if (!strValue.IsEmpty() &&
			(strValue.CompareNoCase(SC_DOC_FILEID_VALUE)==0))
		return SC_FTYPE_BGP;
	}

	UINT uiMode = SetErrorMode(SEM_FAILCRITICALERRORS);
	// Open the file for reading. 
    HANDLE hFile = CreateFile(lpszFname, GENERIC_READ, FILE_SHARE_READ, NULL,  
                        OPEN_EXISTING, 0, 0); 
    if (hFile == INVALID_HANDLE_VALUE)
	{
		SetErrorMode(uiMode);
#if 1
		// TODO: add string to resource, or don't use it
		if (bShowError)
		{
			DWORD dwError = GetLastError();
			CString strMsg;
			strMsg.Format(_T("Unable to open %s"), lpszFname);
			SCShowWinError(dwError, SC_PRODUCTNAME, strMsg);
		}
#endif
        return (SC_FSTATE_NOTFOUND|SC_FTYPE_UKN);
	}

	UINT uiType = SC_FTYPE_UKN;

	// Load file type
	ENHMETAHEADER hdrBuff;
	DWORD dwRead;
	BOOL bResult;

	// wmf header
	bResult = ReadFile(hFile, &hdrBuff, 22, &dwRead, NULL);
	if (bResult && (22==dwRead))
	{// file is long enough
		BYTE* pdwKey = (BYTE*)&hdrBuff;
		if (*((DWORD*)pdwKey)==WMFMETA_PLACEABLEKEY)
			uiType = SC_FILETYPE_WMF; // Aldus wmf
		else
		if (EMR_HEADER==*((DWORD*)pdwKey))
		{// check emf header
			pdwKey += 22;
			bResult = ReadFile(hFile, pdwKey, sizeof(ENHMETAHEADER) - 22, &dwRead, NULL) ;
			if (bResult && (sizeof(ENHMETAHEADER) - 22==dwRead))
			{// file is long enough
				if (ENHMETA_SIGNATURE==hdrBuff.dSignature)
				{
					uiType = SC_FILETYPE_EMFONLY;
				}
			}
		}
		if (SC_FTYPE_UKN==uiType)
		{// check Windows wmf
			METAHEADER *pWinWMFHdr = (METAHEADER*)&hdrBuff;
			if ((1==pWinWMFHdr->mtType || 2==pWinWMFHdr->mtType) &&
				(0x0100==pWinWMFHdr->mtVersion || 0x0300==pWinWMFHdr->mtVersion))
			{
				uiType = SC_FILETYPE_WMF;
			}
		}
		if (SC_FTYPE_UKN==uiType && SCIsGZCandidate((LPBYTE)&hdrBuff, 22))
		{// check compressed Windows emf/wmf
			if (SCUnzipGZFilePart(lpszFname, (LPBYTE)&hdrBuff, sizeof(ENHMETAHEADER)))
			{
				pdwKey = (BYTE*)&hdrBuff;
				if (WMFMETA_PLACEABLEKEY==*((DWORD*)pdwKey))
					uiType = SC_FTYPE_WMZ; // Aldus wmf
				else
				if (EMR_HEADER==*((DWORD*)pdwKey) &&
					ENHMETA_SIGNATURE==hdrBuff.dSignature)
				{
					uiType = SC_FTYPE_EMZ; // emf
				} else
				{
					METAHEADER *pWinWMFHdr = (METAHEADER*)&hdrBuff;
					if ((1==pWinWMFHdr->mtType || 2==pWinWMFHdr->mtType) &&
						(0x0100==pWinWMFHdr->mtVersion || 0x0300==pWinWMFHdr->mtVersion))
					{
						uiType = SC_FTYPE_WMZ; // Windows wmf
					}
				}
			}
		}
	}

	CloseHandle(hFile);
	SetErrorMode(uiMode);
	if (uiType != SC_FTYPE_UKN)
		return uiType;

	// images
#pragma message( __FILE__  "(1129): TODO: Review image filetype  detection")
	// we would like a detection by content, not by extension
	uiType = SCFileTypeFromExt(lpszFname, TRUE);

	return uiType;
}

CString SCEMFDoc::SCDocNameFromFilename(LPCTSTR lpszFname)
{
	if (!SCIsWriteableMedia(lpszFname))
	{// If we can't write on the media, use a temp file
		TCHAR szPath[MAX_PATH];
		TCHAR szBuff[MAX_PATH];
		DWORD dwLength = ::GetTempPath(MAX_PATH, szPath);
		if ((dwLength>0)
			&& (dwLength<MAX_PATH)
			&& ::GetTempFileName(szPath, _T("EMX"), 0, szBuff))
		{
			CString strDocName = szBuff;
			return strDocName;
		}
		return _T("");
	}

	CString strBaseName = lpszFname;
	int iPos = strBaseName.ReverseFind(_T('.'));
	if (iPos!=-1)
		strBaseName = strBaseName.Left(iPos);
	CString strDocName = strBaseName + SC_DOC_EXTENSION;
	// Ensure that the file is unique
	for (UINT i=1; (i<65635); i++)
	{
		if (!SCExistFile(strDocName))
			return strDocName;
		strDocName.Format(_T("%s%d%s"), strBaseName, i, SC_DOC_EXTENSION);
	}
	return _T("");
}


By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
France France
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions