Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1277 lines
33 KiB

  1. /*
  2. * rtfwrit2.cpp
  3. *
  4. * Description:
  5. * This file contains the embedded-object implementation of the RTF
  6. * writer for the RICHEDIT subsystem.
  7. *
  8. * Authors:
  9. * Original RichEdit 1.0 RTF converter: Anthony Francisco
  10. *
  11. * Copyright (c) 1995-2000, Microsoft Corporation. All rights reserved.
  12. */
  13. #include "_common.h"
  14. #include "_rtfwrit.h"
  15. #include "_coleobj.h"
  16. ASSERTDATA
  17. extern const CHAR szEndGroupCRLF[];
  18. #define WHITE RGB(255, 255, 255)
  19. // ************** V-GUYB: Add this for converting pictures to 2bpp during stream out.
  20. #if defined(CONVERT2BPP)
  21. #define PWDV1_BPP 2
  22. typedef struct
  23. {
  24. BITMAPINFOHEADER bmih;
  25. RGBQUAD colors[4];
  26. }
  27. BMI2BPP;
  28. const BYTE ColorTable2bpp[] =
  29. {
  30. 0x00, 0x00, 0x00, 0x00,
  31. 0x55, 0x55, 0x55, 0x00,
  32. 0xAA, 0xAA, 0xAA, 0x00,
  33. 0xFF, 0xFF, 0xFF, 0x00
  34. };
  35. #endif // CONVERT2BPP
  36. // ************** V-GUYB: End of conversion stuff.
  37. static const CHAR szShapeLeadIn[] = "{\\shp{\\*\\shpinst\r\n";
  38. static const CHAR szShapeFillBlip[] = "{\\sp{\\sn fillBlip}{\\sv ";
  39. static const CHAR szShapeParm[] = "{\\sp{\\sn %s}{\\sv %d}}\r\n";
  40. static const CHAR szHexDigits[] = "0123456789abcdef";
  41. static const CHAR szLineBreak[] = "\r\n";
  42. const BYTE ObjectKeyWordIndexes [] =
  43. {
  44. i_objw, i_objh, i_objscalex, i_objscaley, i_objcropl, i_objcropt, i_objcropr, i_objcropb
  45. } ;
  46. const BYTE PictureKeyWordIndexes [] =
  47. {
  48. i_picw, i_pich, i_picscalex, i_picscaley, i_piccropl, i_piccropt, i_piccropr, i_piccropb
  49. } ;
  50. // TODO join with rtfwrit.cpp
  51. // Most control-word output is done with the following printf formats
  52. static const CHAR * rgszCtrlWordFormat[] =
  53. {
  54. "\\%s", "\\%s%d", "{\\%s", "{\\*\\%s"
  55. };
  56. static const WORD IndexROT[] =
  57. {
  58. i_wbitmap,
  59. i_wmetafile,
  60. i_dibitmap,
  61. i_jpegblip,
  62. i_pngblip,
  63. i_objemb,
  64. i_objlink,
  65. i_objautlink
  66. };
  67. TFI *CRTFConverter::_rgtfi = NULL; // @cmember Ptr to 1st font substitute record
  68. INT CRTFConverter::_ctfi = 0; // @cmember Count of font substitute records
  69. WCHAR *CRTFConverter::_pchFontSubInfo = NULL; // @cmember Font name info
  70. // internal table to insert charset into _rgtfi under winNT
  71. typedef struct
  72. {
  73. WCHAR* szLocaleName;
  74. BYTE iCharRep;
  75. } NTCSENTRY;
  76. const NTCSENTRY mpszcs[] =
  77. {
  78. { TEXT("cyr"), 204 }, // All lower case so we don't have to waste time
  79. { TEXT("ce"), 238 }, // doing a tolower below - Exchange2 800
  80. { TEXT("greek"), 161 },
  81. { NULL, 0 } // sentinel
  82. };
  83. #define cszcs ARRAY_SIZE(mpszcs)
  84. /*
  85. * RemoveAdditionalSpace (sz)
  86. *
  87. * @func
  88. * Remove first and last space from the string
  89. * Only one space will remain between words
  90. */
  91. void RemoveAdditionalSpace(
  92. WCHAR *sz) //@parm Character string to remove space from
  93. {
  94. TRACEBEGIN(TRCSUBSYSRTFR, TRCSCOPEINTERN, "RemoveAdditionalSpace");
  95. WCHAR *szSource = sz;
  96. WCHAR *szDestination = sz;
  97. while(*szSource == TEXT(' ') || *szSource == TAB)
  98. *szSource++;
  99. while(*szSource)
  100. {
  101. if(*szSource != TEXT(' ') && *szSource != TAB)
  102. *szDestination++ = *szSource++;
  103. else
  104. {
  105. *szDestination++ = TEXT(' ');
  106. szSource++;
  107. while(*szSource == TEXT(' ') || *szSource == TAB)
  108. *szSource++;
  109. }
  110. }
  111. *szDestination = TEXT('\0');
  112. }
  113. /*
  114. * CRTFConverter::FreeFontSubInfo()
  115. *
  116. * @mfunc release any allocated memory for font substitutions
  117. */
  118. void CRTFConverter::FreeFontSubInfo()
  119. {
  120. FreePv(_pchFontSubInfo);
  121. FreePv(_rgtfi);
  122. _pchFontSubInfo = NULL;
  123. _rgtfi = NULL;
  124. }
  125. /*
  126. * CRTFConverter::ReadFontSubInfo()
  127. *
  128. * @mfunc
  129. * Read the table of Font Substitutes and parse out the tagged fonts
  130. *
  131. * @rdesc
  132. * BOOL TRUE if OK
  133. */
  134. void CRTFConverter::ReadFontSubInfo()
  135. {
  136. #ifndef NOFONTSUBINFO
  137. CLock clock;
  138. int cchBuffer = 600; // Approximately the amount used by NT
  139. int cch;
  140. static const WCHAR szFontSubSection[] = TEXT("FontSubstitutes");
  141. WCHAR *pchTMax;
  142. if(_ctfi)
  143. return;
  144. AssertSz(!_rgtfi, "CRTFConverter::ReadFontSubInfo(): Who donated the rgtfi?");
  145. _pchFontSubInfo = (WCHAR *)PvAlloc(cchBuffer * sizeof(WCHAR), GMEM_FIXED);
  146. if(!_pchFontSubInfo)
  147. goto Cleanup;
  148. next_try:
  149. cch = GetProfileSection(szFontSubSection, _pchFontSubInfo, cchBuffer);
  150. if(cch >= cchBuffer - 2) // GetProfileSection() magic number 2
  151. {
  152. const INT cchT = cchBuffer * 2; // Didn't fit, double the buffer size
  153. if(cchT < cchBuffer) // >32k
  154. goto Cleanup;
  155. cchBuffer = cchT;
  156. _pchFontSubInfo = (WCHAR *)PvReAlloc(_pchFontSubInfo, cchT * sizeof(WCHAR));
  157. if(!_pchFontSubInfo)
  158. goto Cleanup;
  159. goto next_try;
  160. }
  161. else if(!cch)
  162. *_pchFontSubInfo = 0;
  163. else // Fits, now resize _pchFontSubInfo
  164. _pchFontSubInfo = (WCHAR*) PvReAlloc(_pchFontSubInfo, (cch) * sizeof(WCHAR));
  165. _ctfi = 12; // A preliminary guess
  166. _rgtfi = (TFI *)PvAlloc(_ctfi * sizeof(TFI), GMEM_FIXED);
  167. if(_rgtfi)
  168. {
  169. TFI * ptfi = &_rgtfi[0];;
  170. WCHAR * pchT = _pchFontSubInfo;
  171. pchTMax = _pchFontSubInfo + cch;
  172. WCHAR * szTaggedName;
  173. WCHAR * szNonTaggedName;
  174. BOOL fGotTaggedCharSet;
  175. BOOL fGotNonTaggedCharSet;
  176. BYTE iCharRepTagged;
  177. BYTE iCharRepNonTagged;
  178. PARSEFONTNAME iParseLeft;
  179. PARSEFONTNAME iParseRight;
  180. // Parse the entries. We are interested in the following strings:
  181. //
  182. // <tagged font name> = <nontagged font name>
  183. // (where <nontagged font name> = <tagged font name> - <tag>
  184. // <font1 name>,<font1 charset> = <font2 name>
  185. // <tagged font name> = <nontagged font name>,<nontagged font charset>
  186. // (where <nontagged font charset> = <tag>)
  187. // <font1 name>,<font1 charset> = <font2 name>,<font2 charset>
  188. // (where <font1 charset> == <font2 charset>)
  189. iParseLeft = iParseRight = PFN_SUCCESS;
  190. while(pchT < pchTMax && iParseLeft != PFN_EOF
  191. && iParseRight != PFN_EOF)
  192. {
  193. fGotTaggedCharSet = FALSE;
  194. fGotNonTaggedCharSet = FALSE;
  195. if((iParseLeft = ParseFontName(pchT,
  196. pchTMax,
  197. TEXT('='),
  198. &szTaggedName,
  199. iCharRepTagged,
  200. fGotTaggedCharSet,
  201. &pchT)) == PFN_SUCCESS &&
  202. (iParseRight = ParseFontName(pchT,
  203. pchTMax,
  204. TEXT('\0'),
  205. &szNonTaggedName,
  206. iCharRepNonTagged,
  207. fGotNonTaggedCharSet,
  208. &pchT)) == PFN_SUCCESS)
  209. {
  210. Assert(szTaggedName && szNonTaggedName);
  211. BYTE iCharRep;
  212. if(!fGotTaggedCharSet)
  213. {
  214. if(!FontSubstitute(szTaggedName, szNonTaggedName, &iCharRep))
  215. continue;
  216. }
  217. else
  218. iCharRep = iCharRepTagged;
  219. if(fGotNonTaggedCharSet && iCharRep != iCharRepNonTagged)
  220. continue;
  221. // We have a legitimate tagged/nontagged pair, so save it.
  222. ptfi->szTaggedName = szTaggedName;
  223. ptfi->szNormalName = szNonTaggedName;
  224. ptfi->iCharRep = iCharRep;
  225. ptfi++;
  226. if(DiffPtrs(ptfi, &_rgtfi[0]) >= (UINT)_ctfi)
  227. {
  228. // allocate some more
  229. _rgtfi = (TFI *)PvReAlloc(_rgtfi, (_ctfi + cszcs) * sizeof(TFI));
  230. if(!_rgtfi)
  231. goto Cleanup;
  232. ptfi = _rgtfi + _ctfi;
  233. _ctfi += cszcs;
  234. }
  235. }
  236. }
  237. _ctfi = DiffPtrs(ptfi, &_rgtfi[0]);
  238. if(_ctfi)
  239. return;
  240. }
  241. Cleanup:
  242. if(_pchFontSubInfo)
  243. {
  244. FreePv(_pchFontSubInfo);
  245. _pchFontSubInfo = NULL;
  246. }
  247. if(_rgtfi)
  248. {
  249. FreePv(_rgtfi);
  250. _rgtfi = NULL;
  251. }
  252. _ctfi = 0;
  253. return;
  254. #endif // NOFONTSUBINFO
  255. }
  256. /*
  257. * CRTFConverter::ParseFontName(pchBuf, pchBufMax, chDelimiter, pszName,
  258. * &iCharRep, &fSetCharSet, ppchBufNew)
  259. *
  260. * @mfunc
  261. * Parses from the input buffer, pchBuf, a string of the form:
  262. * {WS}*<font_name>{WS}*[,{WS}*<char_set>{WS}*]
  263. * and sets:
  264. * pszName = <font_name>
  265. * bCharSet = <char_set>
  266. * fSetCharSet = (bCharSet set by proc) ? TRUE : FALSE
  267. * ppchBufNew = pointer to point in pchBuf after parsed font name
  268. *
  269. * @rdesc
  270. * BOOL TRUE if OK
  271. */
  272. CRTFConverter::PARSEFONTNAME CRTFConverter::ParseFontName(
  273. WCHAR * pchBuf, //@parm IN: buffer
  274. WCHAR * pchBufMax, //@parm IN: last char in buffer
  275. WCHAR chDelimiter, //@parm IN: char which delimits font name
  276. WCHAR **pszName, //@parm OUT: parsed font name
  277. BYTE & iCharRep, //@parm OUT: parsed font character repertoire
  278. BOOL & fSetCharSet, //@parm OUT: char set parsed?
  279. WCHAR **ppchBufNew) const //@parm OUT: ptr to next font name in input buffer
  280. {
  281. PARSEFONTNAME iRet = PFN_SUCCESS;
  282. Assert(pchBuf && pchBufMax >= pchBuf && pszName && ppchBufNew);
  283. fSetCharSet = FALSE;
  284. *pszName = pchBuf;
  285. if(pchBuf > pchBufMax)
  286. return PFN_EOF;
  287. while(*pchBuf && *pchBuf != TEXT(',') && *pchBuf != chDelimiter)
  288. {
  289. pchBuf++;
  290. if(pchBuf > pchBufMax)
  291. return PFN_EOF;
  292. }
  293. WCHAR chTemp = *pchBuf;
  294. *pchBuf = TEXT('\0');
  295. RemoveAdditionalSpace(*pszName);
  296. if(chTemp == TEXT(','))
  297. {
  298. WCHAR *szCharSet = ++pchBuf;
  299. while(*pchBuf && *pchBuf != chDelimiter)
  300. {
  301. pchBuf++;
  302. if(pchBuf > pchBufMax)
  303. return PFN_EOF;
  304. }
  305. chTemp = *pchBuf;
  306. if(chTemp != chDelimiter)
  307. goto UnexpectedChar;
  308. *pchBuf = TEXT('\0');
  309. RemoveAdditionalSpace(szCharSet);
  310. BYTE bCharSet = 0;
  311. while(*szCharSet >= TEXT('0') && *szCharSet <= TEXT('9'))
  312. {
  313. bCharSet *= 10;
  314. bCharSet += *szCharSet++ - TEXT('0');
  315. }
  316. iCharRep = CharRepFromCharSet(bCharSet);
  317. fSetCharSet = TRUE;
  318. // iRet = PFN_SUCCESS; (done above)
  319. }
  320. else if(chTemp == chDelimiter)
  321. {
  322. // fSetCharSet = FALSE; (done above)
  323. // iRet = PFN_SUCCESS; (done above)
  324. }
  325. else // chTemp == 0
  326. {
  327. UnexpectedChar:
  328. Assert(!chTemp);
  329. // fSetCharSet = FALSE; (done above)
  330. iRet = PFN_FAIL;
  331. }
  332. // We had to at least get a font name out of this
  333. if(!**pszName)
  334. iRet = PFN_FAIL;
  335. // Advance past the delimiter (or NULL char if malformed buffer)
  336. Assert(chTemp == chDelimiter || iRet != PFN_SUCCESS && chTemp == TEXT('\0'));
  337. pchBuf++;
  338. *ppchBufNew = pchBuf;
  339. return iRet;
  340. }
  341. /*
  342. * CRTFConverter::FontSubstitute(szTaggedName, szNormalName, pbCharSet)
  343. *
  344. * @mfunc
  345. * Verify that szTaggedName is szNormalName plus char set tag
  346. * If yes than write corresponding charSet tp pbCharSet
  347. *
  348. * @rdesc
  349. * BOOL
  350. */
  351. BOOL CRTFConverter::FontSubstitute(
  352. WCHAR *szTaggedName, //@parm Name with tag
  353. WCHAR *szNormalName, //@parm Name without tag
  354. BYTE * piCharRep) //@parm Where to write charset
  355. {
  356. const NTCSENTRY *pszcs = mpszcs;
  357. Assert(szTaggedName && szNormalName && piCharRep && *szTaggedName);
  358. // Ensure same name, except for prefix
  359. while(*szNormalName == *szTaggedName)
  360. {
  361. *szNormalName++;
  362. *szTaggedName++;
  363. }
  364. // Verify that we have reached the end of szNormalName
  365. while(*szNormalName)
  366. {
  367. if(*szNormalName != TEXT(' ') && *szNormalName != TAB)
  368. return FALSE;
  369. szNormalName++;
  370. }
  371. szTaggedName++;
  372. while(pszcs->iCharRep)
  373. {
  374. if(!lstrcmpi(szTaggedName, pszcs->szLocaleName))
  375. {
  376. *piCharRep = pszcs->iCharRep;
  377. return TRUE;
  378. }
  379. pszcs++;
  380. }
  381. #if defined(DEBUG) && !defined(NOFULLDEBUG)
  382. char szBuf[MAX_PATH];
  383. char szTag[256];
  384. WideCharToMultiByte(CP_ACP, 0, szTaggedName, -1, szTag, sizeof(szTag),
  385. NULL, NULL);
  386. sprintf(szBuf, "CRTFConverter::FontSubstitute(): Unrecognized tag found at"
  387. " end of tagged font name - \"%s\" (Raid this asap)", szTag);
  388. TRACEWARNSZ(szBuf);
  389. #endif
  390. return FALSE;
  391. }
  392. /*
  393. * CRTFConverter::FindTaggedFont(*szNormalName, bCharSet, ppchTaggedName)
  394. *
  395. * @mfunc
  396. * Find font name may be with additional special tag corresponding to
  397. * szNormalName & bCharSet
  398. *
  399. * @rdesc
  400. * BOOL TRUE if find
  401. */
  402. BOOL CRTFConverter::FindTaggedFont(
  403. const WCHAR *szNormalName, //@parm Font name in RTF
  404. BYTE iCharRep, //@parm RTF charset
  405. WCHAR ** ppchTaggedName) //@parm Where to write tagged name
  406. {
  407. if(!_rgtfi)
  408. return FALSE;
  409. for(int itfi = 0; itfi < _ctfi; itfi++)
  410. {
  411. if(_rgtfi[itfi].iCharRep == iCharRep &&
  412. !lstrcmpi(szNormalName, _rgtfi[itfi].szNormalName))
  413. {
  414. *ppchTaggedName = _rgtfi[itfi].szTaggedName;
  415. return TRUE;
  416. }
  417. }
  418. return FALSE;
  419. }
  420. /*
  421. * CRTFConverter::IsTaggedFont(szName, piCharRep, ppchNormalName)
  422. *
  423. * @mfunc
  424. * Figure out is szName font name with additional tag corresponding to pbCharSet
  425. * If no charset specified, still try to match and return the correct charset
  426. *
  427. * @rdesc
  428. * BOOL TRUE if is
  429. */
  430. BOOL CRTFConverter::IsTaggedFont(
  431. const WCHAR *szName, //@parm Font name in RTF
  432. BYTE * piCharRep, //@parm RTF charset
  433. WCHAR ** ppchNormalName) //@parm Where to write normal name
  434. {
  435. if(!_rgtfi)
  436. return FALSE;
  437. for(int itfi = 0; itfi < _ctfi; itfi++)
  438. {
  439. if((*piCharRep <= 1 || _rgtfi[itfi].iCharRep == *piCharRep) &&
  440. !lstrcmpi(szName, _rgtfi[itfi].szTaggedName))
  441. {
  442. *piCharRep = _rgtfi[itfi].iCharRep;
  443. *ppchNormalName = _rgtfi[itfi].szNormalName;
  444. return TRUE;
  445. }
  446. }
  447. return FALSE;
  448. }
  449. /*
  450. * CRTFWrite::WriteData(pbBuffer, cbBuffer)
  451. *
  452. * @mfunc
  453. * Write out object data. This must be called only after all
  454. * initial object header information has been written out.
  455. *
  456. * @rdesc
  457. * LONG count of bytes written out
  458. */
  459. LONG CRTFWrite::WriteData(
  460. BYTE * pbBuffer, //@parm Point to write buffer
  461. LONG cbBuffer) //@parm Count of bytes to write out
  462. {
  463. TRACEBEGIN(TRCSUBSYSRTFW, TRCSCOPEINTERN, "CRTFWrite::WriteData");
  464. LONG cb = 0;
  465. BYTE bT;
  466. _fNeedDelimeter = FALSE;
  467. while(cb < cbBuffer )
  468. {
  469. bT = *pbBuffer++; // Put out hex value of byte
  470. PutChar(szHexDigits[bT >> 4]); // Store high nibble
  471. PutChar(szHexDigits[bT & 15]); // Store low nibble
  472. // Every 78 chars and at end of group, drop a line
  473. if (!(++cb % 39) || (cb == cbBuffer))
  474. Puts(szLineBreak, sizeof(szLineBreak) - 1);
  475. }
  476. return cb;
  477. }
  478. /*
  479. * CRTFWrite::WriteBinData(pbBuffer, cbBuffer)
  480. *
  481. * @mfunc
  482. * Write out object binary data. This must be called only after all
  483. * initial object header information has been written out.
  484. *
  485. * @rdesc
  486. * LONG count of bytes written out
  487. */
  488. LONG CRTFWrite::WriteBinData(
  489. BYTE * pbBuffer, //@parm Point to write buffer
  490. LONG cbBuffer) //@parm Count of bytes to write out
  491. {
  492. TRACEBEGIN(TRCSUBSYSRTFW, TRCSCOPEINTERN, "CRTFWrite::WriteData");
  493. LONG cb = 0;
  494. BYTE bT;
  495. _fNeedDelimeter = FALSE;
  496. while(cb < cbBuffer )
  497. {
  498. bT = *pbBuffer++;
  499. if (!PutChar(bT))
  500. break;
  501. cb++;
  502. }
  503. return cb;
  504. }
  505. /*
  506. * CRTFWrite::WriteRtfObject(prtfObject, fPicture)
  507. *
  508. * @mfunc
  509. * Writes out an picture or object header's render information
  510. *
  511. * @rdesc
  512. * EC The error code
  513. *
  514. * @devnote
  515. * Eventually use keywords from rtf input list rather than partially
  516. * creating them on the fly
  517. */
  518. EC CRTFWrite::WriteRtfObject(
  519. RTFOBJECT & rtfObject, //@parm Object header info
  520. BOOL fPicture) //@parm TRUE if this is a header for a picture/object
  521. {
  522. TRACEBEGIN(TRCSUBSYSRTFW, TRCSCOPEINTERN, "CRTFWrite::WriteRtfObject");
  523. LONG i;
  524. LONG * pDim = &rtfObject.xExt;
  525. const BYTE *pKeyWordIndex = ObjectKeyWordIndexes;
  526. if(fPicture)
  527. {
  528. pKeyWordIndex = PictureKeyWordIndexes;
  529. if(rtfObject.sType == ROT_Metafile)
  530. pDim = &rtfObject.xExtPict;
  531. }
  532. //Extents, e.g., \picw,\pich
  533. for(i = 2; i--; pDim++, pKeyWordIndex++)
  534. {
  535. if (*pDim)
  536. PutCtrlWord(CWF_VAL, *pKeyWordIndex, (SHORT)*pDim);
  537. }
  538. // Scaling, e.g., \picscalex, \picscaley
  539. pDim = &rtfObject.xScale;
  540. for(i = 2; i--; pDim++, pKeyWordIndex++)
  541. {
  542. if (*pDim && *pDim != 100)
  543. PutCtrlWord(CWF_VAL, *pKeyWordIndex, (SHORT)*pDim);
  544. }
  545. // Cropping, e.g., \piccropl, \piccropt, piccropr, \piccropb
  546. pDim = &rtfObject.rectCrop.left;
  547. for(i = 4; i--; pDim++, pKeyWordIndex++)
  548. {
  549. if (*pDim)
  550. PutCtrlWord(CWF_VAL, *pKeyWordIndex, (SHORT)*pDim);
  551. }
  552. // Write goal sizes
  553. if(fPicture)
  554. {
  555. if (rtfObject.xExtGoal)
  556. PutCtrlWord (CWF_VAL, i_picwgoal, rtfObject.xExtGoal);
  557. if (rtfObject.yExtGoal)
  558. PutCtrlWord (CWF_VAL, i_pichgoal, rtfObject.yExtGoal);
  559. }
  560. return _ecParseError;
  561. }
  562. /*
  563. * CRTFWrite::WritePicture(&rtfObject)
  564. *
  565. * @func
  566. * Writes out a picture's header as well as the object's data.
  567. *
  568. * @rdesc
  569. * EC The error code
  570. */
  571. EC CRTFWrite::WritePicture(
  572. RTFOBJECT & rtfObject) //@parm Object header info
  573. {
  574. TRACEBEGIN(TRCSUBSYSRTFW, TRCSCOPEINTERN, "CRTFWrite::WritePicture");
  575. _ecParseError = ecStreamOutObj;
  576. // Start and write picture group
  577. PutCtrlWord( CWF_GRP, i_pict );
  578. // Write what kind of picture this is. Default JPEG or PNG
  579. LONG iFormat = CWF_STR;
  580. LONG sType = rtfObject.sType;
  581. if(sType != ROT_JPEG && sType != ROT_PNG)
  582. {
  583. sType = ROT_Metafile;
  584. iFormat = CWF_VAL;
  585. }
  586. PutCtrlWord(iFormat, IndexROT[sType], rtfObject.sPictureType);
  587. // Write picture render details
  588. WriteRtfObject(rtfObject, TRUE);
  589. // Start picture data
  590. Puts(szLineBreak, sizeof(szLineBreak) - 1);
  591. // Write out the data
  592. if ((UINT) WriteData(rtfObject.pbResult, rtfObject.cbResult) != rtfObject.cbResult)
  593. goto CleanUp;
  594. _ecParseError = ecNoError;
  595. CleanUp:
  596. PutChar(chEndGroup); // End picture data
  597. return _ecParseError;
  598. }
  599. /*
  600. * CRTFWrite::WriteDib(&rtfObject)
  601. *
  602. * @mfunc
  603. * Writes out an DIB primarily for Win CE
  604. *
  605. * @rdesc
  606. * EC The error code
  607. *
  608. * @devnote
  609. * *** Writes only dibs ***
  610. */
  611. EC CRTFWrite::WriteDib(
  612. RTFOBJECT & rtfObject) //@parm Object header info
  613. {
  614. TRACEBEGIN(TRCSUBSYSRTFW, TRCSCOPEINTERN, "CRTFWrite::WritePicture");
  615. LPBITMAPINFO pbmi = (LPBITMAPINFO) rtfObject.pbResult;
  616. _ecParseError = ecStreamOutObj;
  617. // ************** V-GUYB: Add this for converting pictures to 2bpp during stream out.
  618. // Store the original values so we can restore them on exit.
  619. LPBYTE pbResult = rtfObject.pbResult;
  620. ULONG cbResult = rtfObject.cbResult;
  621. HGLOBAL hMem2bpp = 0;
  622. #if defined(CONVERT2BPP)
  623. // Pictures must be saved as 2bpp if saving to PWord V1 format.
  624. if((_dwFlags & SFF_PWD) && ((_dwFlags & SFF_RTFVAL) >> 16 == 0))
  625. {
  626. if(pbmi->bmiHeader.biBitCount > PWDV1_BPP)
  627. {
  628. HWND hWnd;
  629. HDC hdc, hdcSrc, hdcDst;
  630. HBITMAP hdibSrc, hdibDst;
  631. LPBYTE pbDibSrc, pbDibDst;
  632. BMI2BPP bmi2bpp = {0};
  633. int iOffset, nBytes;
  634. // First get a dc with the source dib in it.
  635. hWnd = GetDesktopWindow();
  636. hdc = GetDC(hWnd);
  637. hdcSrc = CreateCompatibleDC(hdc);
  638. // Using CreateDIBSection below ensures that the working dibs and dcs will get a
  639. // bpp of the appropriate dib, not a bpp based on the bpp of the device display.
  640. if((hdibSrc = CreateDIBSection(hdcSrc, pbmi, DIB_RGB_COLORS, (void**)&pbDibSrc, NULL, 0)))
  641. {
  642. SelectObject(hdcSrc, hdibSrc);
  643. // Get an offset to the source bits
  644. iOffset = sizeof(BITMAPINFOHEADER) + (sizeof(RGBQUAD) * (1<<pbmi->bmiHeader.biBitCount));
  645. memcpy(pbDibSrc, &rtfObject.pbResult[iOffset], rtfObject.cbResult - iOffset);
  646. // Build up a BITMAPINFO appropriate for a 2bpp dib
  647. bmi2bpp.bmih = pbmi->bmiHeader;
  648. bmi2bpp.bmih.biBitCount = PWDV1_BPP;
  649. // Add the 4 color color-table
  650. memcpy(bmi2bpp.colors, (RGBQUAD*)ColorTable2bpp, (1<<PWDV1_BPP) * sizeof(RGBQUAD));
  651. // Create the new dib
  652. hdcDst = CreateCompatibleDC(hdc);
  653. if((hdibDst = CreateDIBSection(hdcDst, (BITMAPINFO*)&bmi2bpp, DIB_RGB_COLORS, (void**)&pbDibDst, NULL, 0)))
  654. {
  655. SelectObject(hdcDst, hdibDst);
  656. // Blit the > 2bpp dib into the 2bpp dib and let the system do the color mapping.
  657. BitBlt(hdcDst, 0, 0, bmi2bpp.bmih.biWidth, bmi2bpp.bmih.biHeight, hdcSrc, 0, 0, SRCCOPY);
  658. // Calculate the new bytes per line for the 2bpp dib.
  659. rtfObject.cBytesPerLine = (((bmi2bpp.bmih.biWidth * PWDV1_BPP) + 31) & ~31) / 8; // DWORD boundary.
  660. // Get the new size of the 2bpp byte array
  661. nBytes = rtfObject.cBytesPerLine * bmi2bpp.bmih.biHeight;
  662. // Get total size of 2bpp dib, (including header and 4 color color-table)
  663. cbResult = sizeof(bmi2bpp) + nBytes;
  664. // Don't change the input pbResult as that is the internal representation of
  665. // the dib. This conversion to 2bpp is only for writing to the output file.
  666. if((hMem2bpp = GlobalAlloc(GMEM_FIXED, cbResult)))
  667. {
  668. if((pbResult = (LPBYTE)GlobalLock(hMem2bpp)))
  669. {
  670. // Copy in the dib header.
  671. memcpy(pbResult, &bmi2bpp.bmih, sizeof(BITMAPINFOHEADER));
  672. // Copy in the 4 color color-table.
  673. memcpy(&pbResult[sizeof(BITMAPINFOHEADER)], (RGBQUAD*)ColorTable2bpp, (1<<PWDV1_BPP) * sizeof(RGBQUAD));
  674. // Now copy in the byte array.
  675. memcpy(&pbResult[sizeof(bmi2bpp)], pbDibDst, nBytes);
  676. _ecParseError = ecNoError;
  677. }
  678. }
  679. DeleteObject(hdibDst);
  680. }
  681. DeleteDC(hdcDst);
  682. DeleteObject(hdibSrc);
  683. }
  684. DeleteDC(hdcSrc);
  685. ReleaseDC(hWnd, hdc);
  686. if(_ecParseError != ecNoError)
  687. goto CleanUp;
  688. }
  689. }
  690. #endif // CONVERT2BPP
  691. // ************** V-GUYB: End of conversion stuff.
  692. // Start and write picture group
  693. PutCtrlWord( CWF_GRP, i_pict );
  694. // Write that this is dib
  695. PutCtrlWord( CWF_VAL, i_dibitmap,rtfObject.sPictureType );
  696. // V-GUYB:
  697. // rtfObject.*Scale is not updated as the user stretches the picture,
  698. // so don't use those here. But rtfObject.*Ext has been set up in the
  699. // calling routine to account for the current site dimensions.
  700. PutCtrlWord( CWF_VAL, i_picscalex, (rtfObject.xExt * 100) / rtfObject.xExtGoal);
  701. PutCtrlWord( CWF_VAL, i_picscaley, (rtfObject.yExt * 100) / rtfObject.yExtGoal);
  702. // Write picture render details
  703. PutCtrlWord( CWF_VAL, i_picw, pbmi->bmiHeader.biWidth );
  704. PutCtrlWord( CWF_VAL, i_pich, pbmi->bmiHeader.biHeight );
  705. PutCtrlWord( CWF_VAL, i_picwgoal, rtfObject.xExtGoal );
  706. PutCtrlWord( CWF_VAL, i_pichgoal, rtfObject.yExtGoal );
  707. PutCtrlWord( CWF_VAL, i_wbmbitspixel, pbmi->bmiHeader.biBitCount );
  708. PutCtrlWord( CWF_VAL, i_wbmplanes, pbmi->bmiHeader.biPlanes );
  709. PutCtrlWord( CWF_VAL, i_wbmwidthbytes, rtfObject.cBytesPerLine );
  710. // Write out the data
  711. PutCtrlWord( CWF_VAL, i_bin, cbResult );
  712. if ((UINT) WriteBinData( pbResult, cbResult ) != cbResult)
  713. {
  714. // This "recovery" action needs to be rethought. There is no way
  715. // the reader will be able to get back in synch.
  716. goto CleanUp;
  717. }
  718. _ecParseError = ecNoError;
  719. CleanUp:
  720. // Did we lock or allocate some temporary space for a 2bpp dib?
  721. if(rtfObject.pbResult != pbResult)
  722. GlobalUnlock(pbResult); // Yes, so unlock it now
  723. if(hMem2bpp)
  724. GlobalFree(hMem2bpp);
  725. // Restore original values.
  726. rtfObject.pbResult = pbResult;
  727. rtfObject.cbResult = cbResult;
  728. PutChar(chEndGroup); // End picture data
  729. return _ecParseError;
  730. }
  731. /*
  732. * CRTFWrite::WriteObject(cp, pobj)
  733. *
  734. * @mfunc
  735. * Writes out an object's header as well as the object's data.
  736. *
  737. * @rdesc
  738. * EC The error code
  739. */
  740. EC CRTFWrite::WriteObject(
  741. LONG cp, //@parm Object char position
  742. COleObject *pobj) //@parm Object
  743. {
  744. TRACEBEGIN(TRCSUBSYSRTFW, TRCSCOPEINTERN, "CRTFWrite::WriteObject");
  745. RTFOBJECT rtfObject;
  746. REOBJECT reObject = {sizeof(REOBJECT), cp} ;
  747. Assert(pobj);
  748. if (pobj->GetObjectData(&reObject, REO_GETOBJ_POLESITE
  749. | REO_GETOBJ_PSTG | REO_GETOBJ_POLEOBJ)) // todo fix Release
  750. {
  751. TRACEERRORSZ("Error geting object ");
  752. }
  753. GetRtfObject(reObject, rtfObject);
  754. HGLOBAL hdata = pobj->GetHdata();
  755. if (hdata)
  756. {
  757. COleObject::ImageInfo *pimageinfo = pobj->GetImageInfo();
  758. rtfObject.pbResult = (LPBYTE) GlobalLock( hdata );
  759. rtfObject.cbResult = GlobalSize( hdata );
  760. rtfObject.sType = ROT_DIB;
  761. rtfObject.xExt = (SHORT) TwipsFromHimetric( reObject.sizel.cx );
  762. rtfObject.yExt = (SHORT) TwipsFromHimetric( reObject.sizel.cy );
  763. rtfObject.xScale = pimageinfo->xScale;
  764. rtfObject.yScale = pimageinfo->yScale;
  765. rtfObject.xExtGoal = pimageinfo->xExtGoal;
  766. rtfObject.yExtGoal = pimageinfo->yExtGoal;
  767. rtfObject.cBytesPerLine = pimageinfo->cBytesPerLine;
  768. WriteDib(rtfObject);
  769. GlobalUnlock( rtfObject.pbResult );
  770. // Make sure to release otherwise the object won't go away
  771. if (reObject.pstg) reObject.pstg->Release();
  772. if (reObject.polesite) reObject.polesite->Release();
  773. if (reObject.poleobj) reObject.poleobj->Release();
  774. return _ecParseError;
  775. }
  776. switch(rtfObject.sType) // Handle pictures in our own
  777. { // special way
  778. case ROT_Embedded:
  779. case ROT_Link:
  780. case ROT_AutoLink:
  781. break;
  782. case ROT_Metafile:
  783. case ROT_DIB:
  784. case ROT_Bitmap:
  785. WritePicture(rtfObject);
  786. goto CleanUpNoEndGroup;
  787. #ifdef DEBUG
  788. default:
  789. AssertSz(FALSE, "CRTFW::WriteObject: Unknown ROT");
  790. break;
  791. #endif DEBUG
  792. }
  793. // Start and write object group
  794. PutCtrlWord( CWF_GRP, i_object );
  795. PutCtrlWord( CWF_STR, IndexROT[rtfObject.sType] );
  796. // PutCtrlWord(CWF_STR, i_objupdate); // TODO may be it needs more smart decision
  797. if (rtfObject.szClass) // Write object class
  798. {
  799. PutCtrlWord(CWF_AST, i_objclass);
  800. WritePcData(rtfObject.szClass);
  801. PutChar(chEndGroup);
  802. }
  803. if (rtfObject.szName) // Write object name
  804. {
  805. PutCtrlWord(CWF_AST, i_objname);
  806. WritePcData(rtfObject.szName);
  807. PutChar( chEndGroup );
  808. }
  809. if (rtfObject.fSetSize) // Write object sizing options
  810. PutCtrlWord(CWF_STR, i_objsetsize);
  811. WriteRtfObject( rtfObject, FALSE ) ; // Write object render info
  812. PutCtrlWord( CWF_AST, i_objdata ) ; // info, start object
  813. Puts( szLineBreak, sizeof(szLineBreak) - 1); // data group
  814. if (!ObjectWriteToEditstream( reObject, rtfObject ))
  815. {
  816. TRACEERRORSZ("Error writing object data");
  817. if (!_ecParseError)
  818. _ecParseError = ecStreamOutObj;
  819. PutChar( chEndGroup ); // End object data
  820. goto CleanUp;
  821. }
  822. PutChar( chEndGroup ); // End object data
  823. PutCtrlWord( CWF_GRP, i_result ); // Start results group
  824. WritePicture( rtfObject ); // Write results group
  825. PutChar( chEndGroup ); // End results group
  826. CleanUp:
  827. PutChar( chEndGroup ); // End object group
  828. CleanUpNoEndGroup:
  829. if (reObject.pstg) reObject.pstg->Release();
  830. if (reObject.polesite) reObject.polesite->Release();
  831. if (reObject.poleobj) reObject.poleobj->Release();
  832. if (rtfObject.pbResult)
  833. {
  834. HGLOBAL hmem;
  835. hmem = GlobalHandle( rtfObject.pbResult );
  836. GlobalUnlock( hmem );
  837. GlobalFree( hmem );
  838. }
  839. if (rtfObject.szClass)
  840. CoTaskMemFree( rtfObject.szClass );
  841. return _ecParseError;
  842. }
  843. /*
  844. * CRTFWrite::WriteBackgroundInfo(pDocInfo)
  845. *
  846. * @mfunc
  847. * Write out screen background data if a background is enabled.
  848. *
  849. * @rdesc
  850. * EC The error code
  851. */
  852. EC CRTFWrite::WriteBackgroundInfo(
  853. CDocInfo *pDocInfo)
  854. {
  855. TRACEBEGIN(TRCSUBSYSRTFW, TRCSCOPEINTERN, "CRTFWrite::WriteInfo");
  856. if (!PutCtrlWord(CWF_AST, i_background))
  857. goto CleanUp2;
  858. if (!Puts(szShapeLeadIn, sizeof(szShapeLeadIn) - 1) ||
  859. !PutShapeParm(i_filltype, pDocInfo->_nFillType) ||
  860. pDocInfo->_sFillAngle && !PutShapeParm(i_fillangle, pDocInfo->_sFillAngle << 16) ||
  861. pDocInfo->_crColor != WHITE && !PutShapeParm(i_fillcolor, pDocInfo->_crColor) ||
  862. pDocInfo->_crBackColor != WHITE && !PutShapeParm(i_fillbackcolor, pDocInfo->_crBackColor) ||
  863. !PutShapeParm(i_fillfocus, pDocInfo->_bFillFocus))
  864. {
  865. goto CleanUp;
  866. }
  867. if(pDocInfo->_bPicFormat != 0xFF)
  868. {
  869. RTFOBJECT rtfObject;
  870. // Blank out the full structure
  871. ZeroMemory(&rtfObject, sizeof(RTFOBJECT));
  872. // Build the header
  873. if(pDocInfo->_hdata)
  874. {
  875. rtfObject.pbResult = (LPBYTE) GlobalLock(pDocInfo->_hdata);
  876. rtfObject.cbResult = GlobalSize(pDocInfo->_hdata);
  877. }
  878. rtfObject.xExt = pDocInfo->_xExt;
  879. rtfObject.yExt = pDocInfo->_yExt;
  880. rtfObject.xScale = pDocInfo->_xScale;
  881. rtfObject.yScale = pDocInfo->_yScale;
  882. rtfObject.xExtGoal = pDocInfo->_xExtGoal;
  883. rtfObject.yExtGoal = pDocInfo->_yExtGoal;
  884. rtfObject.xExtPict = pDocInfo->_xExtPict;
  885. rtfObject.yExtPict = pDocInfo->_yExtPict;
  886. rtfObject.rectCrop = pDocInfo->_rcCrop;
  887. rtfObject.sType = pDocInfo->_bPicFormat;
  888. rtfObject.sPictureType = pDocInfo->_bPicFormatParm;
  889. if(Puts(szShapeFillBlip, sizeof(szShapeFillBlip) - 1))
  890. {
  891. WritePicture(rtfObject);
  892. PutChar(chEndGroup); // Close {\sv {\pict...}}
  893. PutChar(chEndGroup); // Close {\sp{\sn...}{\sv...}}
  894. }
  895. if(pDocInfo->_hdata)
  896. GlobalUnlock(pDocInfo->_hdata);
  897. }
  898. CleanUp:
  899. PutChar(chEndGroup); // Close {\*\shpinst...}
  900. PutChar(chEndGroup); // Close {\shp...}
  901. CleanUp2:
  902. Puts(szEndGroupCRLF, 3); // Close {\*\background...}
  903. return _ecParseError;
  904. }
  905. /*
  906. * CRTFWrite::PutShapeParm(iCtrl, iValue)
  907. *
  908. * @mfunc
  909. * Put control word with rgShapeKeyword[] index <p iCtrl> and value <p iValue>
  910. *
  911. * @rdesc
  912. * TRUE if successful
  913. */
  914. BOOL CRTFWrite::PutShapeParm(
  915. LONG iCtrl, //@parm Index into rgShapeKeyword array
  916. LONG iValue) //@parm Control-word parameter value. If missing,
  917. { // 0 is assumed
  918. TRACEBEGIN(TRCSUBSYSRTFW, TRCSCOPEINTERN, "CRTFWrite::PutShapeParm");
  919. CHAR szT[60];
  920. LONG cb = sprintf(szT, (char *)szShapeParm, rgShapeKeyword[iCtrl].szKeyword, iValue);
  921. return Puts(szT, cb);
  922. }
  923. /*
  924. * CRTFWrite::GetRtfObjectMetafilePict (hmfp, &rtfobject, &sizelGoal)
  925. *
  926. * @mfunc
  927. * Gets information about an metafile into a structure.
  928. *
  929. * @rdesc
  930. * BOOL TRUE on success, FALSE if object cannot be written to RTF.
  931. */
  932. BOOL CRTFWrite::GetRtfObjectMetafilePict(
  933. HGLOBAL hmfp, //@parm The object data
  934. RTFOBJECT & rtfobject, //@parm Where to put the info
  935. SIZEL & sizelGoal) //@parm
  936. {
  937. #ifndef NOMETAFILES
  938. BOOL fSuccess = FALSE;
  939. LPMETAFILEPICT pmfp = (LPMETAFILEPICT)GlobalLock(hmfp);
  940. HGLOBAL hmem = NULL;
  941. ULONG cb;
  942. if (!pmfp)
  943. goto Cleanup;
  944. // Build the header
  945. rtfobject.sPictureType = (SHORT) pmfp->mm;
  946. rtfobject.xExtPict = (SHORT) pmfp->xExt;
  947. rtfobject.yExtPict = (SHORT) pmfp->yExt;
  948. rtfobject.xExtGoal = (SHORT) TwipsFromHimetric(sizelGoal.cx);
  949. rtfobject.yExtGoal = (SHORT) TwipsFromHimetric(sizelGoal.cy);
  950. // Find out how much room we'll need
  951. cb = GetMetaFileBitsEx(pmfp->hMF, 0, NULL);
  952. if (!cb)
  953. goto Cleanup;
  954. // Allocate that space
  955. hmem = GlobalAlloc(GHND, cb);
  956. if (!hmem)
  957. goto Cleanup;
  958. rtfobject.pbResult = (LPBYTE)GlobalLock(hmem);
  959. if (!rtfobject.pbResult)
  960. {
  961. GlobalFree(hmem);
  962. goto Cleanup;
  963. }
  964. // Get the data
  965. rtfobject.cbResult = (ULONG) GetMetaFileBitsEx(pmfp->hMF, (UINT) cb,
  966. rtfobject.pbResult);
  967. if (rtfobject.cbResult != cb)
  968. {
  969. rtfobject.pbResult = NULL;
  970. GlobalFree(hmem);
  971. goto Cleanup;
  972. }
  973. fSuccess = TRUE;
  974. Cleanup:
  975. GlobalUnlock(hmfp);
  976. return fSuccess;
  977. #else
  978. return FALSE;
  979. #endif
  980. }
  981. /*
  982. * CRTFWrite::GetRtfObject (&reobject, &rtfobject)
  983. *
  984. * @mfunc
  985. * Gets information about an RTF object into a structure.
  986. *
  987. * @rdesc
  988. * BOOL TRUE on success, FALSE if object cannot be written to RTF.
  989. */
  990. BOOL CRTFWrite::GetRtfObject(
  991. REOBJECT &reobject, //@parm Information from GetObject
  992. RTFOBJECT &rtfobject) //@parm Where to put info. Strings are read only
  993. // and are owned by the object subsystem
  994. {
  995. BOOL fSuccess = FALSE;
  996. BOOL fNoOleServer = FALSE;
  997. const BOOL fStatic = !!(reobject.dwFlags & REO_STATIC);
  998. SIZEL sizelObj = reobject.sizel;
  999. //COMPATIBILITY: RICHED10 code had a frame size. Do we need something similiar.
  1000. LPTSTR szProgId;
  1001. // Blank out the full structure
  1002. ZeroMemory(&rtfobject, sizeof(RTFOBJECT));
  1003. // If object has no storage it cannot be written.
  1004. if (!reobject.pstg)
  1005. return FALSE;
  1006. // If we don't have the progID for a real OLE object, get it now
  1007. if (!fStatic )
  1008. {
  1009. rtfobject.szClass = NULL;
  1010. // We need a ProgID to put into the RTF stream.
  1011. //$ REVIEW: MAC This call is incorrect for the Mac. It may not matter though
  1012. // if ole support in RichEdit is not needed for the Mac.
  1013. if (ProgIDFromCLSID(reobject.clsid, &szProgId))
  1014. fNoOleServer = TRUE;
  1015. else
  1016. rtfobject.szClass = szProgId;
  1017. }
  1018. #ifndef NOMETAFILES
  1019. HGLOBAL hmfp = OleStdGetMetafilePictFromOleObject(reobject.poleobj,
  1020. reobject.dvaspect, &sizelObj, NULL);
  1021. if (hmfp)
  1022. {
  1023. LPMETAFILEPICT pmfp = NULL;
  1024. fSuccess = GetRtfObjectMetafilePict(hmfp, rtfobject, sizelObj);
  1025. if (pmfp = (LPMETAFILEPICT)GlobalLock(hmfp))
  1026. {
  1027. if (pmfp->hMF)
  1028. DeleteMetaFile(pmfp->hMF);
  1029. GlobalUnlock(hmfp);
  1030. }
  1031. GlobalFree(hmfp);
  1032. // If we don't have Server and we can't get metafile, forget it.
  1033. if (!fSuccess && fNoOleServer)
  1034. return fSuccess;
  1035. }
  1036. #endif
  1037. if (!fStatic)
  1038. {
  1039. // Fill in specific fields
  1040. rtfobject.sType = fNoOleServer ? ROT_Metafile : ROT_Embedded; //$ FUTURE: set for links
  1041. rtfobject.xExt = (SHORT) TwipsFromHimetric(sizelObj.cx);
  1042. rtfobject.yExt = (SHORT) TwipsFromHimetric(sizelObj.cy);
  1043. // fSuccess set TRUE even if we couldn't retrieve a metafile
  1044. // because we don't need a metafile in the non-static case;
  1045. // it's just nice to have one
  1046. fSuccess = TRUE;
  1047. }
  1048. rtfobject.fSetSize = 0; //$ REVIEW: Hmmm
  1049. return fSuccess;
  1050. }
  1051. /*
  1052. * CRTFWrite::ObjectWriteToEditstream (&reObject, &rtfobject)
  1053. *
  1054. * @mfunc
  1055. * Writes an OLE object data to the RTF output stream.
  1056. *
  1057. * @rdesc
  1058. * BOOL TRUE on success, FALSE on failure.
  1059. */
  1060. BOOL CRTFWrite::ObjectWriteToEditstream(
  1061. REOBJECT &reObject, //@parm Info from GetObject
  1062. RTFOBJECT &rtfobject) //@parm Where to get icon data
  1063. {
  1064. HRESULT hr = 0;
  1065. // Force the object to update its storage //// ????
  1066. // Not necessary. Already done in WriteRtf
  1067. // reObject.polesite->SaveObject();
  1068. // If the object is iconic we do some special magic
  1069. if (reObject.dvaspect == DVASPECT_ICON)
  1070. {
  1071. HANDLE hGlobal;
  1072. STGMEDIUM med;
  1073. // Force the presentation to be the iconic view.
  1074. med.tymed = TYMED_HGLOBAL;
  1075. hGlobal = GlobalHandle(rtfobject.pbResult);
  1076. med.hGlobal = hGlobal;
  1077. hr = OleConvertIStorageToOLESTREAMEx(reObject.pstg,
  1078. CF_METAFILEPICT,
  1079. rtfobject.xExtPict,
  1080. rtfobject.yExtPict,
  1081. rtfobject.cbResult, &med,
  1082. (LPOLESTREAM) &RTFWriteOLEStream);
  1083. }
  1084. else
  1085. {
  1086. // Do the standard conversion
  1087. hr = OleConvertIStorageToOLESTREAM(reObject.pstg, (LPOLESTREAM) &RTFWriteOLEStream);
  1088. }
  1089. return SUCCEEDED(hr);
  1090. }