Leaked source code of windows server 2003
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.

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