|
|
/*
* rtfwrit2.cpp * * Description: * This file contains the embedded-object implementation of the RTF * writer for the RICHEDIT subsystem. * * Authors: * Original RichEdit 1.0 RTF converter: Anthony Francisco * Conversion to C++ and RichEdit 2.0: * * Copyright (c) 1995-1998, Microsoft Corporation. All rights reserved. */
#include "_common.h"
#include "_rtfwrit.h"
#include "_coleobj.h"
ASSERTDATA
// ************** V-GUYB: Add this for converting pictures to 2bpp during stream out.
#if defined(CONVERT2BPP)
#define PWDV1_BPP 2
typedef struct { BITMAPINFOHEADER bmih; RGBQUAD colors[4]; } BMI2BPP;
const BYTE ColorTable2bpp[] = { 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0x55, 0x00, 0xAA, 0xAA, 0xAA, 0x00, 0xFF, 0xFF, 0xFF, 0x00 }; #endif // CONVERT2BPP
// ************** V-GUYB: End of conversion stuff.
static const CHAR szHexDigits[] = "0123456789abcdef";
static const CHAR szLineBreak[] = "\r\n";
const BYTE ObjectKeyWordIndexes [] = { i_objw,i_objh,i_objscalex, i_objscaley, i_objcropl, i_objcropt, i_objcropr, i_objcropb } ;
const BYTE PictureKeyWordIndexes [] = { i_picw,i_pich,i_picscalex, i_picscaley, i_piccropl, i_piccropt, i_piccropr, i_piccropb } ;
// TODO join with rtfwrit.cpp
// Most control-word output is done with the following printf formats
static const CHAR * rgszCtrlWordFormat[] = { "\\%s", "\\%s%d", "{\\%s", "{\\*\\%s" };
static const WORD IndexROT[] = { i_wbitmap, i_wmetafile, i_dibitmap, i_objemb, i_objlink, i_objautlink };
TFI *CRTFConverter::_rgtfi = NULL; // @cmember Ptr to 1st font substitute record
INT CRTFConverter::_ctfi = 0; // @cmember Count of font substitute records
TCHAR *CRTFConverter::_pchFontSubInfo = NULL; // @cmember Font name info
// internal table to insert charset into _rgtfi under winNT
typedef struct { TCHAR* szLocaleName; BYTE bCharSet; } NTCSENTRY;
const NTCSENTRY mpszcs[] = { { TEXT("cyr"), 204 }, // all lower case so we don't have to waste time
{ TEXT("ce"), 238 }, // doing a tolower below - Exchange2 800
{ TEXT("greek"), 161 }, { NULL, 0 } // sentinel
};
#define cszcs ARRAY_SIZE(mpszcs)
/*
* Service RemoveAdditionalSpace (sz) * * Purpose: * Remove first and last space from the string * Only one space will remain between words * * Argument * sz characters string */ void RemoveAdditionalSpace(TCHAR *sz) { TRACEBEGIN(TRCSUBSYSRTFR, TRCSCOPEINTERN, "RemoveAdditionalSpace");
TCHAR *szSource = sz; TCHAR *szDestination = sz;
while(*szSource == TEXT(' ') || *szSource == TAB) { *szSource++; }
while(*szSource) { if(*szSource != TEXT(' ') && *szSource != TAB) { *szDestination++ = *szSource++; } else { *szDestination++ = TEXT(' '); szSource++;
while(*szSource == TEXT(' ') || *szSource == TAB) { *szSource++; } } } *szDestination = TEXT('\0'); }
/*
* CRTFConverter::FreeFontSubInfo(void) * * @mfunc release any allocated memory for font substitutions * * @rdesc void */ void CRTFConverter::FreeFontSubInfo() { FreePv(_pchFontSubInfo); FreePv(_rgtfi); _pchFontSubInfo = NULL; _rgtfi = NULL; }
/*
* CRTFConverter::ReadFontSubInfo(void) * * Purpose: * Read the table of Font Substitutes and parse out the tagged fonts * * Returns: * BOOL TRUE if OK */ void CRTFConverter::ReadFontSubInfo() { #ifndef NOFONTSUBINFO
CLock clock; int cchBuffer = 600; // Approximately the amount used by NT
int cch; static const TCHAR szFontSubSection[] = TEXT("FontSubstitutes"); TCHAR *pchTMax;
if(_ctfi) return;
AssertSz(!_rgtfi, "CRTFConverter::ReadFontSubInfo(): Who donated the rgtfi?");
_pchFontSubInfo = (TCHAR *)PvAlloc(cchBuffer * sizeof(TCHAR), GMEM_FIXED); if(!_pchFontSubInfo) { goto Cleanup; }
next_try: cch = GetProfileSection(szFontSubSection, _pchFontSubInfo, cchBuffer); if(cch >= cchBuffer - 2) // GetProfileSection() magic number 2
{ // didn't fit, double the buffer size
const INT cchT = cchBuffer * 2;
if(cchT < cchBuffer) // >32k
{ goto Cleanup; } cchBuffer = cchT; _pchFontSubInfo = (TCHAR *)PvReAlloc(_pchFontSubInfo, cchT * sizeof(TCHAR)); if(!_pchFontSubInfo) { goto Cleanup; } goto next_try; } else if(!cch) { *_pchFontSubInfo = 0; } else //Fits, now resize _pchFontSubInfo
{ _pchFontSubInfo = (WCHAR*) PvReAlloc(_pchFontSubInfo, (cch) * sizeof(WCHAR)); }
_ctfi = 12; // a preliminary guess
_rgtfi = (TFI *)PvAlloc(_ctfi * sizeof(TFI), GMEM_FIXED); if(!_rgtfi) { goto Cleanup; }
TFI *ptfi; TCHAR *pchT;
pchT = _pchFontSubInfo; pchTMax = _pchFontSubInfo + cch; ptfi = &_rgtfi[0];
TCHAR *szTaggedName; TCHAR *szNonTaggedName; BOOL fGotTaggedCharSet; BOOL fGotNonTaggedCharSet; BYTE bTaggedCharSet; BYTE bNonTaggedCharSet; PARSEFONTNAME iParseLeft; PARSEFONTNAME iParseRight;
// parse the entries
// we are interested in the following strings:
//
// <tagged font name> = <nontagged font name>
// (where <nontagged font name> = <tagged font name> - <tag>
// <font1 name>,<font1 charset> = <font2 name>
// <tagged font name> = <nontagged font name>,<nontagged font charset>
// (where <nontagged font charset> = <tag>)
// <font1 name>,<font1 charset> = <font2 name>,<font2 charset>
// (where <font1 charset> == <font2 charset>)
iParseLeft = iParseRight = PFN_SUCCESS;
while(pchT < pchTMax && iParseLeft != PFN_EOF && iParseRight != PFN_EOF) { fGotTaggedCharSet = FALSE; fGotNonTaggedCharSet = FALSE;
if((iParseLeft = ParseFontName(pchT, pchTMax, TEXT('='), &szTaggedName, bTaggedCharSet, fGotTaggedCharSet, &pchT)) == PFN_SUCCESS && (iParseRight = ParseFontName(pchT, pchTMax, TEXT('\0'), &szNonTaggedName, bNonTaggedCharSet, fGotNonTaggedCharSet, &pchT)) == PFN_SUCCESS) { Assert(szTaggedName && szNonTaggedName);
BYTE bCharSet;
if(!fGotTaggedCharSet) { if(!FontSubstitute(szTaggedName, szNonTaggedName, &bCharSet)) { continue; } } else { bCharSet = bTaggedCharSet; }
if(fGotNonTaggedCharSet && bCharSet != bNonTaggedCharSet) { continue; } // We have a legitimate tagged/nontagged pair, so save it.
ptfi->szTaggedName = szTaggedName; ptfi->szNormalName = szNonTaggedName; ptfi->bCharSet = bCharSet;
ptfi++;
if(DiffPtrs(ptfi, &_rgtfi[0]) >= (UINT)_ctfi) { // allocate some more
_rgtfi = (TFI *)PvReAlloc(_rgtfi, (_ctfi + cszcs) * sizeof(TFI)); if(!_rgtfi) { goto Cleanup; } ptfi = _rgtfi + _ctfi; _ctfi += cszcs; } } } _ctfi = DiffPtrs(ptfi, &_rgtfi[0]);
if (!_ctfi) { goto Cleanup; // to cleanup alloc'd memory
} return;
Cleanup: if(_pchFontSubInfo) { FreePv(_pchFontSubInfo); _pchFontSubInfo = NULL; } if(_rgtfi) { FreePv(_rgtfi); _rgtfi = NULL; } _ctfi = 0; return; #endif // NOFONTSUBINFO
}
/*
* CRTFConverter::ParseFontName(pchBuf, pchBufMax, pszName, bCharSet, fSetCharSet, ppchBufNew, chDelimiter) * * Purpose: * Parses from the input buffer, pchBuf, a string of the form: * {WS}*<font_name>{WS}*[,{WS}*<char_set>{WS}*] * and sets: * pszName = <font_name> * bCharSet = <char_set> * fSetCharSet = (bCharSet set by proc) ? TRUE : FALSE * ppchBufNew = pointer to point in pchBuf after parsed font name * * Returns: * BOOL TRUE if OK */ CRTFConverter::PARSEFONTNAME CRTFConverter::ParseFontName(TCHAR *pchBuf, //@parm IN: buffer
TCHAR *pchBufMax, //@parm IN: last char in buffer
TCHAR chDelimiter, //@parm IN: char which delimits font name
TCHAR **pszName, //@parm OUT: parsed font name
BYTE &bCharSet, //@parm OUT: parsed char set
BOOL &fSetCharSet, //@parm OUT: char set parsed?
TCHAR **ppchBufNew //@parm OUT: ptr to next font name in input buffer
) const { PARSEFONTNAME iRet = PFN_SUCCESS;
Assert(pchBuf); Assert(pchBufMax); Assert(pchBufMax >= pchBuf); Assert(pszName); Assert(ppchBufNew);
fSetCharSet = FALSE; *pszName = pchBuf; if(pchBuf > pchBufMax) { return PFN_EOF; }
while(*pchBuf && *pchBuf != TEXT(',') && *pchBuf != chDelimiter) { pchBuf++;
if(pchBuf > pchBufMax) { return PFN_EOF; } }
TCHAR chTemp = *pchBuf; *pchBuf = TEXT('\0'); RemoveAdditionalSpace(*pszName);
if(chTemp == TEXT(',')) { TCHAR *szCharSet = ++pchBuf;
while(*pchBuf && *pchBuf != chDelimiter) { pchBuf++;
if(pchBuf > pchBufMax) { return PFN_EOF; } }
chTemp = *pchBuf;
if(chTemp != chDelimiter) { goto UnexpectedChar; }
*pchBuf = TEXT('\0'); RemoveAdditionalSpace(szCharSet);
bCharSet = 0; while(*szCharSet >= TEXT('0') && *szCharSet <= TEXT('9')) { bCharSet *= 10; bCharSet += *szCharSet++ - TEXT('0'); }
fSetCharSet = TRUE; // iRet = PFN_SUCCESS; (done above)
} else if(chTemp == chDelimiter) { // fSetCharSet = FALSE; (done above)
// iRet = PFN_SUCCESS; (done above)
} else // chTemp == 0
{ UnexpectedChar: Assert(!chTemp); // fSetCharSet = FALSE; (done above)
iRet = PFN_FAIL; }
// we had to at least get a font name out of this
if(!**pszName) { iRet = PFN_FAIL; }
// advance past the delimiter (or NULL char if malformed buffer)
Assert(chTemp == chDelimiter || iRet != PFN_SUCCESS && chTemp == TEXT('\0')); pchBuf++; *ppchBufNew = pchBuf;
return iRet; }
/*
* CRTFConverter::FontSubstitute(szTaggedName, szNormalName, pbCharSet) * * Purpose: * Verify that szTaggedName is szNormalName plus char set tag * If yes than write corresponding charSet tp pbCharSet * * Arguments: * szTaggedName name with tag * szNormalName name without tag * pbcharSEt where to write char set * * Returns: * BOOL */ BOOL CRTFConverter::FontSubstitute(TCHAR *szTaggedName, TCHAR *szNormalName, BYTE *pbCharSet) { const NTCSENTRY *pszcs = mpszcs;
Assert(szTaggedName); Assert(szNormalName); Assert(pbCharSet); Assert(*szTaggedName); // ensure same name, except for prefix
while(*szNormalName == *szTaggedName) { *szNormalName++; *szTaggedName++; } // verify that we have reached the end of szNormalName
while(*szNormalName) { if(*szNormalName != TEXT(' ') && *szNormalName != TAB) { return FALSE; }
szNormalName++; }
szTaggedName++;
while(pszcs->bCharSet) { if(!lstrcmpi(szTaggedName, pszcs->szLocaleName)) { *pbCharSet=pszcs->bCharSet; return TRUE; } pszcs++; }
#if defined(DEBUG) && !defined(PEGASUS)
char szBuf[MAX_PATH]; char szTag[256]; WideCharToMultiByte(CP_ACP, 0, szTaggedName, -1, szTag, sizeof(szTag), NULL, NULL);
sprintf(szBuf, "CRTFConverter::FontSubstitute(): Unrecognized tag found at" " end of tagged font name - \"%s\" (Raid this asap)", szTag); TRACEWARNSZ(szBuf); #endif
return FALSE; }
/*
* CRTFConverter::FindTaggedFont(const char *szNormalName, BYTE bCharSet, char **ppchTaggedName) * * Purpose: * Find font name may be with additional special tag corresponding to szNormalName & bCharSet * * Arguments: * szNormalName font name in RTF * bCharSet RTF char set * ppchTaggedName where to write tagged name * * Returns: * BOOL TRUE if find */ BOOL CRTFConverter::FindTaggedFont(const TCHAR *szNormalName, BYTE bCharSet, TCHAR **ppchTaggedName) { int itfi;
if(!_rgtfi) return FALSE;
for(itfi = 0; itfi < _ctfi; itfi++) { if(_rgtfi[itfi].bCharSet == bCharSet && !lstrcmpi(szNormalName, _rgtfi[itfi].szNormalName)) { *ppchTaggedName = _rgtfi[itfi].szTaggedName; return TRUE; } }
return FALSE; }
/*
* CRTFConverter::IsTaggedFont(const char *szName, BYTE *pbCharSet, char **ppchNormalName) * * Purpose: * Figure out is szName font name with additional tag corresponding to pbCharSet * If no charset specified, still try to match and return the correct charset * * Arguments: * szNormalName font name in RTF * bCharSet RTF char set * ppchNormalName where to write normal name * * Returns: * BOOL TRUE if is */ BOOL CRTFConverter::IsTaggedFont(const TCHAR *szName, BYTE *pbCharSet, TCHAR **ppchNormalName) { int itfi;
if(!_rgtfi) return FALSE;
for(itfi = 0; itfi < _ctfi; itfi++) { if((*pbCharSet <= 1 || _rgtfi[itfi].bCharSet == *pbCharSet) && !lstrcmpi(szName, _rgtfi[itfi].szTaggedName)) { *pbCharSet = _rgtfi[itfi].bCharSet; *ppchNormalName = _rgtfi[itfi].szNormalName; return TRUE; } } return FALSE; }
/*
* CRTFWrite::WriteData(pbBuffer, cbBuffer) * * Purpose: * Write out object data. This must be called only after all * initial object header information has been written out. * * Arguments: * pbBuffer pointer to write buffer * cbBuffer number of bytes to write out * * Returns: * LONG number of bytes written out */ LONG CRTFWrite::WriteData(BYTE * pbBuffer, LONG cbBuffer) { TRACEBEGIN(TRCSUBSYSRTFW, TRCSCOPEINTERN, "CRTFWrite::WriteData");
LONG cb = 0; BYTE bT;
_fNeedDelimeter = FALSE; while(cb < cbBuffer ) { bT = *pbBuffer++; // Put out hex value of byte
PutChar(szHexDigits[bT >> 4]); // Store high nibble
PutChar(szHexDigits[bT & 15]); // Store low nibble
// Every 78 chars and at end of group, drop a line
if (!(++cb % 39) || (cb == cbBuffer)) Puts(szLineBreak, sizeof(szLineBreak) - 1); } return cb; }
/*
* CRTFWrite::WriteBinData(pbBuffer, cbBuffer) * * Purpose: * Write out object binary data. This must be called only after all * initial object header information has been written out. * * Arguments: * pbBuffer pointer to write buffer * cbBuffer number of bytes to write out * * Returns: * LONG number of bytes written out */ LONG CRTFWrite::WriteBinData(BYTE * pbBuffer, LONG cbBuffer) { TRACEBEGIN(TRCSUBSYSRTFW, TRCSCOPEINTERN, "CRTFWrite::WriteData");
LONG cb = 0; BYTE bT;
_fNeedDelimeter = FALSE; while(cb < cbBuffer ) { bT = *pbBuffer++; if (!PutChar(bT)) break; cb++; } return cb; }
/*
* CRTFWrite::WriteRtfObject(prtfObject, fPicture) * * Purpose: * Writes out an picture or object header's render information * * Arguments: * prtfObject The object header information * fPicture Is this a header for a picture or an object * * Returns: * EC The error code * * Comments: * Eventually use keywords from rtf input list rather than partially * creating them on the fly */ EC CRTFWrite::WriteRtfObject(RTFOBJECT & rtfObject, BOOL fPicture) { TRACEBEGIN(TRCSUBSYSRTFW, TRCSCOPEINTERN, "CRTFWrite::WriteRtfObject");
LONG i; LONG * pDim; const BYTE * pKeyWordIndex;
if(fPicture) { pKeyWordIndex = PictureKeyWordIndexes; pDim = &rtfObject.xExtPict; } else { pKeyWordIndex = ObjectKeyWordIndexes; pDim = &rtfObject.xExt;
}
//Extents
for(i = 2; i--; pDim++, pKeyWordIndex++) { if (*pDim ) PutCtrlWord(CWF_VAL, *pKeyWordIndex, (SHORT)*pDim); }
// Scaling
pDim = &rtfObject.xScale; for(i = 2; i--; pDim++, pKeyWordIndex++) { if (*pDim && *pDim != 100 ) PutCtrlWord(CWF_VAL, *pKeyWordIndex, (SHORT)*pDim); } // Cropping
pDim = &rtfObject.rectCrop.left; for(i = 4; i--; pDim++, pKeyWordIndex++) { if (*pDim ) PutCtrlWord(CWF_VAL, *pKeyWordIndex, (SHORT)*pDim); }
return _ecParseError; }
/*
* CRTFWrite::WritePicture(REOBJECT &reObject,RTFOBJECT & rtfObject) * * Purpose: * Writes out an picture's header as well as the object's data. * * Arguments: * reObject Information from GetObject * prtfObject The object header information * * Returns: * EC The error code * * Note: * *** Writes only metafiles *** */ EC CRTFWrite::WritePicture(REOBJECT &reObject,RTFOBJECT & rtfObject) { TRACEBEGIN(TRCSUBSYSRTFW, TRCSCOPEINTERN, "CRTFWrite::WritePicture");
_ecParseError = ecStreamOutObj;
// Start and write picture group
PutCtrlWord( CWF_GRP, i_pict );
// Write that this is metafile
PutCtrlWord( CWF_VAL, i_wmetafile, rtfObject.sPictureType );
// Write picture render details
WriteRtfObject( rtfObject, TRUE );
// Write goal sizes
if (rtfObject.xExtGoal ) PutCtrlWord ( CWF_VAL, i_picwgoal, rtfObject.xExtGoal );
if (rtfObject.yExtGoal ) PutCtrlWord (CWF_VAL, i_pichgoal, rtfObject.yExtGoal);
// Start picture data
Puts( szLineBreak, sizeof(szLineBreak) - 1 );
// Write out the data
if ((UINT) WriteData( rtfObject.pbResult, rtfObject.cbResult ) != rtfObject.cbResult) { goto CleanUp; }
_ecParseError = ecNoError;
CleanUp: PutChar( chEndGroup ); // End picture data
return _ecParseError; }
/*
* CRTFWrite::WriteDib(REOBJECT &reObject,RTFOBJECT & rtfObject) * * Purpose: * Writes out an DIB primarily for Win CE * * Arguments: * reObject Information from GetObject * prtfObject The object header information * * Returns: * EC The error code * * Note: * *** Writes only dibs *** */ EC CRTFWrite::WriteDib(REOBJECT &reObject,RTFOBJECT & rtfObject) { TRACEBEGIN(TRCSUBSYSRTFW, TRCSCOPEINTERN, "CRTFWrite::WritePicture");
LPBITMAPINFO pbmi = (LPBITMAPINFO) rtfObject.pbResult;
_ecParseError = ecStreamOutObj;
// ************** V-GUYB: Add this for converting pictures to 2bpp during stream out.
// Store the original values so we can restore them on exit.
LPBYTE pbResult = rtfObject.pbResult; ULONG cbResult = rtfObject.cbResult; HGLOBAL hMem2bpp = 0;
#if defined(CONVERT2BPP)
// Pictures must be saved as 2bpp if saving to PWord V1 format.
if((_dwFlags & SFF_PWD) && ((_dwFlags & SFF_RTFVAL) >> 16 == 0)) { if(pbmi->bmiHeader.biBitCount > PWDV1_BPP) { HWND hWnd; HDC hdc, hdcSrc, hdcDst; HBITMAP hdibSrc, hdibDst; LPBYTE pbDibSrc, pbDibDst; BMI2BPP bmi2bpp = {0}; int iOffset, nBytes;
// First get a dc with the source dib in it.
hWnd = GetDesktopWindow(); hdc = GetDC(hWnd); hdcSrc = CreateCompatibleDC(hdc);
// Using CreateDIBSection below ensures that the working dibs and dcs will get a
// bpp of the appropriate dib, not a bpp based on the bpp of the device display.
if((hdibSrc = CreateDIBSection(hdcSrc, pbmi, DIB_RGB_COLORS, (void**)&pbDibSrc, NULL, 0))) { SelectObject(hdcSrc, hdibSrc);
// Get an offset to the source bits.
iOffset = sizeof(BITMAPINFOHEADER) + (sizeof(RGBQUAD) * (1<<pbmi->bmiHeader.biBitCount)); memcpy(pbDibSrc, &rtfObject.pbResult[iOffset], rtfObject.cbResult - iOffset);
// Now, build up a BITMAPINFO appropriate for a 2bpp dib.
bmi2bpp.bmih = pbmi->bmiHeader; bmi2bpp.bmih.biBitCount = PWDV1_BPP;
// Add the 4 color color-table.
memcpy(bmi2bpp.colors, (RGBQUAD*)ColorTable2bpp, (1<<PWDV1_BPP) * sizeof(RGBQUAD));
// Now create the new dib.
hdcDst = CreateCompatibleDC(hdc);
if((hdibDst = CreateDIBSection(hdcDst, (BITMAPINFO*)&bmi2bpp, DIB_RGB_COLORS, (void**)&pbDibDst, NULL, 0))) { SelectObject(hdcDst, hdibDst);
// Blit the > 2bpp dib into the 2bpp dib and let the system do the color mapping.
BitBlt(hdcDst, 0, 0, bmi2bpp.bmih.biWidth, bmi2bpp.bmih.biHeight, hdcSrc, 0, 0, SRCCOPY);
// Calculate the new bytes per line for the 2bpp dib.
rtfObject.cBytesPerLine = (((bmi2bpp.bmih.biWidth * PWDV1_BPP) + 31) & ~31) / 8; // DWORD boundary.
// Get the new size of the 2bpp byte array.
nBytes = rtfObject.cBytesPerLine * bmi2bpp.bmih.biHeight;
// Get total size of 2bpp dib, (including header and 4 color color-table).
cbResult = sizeof(bmi2bpp) + nBytes;
// Don't change the input pbResult as that is the internal representation of
// the dib. This conversion to 2bpp is only for writing to the output file.
if((hMem2bpp = GlobalAlloc(GMEM_FIXED, cbResult))) { if((pbResult = (LPBYTE)GlobalLock(hMem2bpp))) { // Copy in the dib header.
memcpy(pbResult, &bmi2bpp.bmih, sizeof(BITMAPINFOHEADER));
// Copy in the 4 color color-table.
memcpy(&pbResult[sizeof(BITMAPINFOHEADER)], (RGBQUAD*)ColorTable2bpp, (1<<PWDV1_BPP) * sizeof(RGBQUAD));
// Now copy in the byte array.
memcpy(&pbResult[sizeof(bmi2bpp)], pbDibDst, nBytes);
_ecParseError = ecNoError; } }
DeleteObject(hdibDst); }
DeleteDC(hdcDst);
DeleteObject(hdibSrc); }
DeleteDC(hdcSrc); ReleaseDC(hWnd, hdc);
if(_ecParseError != ecNoError) { goto CleanUp; } } } #endif // CONVERT2BPP
// ************** V-GUYB: End of conversion stuff.
// Start and write picture group
PutCtrlWord( CWF_GRP, i_pict );
// Write that this is dib
PutCtrlWord( CWF_VAL, i_dibitmap,rtfObject.sPictureType );
// V-GUYB:
// rtfObject.*Scale is not updated as the user stretches the picture,
// so don't use those here. But rtfObject.*Ext has been set up in the
// calling routine to account for the current site dimensions.
PutCtrlWord( CWF_VAL, i_picscalex, (rtfObject.xExt * 100) / rtfObject.xExtGoal); PutCtrlWord( CWF_VAL, i_picscaley, (rtfObject.yExt * 100) / rtfObject.yExtGoal);
// Write picture render details
PutCtrlWord( CWF_VAL, i_picw, pbmi->bmiHeader.biWidth ); PutCtrlWord( CWF_VAL, i_pich, pbmi->bmiHeader.biHeight ); PutCtrlWord( CWF_VAL, i_picwgoal, rtfObject.xExtGoal ); PutCtrlWord( CWF_VAL, i_pichgoal, rtfObject.yExtGoal ); PutCtrlWord( CWF_VAL, i_wbmbitspixel, pbmi->bmiHeader.biBitCount ); PutCtrlWord( CWF_VAL, i_wbmplanes, pbmi->bmiHeader.biPlanes ); PutCtrlWord( CWF_VAL, i_wbmwidthbytes, rtfObject.cBytesPerLine );
// Write out the data
PutCtrlWord( CWF_VAL, i_bin, cbResult ); if ((UINT) WriteBinData( pbResult, cbResult ) != cbResult) { // This "recovery" action needs to be rethought. There is no way
// the reader will be able to get back in synch.
goto CleanUp; }
_ecParseError = ecNoError;
CleanUp:
// Did we lock or allocate some temporary space for a 2bpp dib?
if(rtfObject.pbResult != pbResult) { // Yes, so unlock it now.
GlobalUnlock(pbResult); }
if(hMem2bpp) { GlobalFree(hMem2bpp); }
// Restore original values.
rtfObject.pbResult = pbResult; rtfObject.cbResult = cbResult;
PutChar(chEndGroup); // End picture data
return _ecParseError; }
/*
* CRTFWrite::WriteObject(LONG cp) * * Purpose: * Writes out an object's header as well as the object's data. * * Arguments: * cp The object position * * Returns: * EC The error code */ EC CRTFWrite::WriteObject(LONG cp, COleObject *pobj) { TRACEBEGIN(TRCSUBSYSRTFW, TRCSCOPEINTERN, "CRTFWrite::WriteObject");
RTFOBJECT rtfObject; REOBJECT reObject = { 0} ;
Assert(pobj);
reObject.cbStruct = sizeof (REOBJECT) ; reObject.cp = cp;
if (pobj->GetObjectData(&reObject, REO_GETOBJ_POLESITE | REO_GETOBJ_PSTG | REO_GETOBJ_POLEOBJ)) // todo fix Release
{ TRACEERRORSZ("Error geting object "); }
GetRtfObject(reObject, rtfObject);
HGLOBAL hdata = pobj->GetHdata(); if (hdata) { COleObject::ImageInfo *pimageinfo = pobj->GetImageInfo(); rtfObject.pbResult = (LPBYTE) GlobalLock( hdata ); rtfObject.cbResult = GlobalSize( hdata ); rtfObject.sType = ROT_DIB; rtfObject.xExt = (SHORT) TwipsFromHimetric( reObject.sizel.cx ); rtfObject.yExt = (SHORT) TwipsFromHimetric( reObject.sizel.cy ); rtfObject.xScale = pimageinfo->xScale; rtfObject.yScale = pimageinfo->yScale; rtfObject.xExtGoal = pimageinfo->xExtGoal; rtfObject.yExtGoal = pimageinfo->yExtGoal; rtfObject.cBytesPerLine = pimageinfo->cBytesPerLine; WriteDib( reObject, rtfObject ); GlobalUnlock( rtfObject.pbResult );
// Make sure to release otherwise the object won't go away
if (reObject.pstg) reObject.pstg->Release(); if (reObject.polesite) reObject.polesite->Release(); if (reObject.poleobj) reObject.poleobj->Release();
return _ecParseError; }
switch(rtfObject.sType) // Handle pictures in our own
{ // special way
case ROT_Embedded: case ROT_Link: case ROT_AutoLink: break;
case ROT_Metafile: case ROT_DIB: case ROT_Bitmap: WritePicture( reObject, rtfObject ); goto CleanUpNoEndGroup;
#ifdef DEBUG
default: AssertSz(FALSE, "CRTFW::WriteObject: Unknown ROT"); break; #endif DEBUG
}
// Start and write object group
PutCtrlWord( CWF_GRP, i_object ); PutCtrlWord( CWF_STR, IndexROT[rtfObject.sType] ); // PutCtrlWord(CWF_STR, i_objupdate); // TODO may be it needs more smart decision
if (rtfObject.szClass) // Write object class
{ PutCtrlWord( CWF_AST, i_objclass ); WritePcData( rtfObject.szClass ); PutChar( chEndGroup ); }
if (rtfObject.szName) // Write object name
{ PutCtrlWord( CWF_AST, i_objname ); WritePcData( rtfObject.szName ); PutChar( chEndGroup ); }
if (rtfObject.fSetSize) // Write object sizing
{ // options
PutCtrlWord( CWF_STR, i_objsetsize ); }
WriteRtfObject( rtfObject, FALSE ) ; // Write object render info
PutCtrlWord( CWF_AST, i_objdata ) ; // info, start object
Puts( szLineBreak, sizeof(szLineBreak) - 1); // data group
if (!ObjectWriteToEditstream( reObject, rtfObject )) { TRACEERRORSZ("Error writing object data"); if (!_ecParseError) _ecParseError = ecStreamOutObj; PutChar( chEndGroup ); // End object data
goto CleanUp; }
PutChar( chEndGroup ); // End object data
PutCtrlWord( CWF_GRP, i_result ); // Start results group
WritePicture( reObject,rtfObject ); // Write results group
PutChar( chEndGroup ); // End results group
CleanUp: PutChar( chEndGroup ); // End object group
CleanUpNoEndGroup: if (reObject.pstg) reObject.pstg->Release(); if (reObject.polesite) reObject.polesite->Release(); if (reObject.poleobj) reObject.poleobj->Release(); if (rtfObject.pbResult) { HGLOBAL hmem;
hmem = GlobalHandle( rtfObject.pbResult ); GlobalUnlock( hmem ); GlobalFree( hmem ); } if (rtfObject.szClass) { CoTaskMemFree( rtfObject.szClass ); }
return _ecParseError; }
/*
* GetRtfObjectMetafilePict * * @mfunc * Gets information about an metafile into a structure. * * Arguments: * HGLOBAL The object data * RTFOBJECT Where to put the information. * * @rdesc * BOOL TRUE on success, FALSE if object cannot be written to RTF. */ BOOL CRTFWrite::GetRtfObjectMetafilePict(HGLOBAL hmfp, RTFOBJECT &rtfobject, SIZEL &sizelGoal) { #ifndef NOMETAFILES
BOOL fSuccess = FALSE; LPMETAFILEPICT pmfp = (LPMETAFILEPICT)GlobalLock(hmfp); HGLOBAL hmem = NULL; ULONG cb;
if (!pmfp) goto Cleanup;
// Build the header
rtfobject.sPictureType = (SHORT) pmfp->mm; rtfobject.xExtPict = (SHORT) pmfp->xExt; rtfobject.yExtPict = (SHORT) pmfp->yExt; rtfobject.xExtGoal = (SHORT) TwipsFromHimetric(sizelGoal.cx); rtfobject.yExtGoal = (SHORT) TwipsFromHimetric(sizelGoal.cy);
// Find out how much room we'll need
cb = GetMetaFileBitsEx(pmfp->hMF, 0, NULL); if (!cb) goto Cleanup;
// Allocate that space
hmem = GlobalAlloc(GHND, cb); if (!hmem) goto Cleanup;
rtfobject.pbResult = (LPBYTE)GlobalLock(hmem); if (!rtfobject.pbResult) { GlobalFree(hmem); goto Cleanup; }
// Get the data
rtfobject.cbResult = (ULONG) GetMetaFileBitsEx(pmfp->hMF, (UINT) cb, rtfobject.pbResult); if (rtfobject.cbResult != cb) { rtfobject.pbResult = NULL; GlobalFree(hmem); goto Cleanup; } fSuccess = TRUE;
Cleanup: GlobalUnlock(hmfp); return fSuccess; #else
return FALSE; #endif
}
/*
* GetRtfObject (REOBJECT &reobject, RTFOBJECT &rtfobject) * * Purpose: * Gets information about an RTF object into a structure. * * Arguments: * REOBJECT Information from GetObject * RTFOBJECT Where to put the information. Strings are read only and * are owned by the object subsystem, not the caller. * * Returns: * BOOL TRUE on success, FALSE if object cannot be written to RTF. */ BOOL CRTFWrite::GetRtfObject(REOBJECT &reobject, RTFOBJECT &rtfobject) { BOOL fSuccess = FALSE; BOOL fNoOleServer = FALSE; const BOOL fStatic = !!(reobject.dwFlags & REO_STATIC); SIZEL sizelObj = reobject.sizel; //COMPATIBILITY: RICHED10 code had a frame size. Do we need something similiar.
LPTSTR szProgId;
// Blank out the full structure
ZeroMemory(&rtfobject, sizeof(RTFOBJECT));
// If the object has no storage it cannot be written.
if (!reobject.pstg) return FALSE;
// If we don't have the progID for a real OLE object, get it now
if (!fStatic ) { rtfobject.szClass = NULL; // We need a ProgID to put into the RTF stream.
//$ REVIEW: MAC This call is incorrect for the Mac. It may not matter though
// if ole support in RichEdit is not needed for the Mac.
if (ProgIDFromCLSID(reobject.clsid, &szProgId)) fNoOleServer = TRUE; else rtfobject.szClass = szProgId; }
#ifndef NOMETAFILES
HGLOBAL hmfp = OleStdGetMetafilePictFromOleObject(reobject.poleobj, reobject.dvaspect, &sizelObj, NULL); if (hmfp) { LPMETAFILEPICT pmfp = NULL;
fSuccess = GetRtfObjectMetafilePict(hmfp, rtfobject, sizelObj); if (pmfp = (LPMETAFILEPICT)GlobalLock(hmfp)) { if (pmfp->hMF) DeleteMetaFile(pmfp->hMF); GlobalUnlock(hmfp); } GlobalFree(hmfp);
// If we don't have Server and we can't get metafile, forget it.
if (!fSuccess && fNoOleServer) return fSuccess; } #endif
if (!fStatic) { // Fill in specific fields
rtfobject.sType = fNoOleServer ? ROT_Metafile : ROT_Embedded; //$ FUTURE: set for links
rtfobject.xExt = (SHORT) TwipsFromHimetric(sizelObj.cx); rtfobject.yExt = (SHORT) TwipsFromHimetric(sizelObj.cy);
// fSuccess set even if we couldn't retreive a metafile
// because we don't need a metafile in the non-static case,
// it's just nice to have one
fSuccess = TRUE; } rtfobject.fSetSize = 0; //$ REVIEW: Hmmm
return fSuccess; }
/*
* ObjectWriteToEditstream * * Purpose: * Writes an OLE object data to the RTF output stream. * * Arguments: * REOBJECT Information from GetObject * RTFOBJECT Where to get icon data. * * Returns: * BOOL TRUE on success, FALSE on failure. */ BOOL CRTFWrite::ObjectWriteToEditstream(REOBJECT &reObject, RTFOBJECT &rtfobject) { HRESULT hr = 0;
// Force the object to update its storage //// ????
// Not necessary. Already done in WriteRtf
// reObject.polesite->SaveObject();
// If the object is iconic we do some special magic
if (reObject.dvaspect == DVASPECT_ICON) { HANDLE hGlobal; STGMEDIUM med;
// Force the presentation to be the iconic view.
med.tymed = TYMED_HGLOBAL; hGlobal = GlobalHandle(rtfobject.pbResult); med.hGlobal = hGlobal; hr = OleConvertIStorageToOLESTREAMEx(reObject.pstg, CF_METAFILEPICT, rtfobject.xExtPict, rtfobject.yExtPict, rtfobject.cbResult, &med, (LPOLESTREAM) &RTFWriteOLEStream); } else { // Do the standard conversion
hr = OleConvertIStorageToOLESTREAM(reObject.pstg, (LPOLESTREAM) &RTFWriteOLEStream); } return SUCCEEDED(hr); }
|