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.

2555 lines
68 KiB

  1. /*
  2. * @doc INTERNAL
  3. *
  4. * @module RTFWRIT.CPP - RichEdit RTF Writer (w/o objects) |
  5. *
  6. * This file contains the implementation of the RTF writer
  7. * for the RichEdit control, except for embedded objects,
  8. * which are handled mostly in rtfwrit2.cpp
  9. *
  10. * Authors: <nl>
  11. * Original RichEdit 1.0 RTF converter: Anthony Francisco <nl>
  12. * Conversion to C++ and RichEdit 2.0: Murray Sargent <nl>
  13. * Lots of enhancements: Brad Olenick <nl>
  14. *
  15. * Copyright (c) 1995-1998, Microsoft Corporation. All rights reserved.
  16. */
  17. #include "_common.h"
  18. #include "_rtfwrit.h"
  19. #include "_objmgr.h"
  20. #include "_coleobj.h"
  21. #include "_font.h"
  22. ASSERTDATA
  23. extern const KEYWORD rgKeyword[];
  24. //========================= Global String Constants ==================================
  25. BYTE bCharSetANSI = ANSI_CHARSET; // ToDo: make more general
  26. #ifdef DEBUG
  27. // Quick way to find out what went wrong: rgszParseError[ecParseError]
  28. //
  29. CHAR * rgszParseError[] =
  30. {
  31. "No error",
  32. "Can't convert to Unicode", // FF
  33. "Color table overflow", // FE
  34. "Expecting '\\rtf'", // FD
  35. "Expecting '{'", // FC
  36. "Font table overflow", // FB
  37. "General failure", // FA
  38. "Keyword too long", // F9
  39. "Lexical analyzer initialize failed", // F8
  40. "No memory", // F7
  41. "Parser is busy", // F6
  42. "PutChar() function failed", // F5
  43. "Stack overflow", // F4
  44. "Stack underflow", // F3
  45. "Unexpected character", // F2
  46. "Unexpected end of file", // F1
  47. "Unexpected token", // F0
  48. "UnGetChar() function failed", // EF
  49. "Maximum text length reached", // EE
  50. "Streaming out object failed", // ED
  51. "Streaming in object failed", // EC
  52. "Truncated at CR or LF", // EB
  53. "Format-cache failure", // EA
  54. NULL // End of list marker
  55. };
  56. CHAR * szDest[] =
  57. {
  58. "RTF",
  59. "Color Table",
  60. "Font Table",
  61. "Binary",
  62. "Object",
  63. "Object Class",
  64. "Object Name",
  65. "Object Data",
  66. "Field",
  67. "Field Result",
  68. "Field Instruction",
  69. "Symbol",
  70. "Paragraph Numbering",
  71. "Picture"
  72. };
  73. #endif
  74. // Most control-word output is done with the following printf formats
  75. static const CHAR * rgszCtrlWordFormat[] =
  76. {
  77. "\\%s", "\\%s%d", "{\\%s", "{\\*\\%s", "{\\%s%d"
  78. };
  79. // Special control-word formats
  80. static const CHAR szBeginFontEntryFmt[] = "{\\f%d\\%s";
  81. static const CHAR szBulletGroup[] = "{\\pntext\\f%d\\'B7\\tab}";
  82. static const CHAR szBulletFmt[] = "{\\*\\pn\\pnlvl%s\\pnf%d\\pnindent%d{\\pntxtb\\'B7}}";
  83. static const CHAR szBeginNumberGroup[] = "{\\pntext\\f%d ";
  84. static const CHAR szEndNumberGroup[] = "\\tab}";
  85. static const CHAR szBeginNumberFmt[] = "{\\*\\pn\\pnlvl%s\\pnf%d\\pnindent%d\\pnstart%d";
  86. static const CHAR szpntxtb[] = "{\\pntxtb(}";
  87. static const CHAR szpntxta[] = "{\\pntxta%c}";
  88. static const CHAR szColorEntryFmt[] = "\\red%d\\green%d\\blue%d;";
  89. static const CHAR szEndFontEntry[] = ";}";
  90. const CHAR szEndGroupCRLF[] = "}\r\n";
  91. static const CHAR szEscape2CharFmt[] = "\\'%02x\\'%02x";
  92. static const CHAR szLiteralCharFmt[] = "\\%c";
  93. static const CHAR szPar[] = "\\par\r\n";
  94. static const CHAR szPar10[] = "\r\n\\par ";
  95. static const CHAR szObjPosHolder[] = "\\objattph\\'20";
  96. static const CHAR szDefaultFont[] = "\\deff0";
  97. static const CHAR szHorzdocGroup[] = "{\\horzdoc}";
  98. static const CHAR szNormalStyle[] = "{ Normal;}";
  99. static const CHAR szHeadingStyle[] = "{\\s%d heading %d;}";
  100. static const CHAR szEndRow[] = "\\row\r\n";
  101. #define szEscapeCharFmt &szEscape2CharFmt[6]
  102. // Arrays of RTF control-word indices. NOTE: if any index is greater than 255,
  103. // the corresponding array must be changed to a WORD array. The compiler
  104. // issues a warning in such cases
  105. const BYTE rgiszTerminators[] =
  106. {
  107. i_cell, 0, i_tab, 0, i_line, i_page
  108. };
  109. // Keep these indices in sync with the special character values in _common.h
  110. const WORD rgiszSpecial[] =
  111. {
  112. i_enspace, // 0x2002
  113. i_emspace, // 0x2003
  114. 0, // 0x2004
  115. 0, // 0x2005
  116. 0, // 0x2006
  117. 0, // 0x2007
  118. 0, // 0x2008
  119. 0, // 0x2009
  120. 0, // 0x200A
  121. 0, // 0x200B
  122. i_zwnj, // 0x200C
  123. i_zwj, // 0x200D
  124. i_ltrmark, // 0x200E
  125. i_rtlmark, // 0x200F
  126. 0, // 0x2010
  127. 0, // 0x2011
  128. 0, // 0x2012
  129. i_endash, // 0x2013
  130. i_emdash, // 0x2014
  131. 0, // 0x2015
  132. 0, // 0x2016
  133. 0, // 0x2017
  134. i_lquote, // 0x2018
  135. i_rquote, // 0x2019
  136. 0, // 0x201A
  137. 0, // 0x201B
  138. i_ldblquote, // 0x201C
  139. i_rdblquote, // 0x201D
  140. 0, // 0x201E
  141. 0, // 0x201F
  142. 0, // 0x2020
  143. 0, // 0x2021
  144. i_bullet // 0x2022
  145. };
  146. const BYTE rgiszEffects[] =
  147. { // Effects keywords
  148. i_deleted, i_revised, i_disabled, i_impr, // Ordered max CFE_xx to
  149. i_embo, i_shad, i_outl, i_v, i_caps, i_scaps, // min CFE_xx (cept i_deleted)
  150. i_disabled, i_protect, i_strike, i_ul, i_i, i_b // (see WriteCharFormat())
  151. };
  152. #define CEFFECTS ARRAY_SIZE(rgiszEffects)
  153. const BYTE rgiszPFEffects[] = // PF effects keywords
  154. { // Ordered max PFE_xx to
  155. i_collapsed, i_sbys, i_hyphpar, i_nowidctlpar, // min PFE_xx
  156. i_noline, i_pagebb, i_keepn, i_keep, i_rtlpar
  157. }; // (see WriteParaFormat())
  158. #define CPFEFFECTS ARRAY_SIZE(rgiszPFEffects)
  159. const BYTE rgiszUnderlines[] =
  160. {
  161. i_ulnone, i_ul, i_ulw, i_uldb, i_uld, // Std Word underlines
  162. i_uldash, i_uldashd, i_uldashdd, i_ulwave, i_ulth, i_ulhair
  163. };
  164. #define CUNDERLINES ARRAY_SIZE(rgiszUnderlines)
  165. const BYTE rgiszFamily[] = // Font family RTF name
  166. { // keywords in order of
  167. i_fnil, i_froman, i_fswiss, i_fmodern, // bPitchAndFamily
  168. i_fscript, i_fdecor, i_ftech, i_fbidi
  169. };
  170. #define CFAMILIES ARRAY_SIZE(rgiszFamily)
  171. const BYTE rgiszAlignment[] = // Alignment keywords
  172. { // Keep in sync with
  173. i_ql, i_qr, i_qc, i_qj // alignment constants
  174. };
  175. const BYTE rgiszTabAlign[] = // Tab alignment keywords
  176. { // Keep in sync with tab
  177. i_tqc, i_tqr, i_tqdec // alignment constants
  178. };
  179. const BYTE rgiszTabLead[] = // Tab leader keywords
  180. { // Keep in sync with tab
  181. i_tldot, i_tlhyph, i_tlul, i_tlth, i_tleq // leader constants
  182. };
  183. const BYTE rgiszNumberStyle[] = // Numbering style keywords
  184. { // Keep in sync with TOM
  185. i_pndec, i_pnlcltr, i_pnucltr, // values
  186. i_pnlcrm, i_pnucrm
  187. };
  188. const BYTE rgiszBorders[] = // Border combination keywords
  189. {
  190. i_box,
  191. i_brdrt, i_brdrl, i_brdrb, i_brdrr,
  192. i_trbrdrt, i_trbrdrl, i_trbrdrb, i_trbrdrr,
  193. i_clbrdrt, i_clbrdrl, i_clbrdrb, i_clbrdrr
  194. };
  195. const BYTE rgiszBorderStyles[] = // Border style keywords
  196. {
  197. i_brdrdash, i_brdrdashsm, i_brdrdb, i_brdrdot,
  198. i_brdrhair, i_brdrs, i_brdrth, i_brdrtriple
  199. };
  200. #define CBORDERSTYLES ARRAY_SIZE(rgiszBorderStyles)
  201. const BYTE rgiszBorderEffects[] = // Border effect keywords
  202. {
  203. i_brdrbar, i_brdrbtw, i_brdrsh // Reverse order from bits
  204. };
  205. const BYTE rgiszShadingStyles[] = // Shading style keywords
  206. {
  207. i_bgbdiag, i_bgcross, i_bgdcross, i_bgdkbdiag,
  208. i_bgdkcross, i_bgdkdcross, i_bgdkfdiag, i_bgdkhoriz,
  209. i_bgdkvert, i_bgfdiag, i_bghoriz, i_bgvert
  210. };
  211. #define CSHADINGSTYLES ARRAY_SIZE(rgiszShadingStyles)
  212. // RGB with 2 bits per color type (in BGR order)
  213. const COLORREF g_Colors[] =
  214. {
  215. RGB( 0, 0, 0), // \red0\green0\blue0
  216. RGB( 0, 0, 255), // \red0\green0\blue255
  217. RGB( 0, 255, 255), // \red0\green255\blue255
  218. RGB( 0, 255, 0), // \red0\green255\blue0
  219. RGB(255, 0, 255), // \red255\green0\blue255
  220. RGB(255, 0, 0), // \red255\green0\blue0
  221. RGB(255, 255, 0), // \red255\green255\blue0
  222. RGB(255, 255, 255), // \red255\green255\blue255
  223. RGB( 0, 0, 128), // \red0\green0\blue128
  224. RGB( 0, 128, 128), // \red0\green128\blue128
  225. RGB( 0, 128, 0), // \red0\green128\blue0
  226. RGB(128, 0, 128), // \red128\green0\blue128
  227. RGB(128, 0, 0), // \red128\green0\blue0
  228. RGB(128, 128, 0), // \red128\green128\blue0
  229. RGB(128, 128, 128), // \red128\green128\blue128
  230. RGB(192, 192, 192), // \red192\green192\blue192
  231. };
  232. /*
  233. * CRTFWrite::MapsToRTFKeywordW(wch)
  234. *
  235. * @mfunc
  236. * Returns a flag indicating whether the character maps to an RTF keyword
  237. *
  238. * @rdesc
  239. * BOOL TRUE if char maps to RTF keyword
  240. */
  241. inline BOOL CRTFWrite::MapsToRTFKeywordW(
  242. WCHAR wch)
  243. {
  244. return
  245. !_fNCRForNonASCII &&
  246. (IN_RANGE(TAB, wch, CR) ||
  247. wch == CELL ||
  248. wch == BSLASH ||
  249. wch == LBRACE ||
  250. wch == RBRACE ||
  251. IN_RANGE(ENSPACE, wch, EMSPACE) ||
  252. IN_RANGE(ENDASH, wch, EMDASH) ||
  253. IN_RANGE(LQUOTE, wch, RQUOTE) ||
  254. IN_RANGE(LDBLQUOTE, wch, RDBLQUOTE) ||
  255. wch == BULLET ||
  256. wch == chOptionalHyphen ||
  257. wch == chNonBreakingSpace);
  258. }
  259. /*
  260. * CRTFWrite::MapsToRTFKeywordA(ch)
  261. *
  262. * @mfunc
  263. * Returns a flag indicating whether the character maps to an RTF keyword
  264. *
  265. * @rdesc
  266. * BOOL TRUE if char maps to RTF keyword
  267. */
  268. inline BOOL CRTFWrite::MapsToRTFKeywordA(char ch)
  269. {
  270. return IN_RANGE(TAB, ch, CR) ||
  271. ch == CELL ||
  272. ch == BSLASH ||
  273. ch == LBRACE ||
  274. ch == RBRACE;
  275. }
  276. /*
  277. * CRTFWrite::MapToRTFKeywordW(pv, cch, iCharEncoding)
  278. *
  279. * @mfunc
  280. * Examines the first character in the string pointed to by pv and
  281. * writes out the corresponding RTF keyword. In situations where
  282. * the first and subsequent characters map to a single keyword, we
  283. * return the number of additional characters used in the mapping.
  284. *
  285. * @rdesc
  286. * int indicates the number of additional characters used when
  287. * the mapping to an RTF keyword involves > 1 characters.
  288. */
  289. int CRTFWrite::MapToRTFKeyword(
  290. void * pv, //@parm ptr to ansi or Unicode string
  291. int cch,
  292. int iCharEncoding)
  293. {
  294. Assert(iCharEncoding == MAPTOKWD_ANSI || iCharEncoding == MAPTOKWD_UNICODE);
  295. WCHAR ch = ((iCharEncoding == MAPTOKWD_ANSI) ? *(char *)pv : *(WCHAR *)pv);
  296. int cchRet = 0;
  297. Assert((iCharEncoding == MAPTOKWD_ANSI) ? MapsToRTFKeywordA(ch) : MapsToRTFKeywordW(ch));
  298. switch(ch)
  299. {
  300. case BULLET:
  301. case EMDASH:
  302. case EMSPACE:
  303. case ENDASH:
  304. case ENSPACE:
  305. case LDBLQUOTE:
  306. case LQUOTE:
  307. case RDBLQUOTE:
  308. case RQUOTE:
  309. Assert(ch > 0xFF);
  310. if(iCharEncoding != MAPTOKWD_ANSI)
  311. {
  312. AssertSz(rgiszSpecial[ch - ENSPACE] != 0,
  313. "CRTFWrite::WriteText(): rgiszSpecial out-of-sync");
  314. PutCtrlWord(CWF_STR, rgiszSpecial[ch - ENSPACE]);
  315. }
  316. break;
  317. case TAB:
  318. #if TAB == CELL // If TABs and CELLs are equal, in
  319. if(_pPF->InTable()) // tables convert TABs to CELL value
  320. ch -= 2; // of 7 for rgiszTerminators[]
  321. #else
  322. case CELL:
  323. #endif
  324. case FF:
  325. case VT:
  326. PutCtrlWord(CWF_STR, rgiszTerminators[ch - (TAB - 2)]);
  327. break;
  328. case CR:
  329. {
  330. WCHAR ch1;
  331. WCHAR ch2;
  332. if(iCharEncoding == MAPTOKWD_ANSI)
  333. {
  334. char *pch = (char *)pv;
  335. ch1 = pch[1];
  336. ch2 = pch[2];
  337. }
  338. else
  339. {
  340. WCHAR *pch = (WCHAR *)pv;
  341. ch1 = pch[1];
  342. ch2 = pch[2];
  343. }
  344. if(cch > 1 && ch1 == CR && ch2 == LF)
  345. {
  346. // Translate CRCRLF to a blank (represents soft line break)
  347. PutChar(' ');
  348. cchRet = 2;
  349. break;
  350. }
  351. if(cch && ch1 == LF) // Ignore LF after CR
  352. {
  353. cchRet = 1;
  354. }
  355. if(_pPF->InTable()) // CR terminates a row in our simple
  356. { // table model, so output \row
  357. Puts(szEndRow, sizeof(szEndRow) - 1);
  358. _fCheckInTable = TRUE;
  359. break;
  360. }
  361. } // Fall thru to LF (EOP) case
  362. case LF:
  363. if (_ped->Get10Mode())
  364. Puts(szPar10, sizeof(szPar10) - 1);
  365. else
  366. Puts(szPar, sizeof(szPar) - 1);
  367. if(_fBullet)
  368. {
  369. if(cch > 0)
  370. {
  371. if(!_nNumber)
  372. printF(szBulletGroup, _symbolFont);
  373. else if(!_pPF->IsNumberSuppressed())
  374. {
  375. WCHAR szNumber[CCHMAXNUMTOSTR];
  376. _pPF->NumToStr(szNumber, ++_nNumber, fRtfWrite);
  377. printF(szBeginNumberGroup, _nFont);
  378. WritePcData(szNumber, _cpg, FALSE);
  379. printF(szEndNumberGroup);
  380. }
  381. }
  382. else
  383. _fBulletPending = TRUE;
  384. }
  385. break;
  386. case chOptionalHyphen:
  387. ch = '-'; // Fall thru to printFLiteral
  388. printFLiteral:
  389. case BSLASH:
  390. case LBRACE:
  391. case RBRACE:
  392. printF(szLiteralCharFmt, ch);
  393. break;
  394. case chNonBreakingSpace:
  395. ch = '~';
  396. goto printFLiteral;
  397. }
  398. return cchRet;
  399. }
  400. //======================== CRTFConverter Base Class ==================================
  401. /*
  402. * CRTFConverter::CRTFConverter()
  403. *
  404. * @mfunc
  405. * RTF Converter constructor
  406. */
  407. CRTFConverter::CRTFConverter(
  408. CTxtRange * prg, //@parm CTxtRange for transfer
  409. EDITSTREAM * pes, //@parm Edit stream for transfer
  410. DWORD dwFlags, //@parm Converter flags
  411. BOOL fRead) //@parm Initialization for a reader or writer
  412. {
  413. TRACEBEGIN(TRCSUBSYSRTFW, TRCSCOPEINTERN, "CRTFConverter::CRTFConverter");
  414. AssertSz(prg && pes && pes->pfnCallback,
  415. "CRTFWrite::CRTFWrite: Bad RichEdit");
  416. _prg = prg;
  417. _pes = pes;
  418. _ped = prg->GetPed();
  419. _dwFlags = dwFlags;
  420. _ecParseError = ecNoError;
  421. if(!_ctfi)
  422. ReadFontSubInfo();
  423. #if defined(DEBUG) && !defined(MACPORT)
  424. _hfileCapture = NULL;
  425. #if !defined(PEGASUS)
  426. if(GetProfileIntA("RICHEDIT DEBUG", "RTFCAPTURE", 0))
  427. {
  428. char szTempPath[MAX_PATH] = "\0";
  429. const char cszRTFReadCaptureFile[] = "CaptureRead.rtf";
  430. const char cszRTFWriteCaptureFile[] = "CaptureWrite.rtf";
  431. DWORD cchLength;
  432. SideAssert(cchLength = GetTempPathA(MAX_PATH, szTempPath));
  433. // append trailing backslash if neccessary
  434. if(szTempPath[cchLength - 1] != '\\')
  435. {
  436. szTempPath[cchLength] = '\\';
  437. szTempPath[cchLength + 1] = 0;
  438. }
  439. strcat(szTempPath, fRead ? cszRTFReadCaptureFile :
  440. cszRTFWriteCaptureFile);
  441. SideAssert(_hfileCapture = CreateFileA(szTempPath,
  442. GENERIC_WRITE,
  443. FILE_SHARE_READ,
  444. NULL,
  445. CREATE_ALWAYS,
  446. FILE_ATTRIBUTE_NORMAL,
  447. NULL));
  448. }
  449. #endif // !defined(PEGASUS)
  450. #endif // defined(DEBUG) && !defined(MACPORT)
  451. }
  452. //======================== OLESTREAM functions =======================================
  453. DWORD CALLBACK RTFPutToStream (
  454. RTFWRITEOLESTREAM * OLEStream, //@parm OLESTREAM
  455. const void * pvBuffer, //@parm Buffer to write
  456. DWORD cb) //@parm Bytes to write
  457. {
  458. return OLEStream->Writer->WriteData ((BYTE *)pvBuffer, cb);
  459. }
  460. //============================ CRTFWrite Class ==================================
  461. /*
  462. * CRTFWrite::CRTFWrite()
  463. *
  464. * @mfunc
  465. * RTF writer constructor
  466. */
  467. CRTFWrite::CRTFWrite(
  468. CTxtRange * prg, //@parm CTxtRange to write
  469. EDITSTREAM * pes, //@parm Edit stream to write to
  470. DWORD dwFlags) //@parm Write flags
  471. : CRTFConverter(prg, pes, dwFlags, FALSE)
  472. {
  473. TRACEBEGIN(TRCSUBSYSRTFW, TRCSCOPEINTERN, "CRTFWrite::CRTFWrite");
  474. ZeroMemory(&_CF, sizeof(CCharFormat)); // Setup "previous" CF with RTF
  475. _CF._dwEffects = CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR;// Font info is given
  476. _CF._yHeight = -32768; // by first font in range
  477. // [see end of LookupFont()]
  478. Assert(_ped);
  479. _ped->GetDefaultLCID(&_CF._lcid);
  480. // init OleStream
  481. RTFWriteOLEStream.Writer = this;
  482. RTFWriteOLEStream.lpstbl->Put = (DWORD (CALLBACK* )(LPOLESTREAM, const void FAR*, DWORD))
  483. RTFPutToStream;
  484. RTFWriteOLEStream.lpstbl->Get = NULL;
  485. _fIncludeObjects = TRUE;
  486. if((dwFlags & SF_RTFNOOBJS) == SF_RTFNOOBJS)
  487. _fIncludeObjects = FALSE;
  488. _fNCRForNonASCII = (dwFlags & SF_NCRFORNONASCII) != 0;
  489. _fNeedDelimeter = FALSE;
  490. _nHeadingStyle = 0; // No headings found
  491. _nNumber = 0; // No paragraph numbering yet
  492. _fCheckInTable = FALSE;
  493. _pPF = NULL;
  494. _pbAnsiBuffer = NULL;
  495. }
  496. /*
  497. * CRTFWrite::FlushBuffer()
  498. *
  499. * @mfunc
  500. * Flushes output buffer
  501. *
  502. * @rdesc
  503. * BOOL TRUE if successful
  504. */
  505. BOOL CRTFWrite::FlushBuffer()
  506. {
  507. TRACEBEGIN(TRCSUBSYSRTFW, TRCSCOPEINTERN, "CRTFWrite::FlushBuffer");
  508. LONG cchWritten;
  509. if (!_cchBufferOut)
  510. return TRUE;
  511. #ifdef DEBUG_PASTE
  512. if (FromTag(tagRTFAsText))
  513. {
  514. CHAR * pchEnd = &_pchRTFBuffer[_cchBufferOut];
  515. CHAR chT = *pchEnd;
  516. *pchEnd = 0;
  517. TraceString(_pchRTFBuffer);
  518. *pchEnd = chT;
  519. }
  520. #endif
  521. _pes->dwError = _pes->pfnCallback(_pes->dwCookie,
  522. (unsigned char *)_pchRTFBuffer,
  523. _cchBufferOut, &cchWritten);
  524. #if defined(DEBUG) && !defined(MACPORT) && !defined(PEGASUS)
  525. if(_hfileCapture)
  526. {
  527. DWORD cbLeftToWrite = _cchBufferOut;
  528. DWORD cbWritten2 = 0;
  529. BYTE *pbToWrite = (BYTE *)_pchRTFBuffer;
  530. while(WriteFile(_hfileCapture,
  531. pbToWrite,
  532. cbLeftToWrite,
  533. &cbWritten2,
  534. NULL) &&
  535. (pbToWrite += cbWritten2,
  536. (cbLeftToWrite -= cbWritten2)));
  537. }
  538. #endif
  539. if (_pes->dwError)
  540. {
  541. _ecParseError = ecPutCharFailed;
  542. return FALSE;
  543. }
  544. AssertSz(cchWritten == _cchBufferOut,
  545. "CRTFW::FlushBuffer: incomplete write");
  546. _cchOut += _cchBufferOut;
  547. _pchRTFEnd = _pchRTFBuffer; // Reset buffer
  548. _cchBufferOut = 0;
  549. return TRUE;
  550. }
  551. /*
  552. * CRTFWrite::PutChar(ch)
  553. *
  554. * @mfunc
  555. * Put out the character <p ch>
  556. *
  557. * @rdesc
  558. * BOOL TRUE if successful
  559. */
  560. BOOL CRTFWrite::PutChar(
  561. CHAR ch) //@parm char to be put
  562. {
  563. TRACEBEGIN(TRCSUBSYSRTFW, TRCSCOPEINTERN, "CRTFWrite::PutChar");
  564. CheckDelimeter(); // If _fNeedDelimeter, may need to
  565. // PutChar(' ')
  566. // Flush buffer if char won't fit
  567. if (_cchBufferOut + 1 >= cachBufferMost && !FlushBuffer())
  568. return FALSE;
  569. *_pchRTFEnd++ = ch; // Store character in buffer
  570. ++_cchBufferOut;
  571. return TRUE;
  572. }
  573. /*
  574. * CRTFWrite::CheckInTable(fPutIntbl)
  575. *
  576. * @mfunc
  577. * If _fCheckInTable or !fPutIntbl, output row header RTF. If fPutIntbl
  578. * and _fCheckInTable, output \intbl as well. Note that fPutIntbl is
  579. * FALSE when a PF is being output, since this control word needs to
  580. * be output after the \pard, but the other row RTF needs to be output
  581. * before the \pard.
  582. *
  583. * @rdesc
  584. * BOOL TRUE if in table and outputted all relevant \trowd stuff
  585. */
  586. BOOL CRTFWrite::CheckInTable(
  587. BOOL fPutIntbl) //@parm TRUE if \intbl should be output
  588. {
  589. TRACEBEGIN(TRCSUBSYSRTFW, TRCSCOPEINTERN, "CRTFWrite::CheckInTable");
  590. _fCheckInTable = FALSE;
  591. if(_pPF->InTable())
  592. {
  593. if(!_fRangeHasEOP)
  594. return TRUE;
  595. LONG cTab = _pPF->_bTabCount;
  596. LONG h = _pPF->_dxOffset;
  597. LONG i, j = _pPF->_dxStartIndent;
  598. LONG k = _pPF->_bAlignment;
  599. DWORD Tab, Widths;
  600. if (!PutCtrlWord(CWF_STR, i_trowd) || // Reset table properties
  601. h && !PutCtrlWord(CWF_VAL, i_trgaph, h) ||
  602. j && !PutCtrlWord(CWF_VAL, i_trleft, j) ||
  603. IN_RANGE(PFA_RIGHT, k, PFA_CENTER) &&
  604. !PutCtrlWord(CWF_STR, k == PFA_RIGHT ? i_trqr : i_trqc))
  605. {
  606. return FALSE;
  607. }
  608. PutBorders(TRUE);
  609. const LONG *prgxTabs = _pPF->GetTabs();
  610. for(i = 0; i < cTab; i++)
  611. {
  612. Tab = *prgxTabs++;
  613. Widths = Tab >> 24;
  614. if(Widths)
  615. {
  616. for(j = 0; j < 4; j++, Widths >>= 2)
  617. {
  618. LONG w = Widths & 3;
  619. if(w && (!PutCtrlWord(CWF_STR, rgiszBorders[j + 9]) ||
  620. !PutCtrlWord(CWF_VAL, i_brdrw, 15*w) ||
  621. !PutCtrlWord(CWF_STR, i_brdrs)))
  622. {
  623. return FALSE;
  624. }
  625. }
  626. CheckDelimeter();
  627. }
  628. if(!PutCtrlWord(CWF_VAL, i_cellx, GetTabPos(Tab)))
  629. return FALSE;
  630. }
  631. if(!fPutIntbl || PutCtrlWord(CWF_STR, i_intbl))
  632. return TRUE;
  633. }
  634. return FALSE;
  635. }
  636. /*
  637. * CRTFWrite::PutBorders(fInTable)
  638. *
  639. * @mfunc
  640. * If any borders are defined, output their control words
  641. *
  642. * @rdesc
  643. * error code
  644. */
  645. EC CRTFWrite::PutBorders(
  646. BOOL fInTable)
  647. {
  648. DWORD Widths = _pPF->_wBorderWidth;
  649. BOOL fBox = _pPF->_wEffects & PFE_BOX;
  650. if(Widths || fBox)
  651. {
  652. DWORD Colors = _pPF->_dwBorderColor;
  653. DWORD dwEffects = Colors >> 20;
  654. LONG i = 1, iMax = 4; // NonBox for loop limits
  655. LONG j, k;
  656. DWORD Spaces = _pPF->_wBorderSpace;
  657. DWORD Styles = _pPF->_wBorders;
  658. if(fBox)
  659. i = iMax = 0; // For box, only write one set
  660. for( ; i <= iMax; i++, Spaces >>= 4, Styles >>= 4, Widths >>= 4, Colors >>= 5)
  661. {
  662. if(!(Widths & 0xF) && !fBox) // No width, so no border
  663. continue;
  664. j = TWIPS_PER_POINT*(Spaces & 0xF);
  665. k = Colors & 0x1F;
  666. if (!PutCtrlWord(CWF_STR, rgiszBorders[i + 4*fInTable]) ||
  667. !PutCtrlWord(CWF_STR, rgiszBorderStyles[Styles & 0xF]) ||
  668. !PutCtrlWord(CWF_VAL, i_brdrw, 10*(Widths & 0xF)) ||
  669. k &&
  670. !PutCtrlWord(CWF_VAL, i_brdrcf, LookupColor(g_Colors[k-1]) + 1) ||
  671. j && !PutCtrlWord(CWF_VAL, i_brsp, j))
  672. {
  673. break;
  674. }
  675. for(j = 3; j--; dwEffects >>= 1) // Output border effects
  676. {
  677. if (dwEffects & 1 &&
  678. !PutCtrlWord(CWF_STR, rgiszBorderEffects[j]))
  679. {
  680. break;
  681. }
  682. }
  683. CheckDelimeter(); // Output a ' '
  684. }
  685. }
  686. return _ecParseError;
  687. }
  688. /*
  689. * CRTFWrite::Puts(sz, cb)
  690. *
  691. * @mfunc
  692. * Put out the string <p sz>
  693. *
  694. * @rdesc
  695. * BOOL TRUE if successful
  696. */
  697. BOOL CRTFWrite::Puts(
  698. CHAR const * sz,
  699. LONG cb) //@parm String to be put
  700. {
  701. TRACEBEGIN(TRCSUBSYSRTFW, TRCSCOPEINTERN, "CRTFWrite::Puts");
  702. if(*sz == '\\' || *sz == '{' || *sz == ' ')
  703. _fNeedDelimeter = FALSE;
  704. CheckDelimeter(); // If _fNeedDelimeter, may need to
  705. // PutChar(' ')
  706. // Flush buffer if string won't fit
  707. if (_cchBufferOut + cb >= cachBufferMost && !FlushBuffer())
  708. return FALSE;
  709. if (cb >= cachBufferMost) // If buffer still can't handle string,
  710. { // we have to write string directly
  711. LONG cbWritten;
  712. #ifdef DEBUG_PASTE
  713. if (FromTag(tagRTFAsText))
  714. TraceString(sz);
  715. #endif
  716. _pes->dwError = _pes->pfnCallback(_pes->dwCookie,
  717. (LPBYTE) sz, cb, &cbWritten);
  718. _cchOut += cbWritten;
  719. #if defined(DEBUG) && !defined(MACPORT) && !defined(PEGASUS)
  720. if(_hfileCapture)
  721. {
  722. DWORD cbLeftToWrite = cb;
  723. DWORD cbWritten2 = 0;
  724. BYTE *pbToWrite = (BYTE *)sz;
  725. while(WriteFile(_hfileCapture,
  726. pbToWrite,
  727. cbLeftToWrite,
  728. &cbWritten2,
  729. NULL) &&
  730. (pbToWrite += cbWritten2,
  731. (cbLeftToWrite -= cbWritten2)));
  732. }
  733. #endif
  734. if (_pes->dwError)
  735. {
  736. _ecParseError = ecPutCharFailed;
  737. return FALSE;
  738. }
  739. AssertSz(cbWritten == cb,
  740. "CRTFW::Puts: incomplete write");
  741. }
  742. else
  743. {
  744. CopyMemory(_pchRTFEnd, sz, cb); // Put string into buffer for later
  745. _pchRTFEnd += cb; // output
  746. _cchBufferOut += cb;
  747. }
  748. return TRUE;
  749. }
  750. /*
  751. * CRTFWrite::PutCtrlWord(iFormat, iCtrl, iValue)
  752. *
  753. * @mfunc
  754. * Put control word with rgKeyword[] index <p iCtrl> and value <p iValue>
  755. * using format rgszCtrlWordFormat[<p iFormat>]
  756. *
  757. * @rdesc
  758. * TRUE if successful
  759. *
  760. * @devnote
  761. * Sets _fNeedDelimeter to flag that next char output must be a control
  762. * word delimeter, i.e., not alphanumeric (see PutChar()).
  763. */
  764. BOOL CRTFWrite::PutCtrlWord(
  765. LONG iFormat, //@parm Format index into rgszCtrlWordFormat
  766. LONG iCtrl, //@parm Index into Keyword array
  767. LONG iValue) //@parm Control-word parameter value. If missing,
  768. { // 0 is assumed
  769. TRACEBEGIN(TRCSUBSYSRTFW, TRCSCOPEINTERN, "CRTFWrite::PutCtrlWord");
  770. BOOL bRet;
  771. CHAR szT[60];
  772. LONG cb;
  773. cb = sprintf(szT,
  774. (char *)rgszCtrlWordFormat[iFormat],
  775. rgKeyword[iCtrl].szKeyword,
  776. iValue);
  777. _fNeedDelimeter = FALSE;
  778. bRet = Puts(szT, cb);
  779. _fNeedDelimeter = TRUE; // Ensure next char isn't
  780. // alphanumeric
  781. return bRet;
  782. }
  783. /*
  784. * CRTFWrite::printF(szFmt, ...)
  785. *
  786. * @mfunc
  787. * Provide formatted output
  788. *
  789. * @rdesc
  790. * TRUE if successful
  791. */
  792. BOOL _cdecl CRTFWrite::printF(
  793. CONST CHAR * szFmt, //@parm Format string for printf()
  794. ...) //@parmvar Parameter list
  795. {
  796. TRACEBEGIN(TRCSUBSYSRTFW, TRCSCOPEINTERN, "CRTFWrite::printF");
  797. va_list marker;
  798. CHAR szT[60];
  799. va_start(marker, szFmt);
  800. int cb = W32->WvsprintfA(60, szT, szFmt, marker);
  801. va_end(marker);
  802. return Puts(szT, cb);
  803. }
  804. /*
  805. * CRTFWrite::WritePcData(szData, nCodePage, fIsDBCS)
  806. *
  807. * @mfunc
  808. * Write out the string <p szData> as #PCDATA where any special chars
  809. * are protected by leading '\\'.
  810. *
  811. * @rdesc
  812. * EC (_ecParseError)
  813. */
  814. EC CRTFWrite::WritePcData(
  815. const TCHAR * szData, //@parm #PCDATA string to write
  816. INT nCodePage, //@parm code page default value CP_ACP
  817. BOOL fIsDBCS) //@parm szData is a DBCS string stuffed into Unicode buffer
  818. {
  819. TRACEBEGIN(TRCSUBSYSRTFW, TRCSCOPEINTERN, "CRTFWrite::WritePcData");
  820. BYTE ch;
  821. BOOL fMissingCodePage;
  822. BOOL fMultiByte;
  823. const BYTE *pch;
  824. const char *pchToDBCSDefault = NULL;
  825. BOOL * pfUsedDefault = NULL;
  826. if(IsUTF8)
  827. nCodePage = CP_UTF8;
  828. if(!*szData)
  829. return _ecParseError;
  830. int DataSize = wcslen(szData) + 1;
  831. int BufferSize = DataSize * 3;
  832. char *pBuffer = (char *)PvAlloc(BufferSize, GMEM_ZEROINIT);
  833. if(!pBuffer)
  834. return ecNoMemory;
  835. #if defined(DEBUG) || defined(_RELEASE_ASSERTS_)
  836. // When WCTMB fails to convert a char, the following default
  837. // char is used as a placeholder in the string being converted
  838. const char chToDBCSDefault = 0;
  839. BOOL fUsedDefault;
  840. pchToDBCSDefault = &chToDBCSDefault;
  841. pfUsedDefault = &fUsedDefault;
  842. #endif
  843. int cchRet = WCTMB(fIsDBCS ? INVALID_CODEPAGE : nCodePage, 0,
  844. szData, -1, pBuffer, BufferSize,
  845. pchToDBCSDefault, pfUsedDefault,
  846. &fMissingCodePage);
  847. Assert(cchRet > 0);
  848. if(!fIsDBCS && fMissingCodePage && nCodePage != CP_ACP)
  849. {
  850. // Here, the system could not convert the Unicode string because the
  851. // code page is not installed on the system. Fallback to CP_ACP.
  852. cchRet = WCTMB(CP_ACP, 0,
  853. szData, -1, pBuffer, BufferSize,
  854. pchToDBCSDefault, pfUsedDefault,
  855. &fMissingCodePage);
  856. Assert(cchRet > 0);
  857. nCodePage = CP_ACP;
  858. }
  859. AssertSz(!fUsedDefault, "CRTFWrite::WritePcData(): Found character in "
  860. "control text which cannot be converted from "
  861. "Unicode");
  862. if(cchRet <= 0)
  863. {
  864. _ecParseError = ecCantUnicode;
  865. goto CleanUp;
  866. }
  867. BufferSize = cchRet;
  868. fMultiByte = (BufferSize > DataSize) || fIsDBCS || fMissingCodePage;
  869. pch = (BYTE *)pBuffer;
  870. ch = *pch;
  871. // If _fNeedDelimeter, may need to PutChar(' ')
  872. CheckDelimeter();
  873. while (!_ecParseError && (ch = *pch++))
  874. {
  875. if(fMultiByte && *pch && nCodePage != CP_UTF8 && GetTrailBytesCount(ch, nCodePage))
  876. printF(szEscape2CharFmt, ch, *pch++); // Output DBC pair
  877. else
  878. {
  879. if(ch == LBRACE || ch == RBRACE || ch == BSLASH)
  880. printF(szLiteralCharFmt, ch);
  881. else if(ch < 32 || ch == ';' || ch > 127)
  882. printF(szEscapeCharFmt, ch);
  883. else
  884. PutChar(ch);
  885. }
  886. }
  887. CleanUp:
  888. FreePv(pBuffer);
  889. return _ecParseError;
  890. }
  891. /*
  892. * CRTFWrite::LookupColor(colorref)
  893. *
  894. * @mfunc
  895. * Return color-table index for color referred to by <p colorref>.
  896. * If a match isn't found, an entry is added.
  897. *
  898. * @rdesc
  899. * LONG Index into colortable
  900. * <lt> 0 on error
  901. */
  902. LONG CRTFWrite::LookupColor(
  903. COLORREF colorref) //@parm colorref to look for
  904. {
  905. TRACEBEGIN(TRCSUBSYSRTFW, TRCSCOPEINTERN, "CRTFWrite::LookupColor");
  906. LONG Count = _colors.Count();
  907. LONG iclrf;
  908. COLORREF * pclrf;
  909. for(iclrf = 0; iclrf < Count; iclrf++) // Look for color
  910. if(_colors.GetAt(iclrf) == colorref)
  911. return iclrf;
  912. pclrf = _colors.Add(1, NULL); // If we couldn't find it,
  913. if(!pclrf) // add it to color table
  914. return -1;
  915. *pclrf = colorref;
  916. return iclrf;
  917. }
  918. /*
  919. * CRTFWrite::LookupFont(pCF)
  920. *
  921. * @mfunc
  922. * Returns index into font table for font referred to by
  923. * CCharFormat *<p pCF>. If a match isn't found, an entry is added.
  924. *
  925. * @rdesc
  926. * SHORT Index into fonttable
  927. * <lt> 0 on error
  928. */
  929. LONG CRTFWrite::LookupFont(
  930. CCharFormat const * pCF) //@parm CCharFormat holding font name
  931. { // to look up
  932. TRACEBEGIN(TRCSUBSYSRTFW, TRCSCOPEINTERN, "CRTFWrite::LookupFont");
  933. LONG Count = _fonts.Count();
  934. LONG itf;
  935. TEXTFONT * ptf;
  936. for(itf = 0; itf < Count; itf++)
  937. { // Look for font
  938. ptf = _fonts.Elem(itf);
  939. if (ptf->bPitchAndFamily == pCF->_bPitchAndFamily &&// of same pitch,
  940. ptf->bCharSet == pCF->_bCharSet && // char set, and
  941. ptf->iFont == pCF->_iFont) // name
  942. {
  943. return itf; // Found it
  944. }
  945. }
  946. ptf = _fonts.Add(1, NULL); // Didn't find it:
  947. if(!ptf) // add to table
  948. return -1;
  949. ptf->bPitchAndFamily = pCF->_bPitchAndFamily;
  950. ptf->bCharSet = pCF->_bCharSet;
  951. ptf->sCodePage = (short)GetCodePage (ptf->bCharSet);
  952. ptf->iFont = pCF->_iFont;
  953. ptf->fNameIsDBCS = (pCF->_dwEffects & CFE_FACENAMEISDBCS) != 0;
  954. #if 0
  955. // Bug1523 - (BradO) I removed this section of code so that a /fN tag is always
  956. // emitted for the first run of text. In theory, we should be able to
  957. // assume that the first run of text would carry the default font.
  958. // It turns out that when reading RTF, Word doesn't use anything predictable
  959. // for the font of the first run of text in the absence of an explicit /fN,
  960. // thus, we have to explicitly emit a /fN tag for the first run of text.
  961. if(!Count) // 0th font is
  962. { // default \deff0
  963. _CF.bPitchAndFamily = pCF->bPitchAndFamily; // Set "previous"
  964. _CF.bCharSet = pCF->bCharSet; // CF accordingly
  965. wcscpy(_CF.szFaceName, pCF->szFaceName);
  966. }
  967. #endif
  968. return itf;
  969. }
  970. /*
  971. * CRTFWrite::BuildTables(rpCF, rpPF, cch, fNameIsDBCS)
  972. *
  973. * @mfunc
  974. * Build font and color tables for write range of length <p cch>
  975. *
  976. * @rdesc
  977. * EC The error code
  978. */
  979. EC CRTFWrite::BuildTables(
  980. CFormatRunPtr& rpCF, //@parm CF run ptr for start of write range
  981. CFormatRunPtr& rpPF, //@parm PF run ptr for start of write range
  982. LONG cch, //@parm # chars in write range
  983. BOOL& fNameIsDBCS) //@parm OUT =TRUE if there is any CFE_FACENAMEISDBCS Run in selection.
  984. {
  985. TRACEBEGIN(TRCSUBSYSRTFW, TRCSCOPEINTERN, "CRTFWrite::BuildTables");
  986. LONG i;
  987. LONG ifmt = 0;
  988. const CCharFormat * pCF = NULL;
  989. const CParaFormat * pPF = NULL;
  990. CFormatRunPtr rp(rpCF);
  991. CFormatRunPtr rpPFtemp(rpPF);
  992. LONG cchTotal = cch;
  993. fNameIsDBCS = FALSE;
  994. while(cch > 0)
  995. {
  996. ifmt = rp.GetFormat(); // _iFormat for next CF run
  997. pCF = _ped->GetCharFormat(ifmt);
  998. Assert(pCF);
  999. if (pCF->_dwEffects & CFE_FACENAMEISDBCS)
  1000. fNameIsDBCS = TRUE;
  1001. // Look up character-format *pCF's font and color. If either isn't
  1002. // found, it is added to appropriate table. Don't lookup color
  1003. // for CCharFormats with auto-color
  1004. if (LookupFont(pCF) < 0 ||
  1005. (!(pCF->_dwEffects & CFE_AUTOCOLOR) &&
  1006. LookupColor(pCF->_crTextColor) < 0) ||
  1007. (!(pCF->_dwEffects & CFE_AUTOBACKCOLOR) &&
  1008. LookupColor(pCF->_crBackColor) < 0))
  1009. {
  1010. break;
  1011. }
  1012. if(!rp.IsValid())
  1013. break;
  1014. cch -= rp.GetCchLeft();
  1015. rp.NextRun();
  1016. }
  1017. // now look for bullets; if found, then we need to include
  1018. // the "Symbol" font
  1019. cch = cchTotal;
  1020. _symbolFont = 0;
  1021. while( cch > 0 )
  1022. {
  1023. ifmt = rpPFtemp.GetFormat();
  1024. pPF = _ped->GetParaFormat(ifmt);
  1025. if(!pPF)
  1026. goto CacheError;
  1027. if(pPF->_wNumbering == PFN_BULLET && !_symbolFont)
  1028. {
  1029. CCharFormat CF;
  1030. // Be sure these choices agree with those in CMeasurer::GetCcsBullet()
  1031. // and that LookupFont() doesn't access any other CF members.
  1032. CF._iFont = IFONT_SYMBOL;
  1033. CF._bCharSet = SYMBOL_CHARSET;
  1034. CF._bPitchAndFamily = FF_DONTCARE;
  1035. // Save Font index for Symbol. Reset it to 0 if LookupFont
  1036. // returns error.
  1037. _symbolFont = LookupFont(&CF);
  1038. _symbolFont = max(_symbolFont, 0);
  1039. // We don't need to bother looking for more bullets, since
  1040. // in RichEdit 2.0, all bullets either have the same font or
  1041. // have their formatting information in the character format
  1042. // for the EOP mark.
  1043. break;
  1044. }
  1045. WORD Widths = pPF->_wBorderWidth;
  1046. DWORD Colors = pPF->_dwBorderColor & 0xFFFFF;
  1047. while(Widths && Colors)
  1048. {
  1049. i = Colors & 0x1F;
  1050. if(i && (Widths & 0xF))
  1051. LookupColor(g_Colors[i - 1]);
  1052. Widths >>= 4;
  1053. Colors >>= 5;
  1054. }
  1055. i = (pPF->_wShadingStyle >> 6) & 31; // Shading forecolor
  1056. if(i)
  1057. LookupColor(g_Colors[i - 1]);
  1058. i = pPF->_wShadingStyle >> 11; // Shading backcolor
  1059. if(i)
  1060. LookupColor(g_Colors[i - 1]);
  1061. if(IsHeadingStyle(pPF->_sStyle) && pPF->_sStyle < _nHeadingStyle)
  1062. _nHeadingStyle = pPF->_sStyle;
  1063. if(!rpPFtemp.IsValid())
  1064. break;
  1065. cch -= rpPFtemp.GetCchLeft();
  1066. rpPFtemp.NextRun();
  1067. }
  1068. return _ecParseError;
  1069. CacheError:
  1070. _ecParseError = ecFormatCache;
  1071. return ecFormatCache; // Access to CF/PF cache failed
  1072. }
  1073. /*
  1074. * CRTFWrite::WriteFontTable()
  1075. *
  1076. * @mfunc
  1077. * Write out font table
  1078. *
  1079. * @rdesc
  1080. * EC The error code
  1081. */
  1082. EC CRTFWrite::WriteFontTable()
  1083. {
  1084. TRACEBEGIN(TRCSUBSYSRTFW, TRCSCOPEINTERN, "CRTFWrite::WriteFontTable");
  1085. LONG Count = _fonts.Count();
  1086. int itf;
  1087. int m;
  1088. int pitch;
  1089. TEXTFONT * ptf;
  1090. char * szFamily;
  1091. const TCHAR * szName;
  1092. TCHAR * szTaggedName;
  1093. if(!Count || !PutCtrlWord(CWF_GRP, i_fonttbl)) // Start font table group
  1094. goto CleanUp;
  1095. for (itf = 0; itf < Count; itf++)
  1096. {
  1097. ptf = _fonts.Elem(itf);
  1098. // if (ptf->sCodePage)
  1099. // if (! PutCtrlWord(CWF_VAL, i_cpg, ptf->sCodePage ) )
  1100. // goto CleanUp;
  1101. // Define font family
  1102. m = ptf->bPitchAndFamily >> 4;
  1103. szFamily = rgKeyword[rgiszFamily[m < CFAMILIES ? m : 0]].szKeyword;
  1104. szName = GetFontName(ptf->iFont);
  1105. szTaggedName = NULL;
  1106. // Check to see if this is a tagged font
  1107. if (!ptf->bCharSet ||
  1108. !FindTaggedFont(szName, ptf->bCharSet, &szTaggedName))
  1109. {
  1110. szTaggedName = NULL;
  1111. }
  1112. pitch = ptf->bPitchAndFamily & 0xF; // Write font
  1113. if (!printF(szBeginFontEntryFmt, itf, szFamily)) // entry, family,
  1114. goto CleanUp;
  1115. _fNeedDelimeter = TRUE;
  1116. if (pitch && !PutCtrlWord(CWF_VAL, i_fprq, pitch)) // and pitch
  1117. goto CleanUp;
  1118. if(!ptf->sCodePage && ptf->bCharSet)
  1119. ptf->sCodePage = (short)GetCodePage(ptf->bCharSet);
  1120. // Write charset. Win32 uses ANSI_CHARSET to mean the default Windows
  1121. // character set, so find out what it really is
  1122. extern BYTE bCharSetANSI;
  1123. if(ptf->bCharSet != DEFAULT_CHARSET)
  1124. {
  1125. BYTE bCharSet = ptf->bCharSet;
  1126. BOOL fWroteCharSet = TRUE;
  1127. if(ptf->bCharSet == PC437_CHARSET || IsPrivateCharSet(bCharSet))
  1128. {
  1129. fWroteCharSet = FALSE;
  1130. bCharSet = ANSI_CHARSET;
  1131. }
  1132. if(!PutCtrlWord(CWF_VAL, i_fcharset, bCharSet))
  1133. goto CleanUp;
  1134. // Skip \cpgN output if we've already output a \fcharsetN tag.
  1135. // This is to accomodate RE 1.0, which can't handle some \cpgN
  1136. // tags properly. Specifically, when RE 1.0 parses the \cpgN tag
  1137. // it looks up the corresponding charset value. Turns out its
  1138. // codepage/charset table is incomplete so it maps some codepages
  1139. // to charset 0, trouncing the previously read \fcharsetN value.
  1140. if (fWroteCharSet)
  1141. goto WroteCharSet;
  1142. }
  1143. if(ptf->sCodePage && !PutCtrlWord(CWF_VAL, i_cpg, ptf->sCodePage))
  1144. goto CleanUp;
  1145. WroteCharSet:
  1146. if(szTaggedName)
  1147. {
  1148. // Have a tagged font: write out group with real name followed by tagged name
  1149. if(!PutCtrlWord(CWF_AST, i_fname) ||
  1150. WritePcData(szName, ptf->sCodePage, ptf->fNameIsDBCS) ||
  1151. !Puts(szEndFontEntry, sizeof(szEndFontEntry) - 1) ||
  1152. WritePcData(szTaggedName, ptf->sCodePage, ptf->fNameIsDBCS) ||
  1153. !Puts(szEndFontEntry, sizeof(szEndFontEntry) - 1))
  1154. {
  1155. goto CleanUp;
  1156. }
  1157. }
  1158. else if(WritePcData(szName, ptf->sCodePage, ptf->fNameIsDBCS) ||
  1159. !Puts(szEndFontEntry, sizeof(szEndFontEntry) - 1))
  1160. // If non-tagged font just write name out
  1161. {
  1162. goto CleanUp;
  1163. }
  1164. }
  1165. Puts(szEndGroupCRLF, sizeof(szEndGroupCRLF) - 1); // End font table group
  1166. CleanUp:
  1167. return _ecParseError;
  1168. }
  1169. /*
  1170. * CRTFWrite::WriteColorTable()
  1171. *
  1172. * @mfunc
  1173. * Write out color table
  1174. *
  1175. * @rdesc
  1176. * EC The error code
  1177. */
  1178. EC CRTFWrite::WriteColorTable()
  1179. {
  1180. TRACEBEGIN(TRCSUBSYSRTFW, TRCSCOPEINTERN, "CRTFWrite::WriteColorTable");
  1181. LONG Count = _colors.Count();
  1182. COLORREF clrf;
  1183. LONG iclrf;
  1184. if (!Count || !PutCtrlWord(CWF_GRP, i_colortbl) // Start color table group
  1185. || !PutChar(';')) // with null first entry
  1186. {
  1187. goto CleanUp;
  1188. }
  1189. for(iclrf = 0; iclrf < Count; iclrf++)
  1190. {
  1191. clrf = _colors.GetAt(iclrf);
  1192. if (!printF(szColorEntryFmt,
  1193. GetRValue(clrf), GetGValue(clrf), GetBValue(clrf)))
  1194. goto CleanUp;
  1195. }
  1196. Puts(szEndGroupCRLF,sizeof(szEndGroupCRLF) -1); // End color table group
  1197. CleanUp:
  1198. return _ecParseError;
  1199. }
  1200. /*
  1201. * CRTFWrite::WriteCharFormat(pCF)
  1202. *
  1203. * @mfunc
  1204. * Write deltas between CCharFormat <p pCF> and the previous CCharFormat
  1205. * given by _CF, and then set _CF = *<p pCF>.
  1206. *
  1207. * @rdesc
  1208. * EC The error code
  1209. *
  1210. * @devnote
  1211. * For optimal output, could write \\plain and use deltas relative to
  1212. * \\plain if this results in less output (typically only one change
  1213. * is made when CF changes, so less output results when compared to
  1214. * previous CF than when compared to \\plain).
  1215. */
  1216. EC CRTFWrite::WriteCharFormat(
  1217. const CCharFormat * pCF) //@parm Ptr to CCharFormat
  1218. {
  1219. TRACEBEGIN(TRCSUBSYSRTFW, TRCSCOPEINTERN, "CRTFWrite::WriteCharFormat");
  1220. DWORD dwEffects = pCF->_dwEffects; // Current effects
  1221. DWORD dwChanges = _CF._dwEffects; // Previous effects (will be
  1222. LONG i; // changed between them)
  1223. LONG iFormat;
  1224. LONG iValue; // Control-word value
  1225. LONG i_sz; // Temp ctrl string index
  1226. LONG yOffset = pCF->_yOffset;
  1227. DWORD UType1 = _CF._bUnderlineType; // Previous underline type
  1228. if(UType1 >= CUNDERLINES) // Special underlines are
  1229. dwChanges &= ~CFE_UNDERLINE; // not written out, so
  1230. // claim they're not on
  1231. DWORD UType2 = pCF->_bUnderlineType; // Current underline type
  1232. if(UType2 >= CUNDERLINES) // _bUnderlineType for
  1233. dwEffects &= ~CFE_UNDERLINE; // specials has non0
  1234. // high nibble
  1235. dwChanges ^= dwEffects; // Now dwChanges is the
  1236. // diff between effects
  1237. if (dwChanges & CFE_AUTOCOLOR || // Change in autocolor
  1238. pCF->_crTextColor != _CF._crTextColor) // or text color
  1239. {
  1240. iValue = 0; // Default autocolor
  1241. if(!(dwEffects & CFE_AUTOCOLOR)) // Make that text color
  1242. iValue = LookupColor(pCF->_crTextColor) + 1;
  1243. if(!PutCtrlWord(CWF_VAL, i_cf, iValue))
  1244. goto CleanUp;
  1245. }
  1246. if (dwChanges & CFE_AUTOBACKCOLOR || // Change in autobackcolor
  1247. pCF->_crBackColor != _CF._crBackColor) // or backcolor
  1248. {
  1249. iValue = 0; // Default autobackcolor
  1250. if(!(dwEffects & CFE_AUTOBACKCOLOR)) // Make that back color
  1251. iValue = LookupColor(pCF->_crBackColor) + 1;
  1252. if(!PutCtrlWord(CWF_VAL, i_highlight, iValue))
  1253. goto CleanUp;
  1254. }
  1255. if (pCF->_lcid != _CF._lcid &&
  1256. !PutCtrlWord(CWF_VAL, i_lang, LANGIDFROMLCID((WORD)pCF->_lcid)) ||
  1257. pCF->_sSpacing != _CF._sSpacing &&
  1258. !PutCtrlWord(CWF_VAL, i_expndtw, pCF->_sSpacing) ||
  1259. /* FUTURE (alexgo): This code is incorrect and we don't
  1260. yet handle the Style table. We may want to support this
  1261. better in a future version.
  1262. pCF->_sStyle != _CF._sStyle && pCF->_sStyle > 0 &&
  1263. !PutCtrlWord(CWF_VAL, i_cs, pCF->_sStyle) || */
  1264. pCF->_bAnimation != _CF._bAnimation &&
  1265. !PutCtrlWord(CWF_VAL, i_animtext, pCF->_bAnimation) ||
  1266. /* FUTURE (alexgo): this code doesn't work yet, as we don't
  1267. output the revision table. We may want to support this
  1268. better in a future version
  1269. pCF->_bRevAuthor!= _CF._bRevAuthor &&
  1270. !PutCtrlWord(CWF_VAL, i_revauth, pCF->_bRevAuthor) || */
  1271. pCF->_wKerning != _CF._wKerning &&
  1272. !PutCtrlWord(CWF_VAL, i_kerning, pCF->_wKerning/10) )
  1273. {
  1274. goto CleanUp;
  1275. }
  1276. // Handle all underline types. Special underline types (nonzero high
  1277. // nibble in CCharFormat::_bUnderlineType) are considered to be no
  1278. // underline and have their UType set equal to 0 above and underline
  1279. // effect bits reset to 0.
  1280. if ((dwChanges & CFM_UNDERLINE) ||
  1281. (dwEffects & CFE_UNDERLINE) && UType1 != UType2)
  1282. {
  1283. dwChanges &= ~CFE_UNDERLINE; // Suppress underline
  1284. i = dwEffects & CFE_UNDERLINE ? UType2: 0; // action in next for()
  1285. if(!PutCtrlWord(CWF_STR, rgiszUnderlines[i]))
  1286. goto CleanUp;
  1287. }
  1288. // This must be before next stuff
  1289. if(dwChanges & (CFM_SUBSCRIPT | CFM_SUPERSCRIPT))// change in sub/sup
  1290. { // status
  1291. i_sz = dwEffects & CFE_SUPERSCRIPT ? i_super
  1292. : dwEffects & CFE_SUBSCRIPT ? i_sub
  1293. : i_nosupersub;
  1294. if(!PutCtrlWord(CWF_STR, i_sz))
  1295. goto CleanUp;
  1296. }
  1297. if(dwChanges & CFE_DELETED) // Insert deleted at high
  1298. { // end of bit string
  1299. dwChanges |= CFE_REVISED << 1;
  1300. if(dwEffects & CFE_DELETED)
  1301. dwEffects |= CFE_REVISED << 1;
  1302. }
  1303. dwChanges &= ((1 << CEFFECTS) - 1) & ~CFE_LINK; // Output keywords for
  1304. for(i = CEFFECTS; // effects that changed
  1305. dwChanges && i--; // rgszEffects[] contains
  1306. dwChanges >>= 1, dwEffects >>= 1) // effect keywords in
  1307. { // order max CFE_xx to
  1308. if(dwChanges & 1) // min CFE-xx
  1309. { // Change from last call
  1310. iValue = dwEffects & 1; // If effect is off, write
  1311. iFormat = iValue ? CWF_STR : CWF_VAL; // a 0; else no value
  1312. if(!PutCtrlWord(iFormat,
  1313. rgiszEffects[i], iValue))
  1314. goto CleanUp;
  1315. }
  1316. }
  1317. if(yOffset != _CF._yOffset) // Change in base line
  1318. { // position
  1319. yOffset /= 10; // Default going to up
  1320. i_sz = i_up;
  1321. iFormat = CWF_VAL;
  1322. if(yOffset < 0) // Make that down
  1323. {
  1324. i_sz = i_dn;
  1325. yOffset = -yOffset;
  1326. }
  1327. if(!PutCtrlWord(iFormat, i_sz, yOffset))
  1328. goto CleanUp;
  1329. }
  1330. if (pCF->_bPitchAndFamily != _CF._bPitchAndFamily || // Change in font
  1331. pCF->_bCharSet != _CF._bCharSet ||
  1332. pCF->_iFont != _CF._iFont)
  1333. {
  1334. iValue = LookupFont(pCF);
  1335. if(iValue < 0 || !PutCtrlWord(CWF_VAL, i_f, iValue))
  1336. goto CleanUp;
  1337. // RichEdit encodes the current direction in bCharSet, but Word likes
  1338. // to know explicitly, so output the appropriate choice of \rtlch or
  1339. // \ltrch if the direction changes
  1340. BOOL fRTLCharSet = IsRTLCharSet(pCF->_bCharSet);
  1341. if (fRTLCharSet != IsRTLCharSet(_CF._bCharSet) &&
  1342. !PutCtrlWord(CWF_STR, fRTLCharSet ? i_rtlch : i_ltrch))
  1343. {
  1344. goto CleanUp;
  1345. }
  1346. }
  1347. if (pCF->_yHeight != _CF._yHeight) // Change in font size
  1348. {
  1349. iValue = (pCF->_yHeight + (pCF->_yHeight > 0 ? 5 : -5))/10;
  1350. if(!PutCtrlWord(CWF_VAL, i_fs, iValue))
  1351. goto CleanUp;
  1352. }
  1353. _CF = *pCF; // Update previous CCharFormat
  1354. CleanUp:
  1355. return _ecParseError;
  1356. }
  1357. /*
  1358. * CRTFWrite::WriteParaFormat(prtp)
  1359. *
  1360. * @mfunc
  1361. * Write out attributes specified by the CParaFormat <p pPF> relative
  1362. * to para defaults (probably produces smaller output than relative to
  1363. * previous para format and let's you redefine tabs -- no RTF kill
  1364. * tab command except \\pard)
  1365. *
  1366. * @rdesc
  1367. * EC The error code
  1368. */
  1369. EC CRTFWrite::WriteParaFormat(
  1370. const CRchTxtPtr * prtp) //@parm Ptr to rich-text ptr at current cp
  1371. {
  1372. TRACEBEGIN(TRCSUBSYSRTFW, TRCSCOPEINTERN, "CRTFWrite::WriteParaFormat");
  1373. Assert(_ped);
  1374. //if(!_fRangeHasEOP) // Don't write para info if
  1375. // return _ecParseError; // range has no EOPs
  1376. const CParaFormat * pPFPrev = _pPF;
  1377. const CParaFormat * pPF = _pPF = prtp->GetPF();
  1378. BOOL fInTable = pPF->InTable();
  1379. LONG c; // Temporary count
  1380. LONG cTab = pPF->_bTabCount;
  1381. DWORD dwEffects;
  1382. DWORD dwRule = pPF->_bLineSpacingRule;
  1383. LONG dy = pPF->_dyLineSpacing;
  1384. LONG i_t, i, j, k;
  1385. LONG tabAlign, tabLead, tabPos;
  1386. LONG lDocDefaultTab = _ped->GetDefaultTab();
  1387. const LONG *prgxTabs = NULL;
  1388. if(!lDocDefaultTab)
  1389. lDocDefaultTab = lDefaultTab;
  1390. CheckInTable(FALSE);
  1391. if(fInTable)
  1392. cTab = 0; // Suppress \tab output
  1393. AssertSz(cTab >= 0 && cTab <= MAX_TAB_STOPS,
  1394. "CRTFW::WriteParaFormat: illegal cTabCount");
  1395. // EVIL HACK ALERT! - Exchange's IMC keys on the \protect tag when it does
  1396. // its reply-ticking for mail being sent to Internet recipients.
  1397. // Paragraphs following a \pard and containing a \protect tag are
  1398. // reply-ticked, so we must ensure that each \pard in a protected range
  1399. // is followed by a \protect tag.
  1400. if (_CF._dwEffects & CFE_PROTECTED && !PutCtrlWord(CWF_VAL, i_protect, 0) ||
  1401. !PutCtrlWord(CWF_STR, i_pard) || // Reset para attributes
  1402. _CF._dwEffects & CFE_PROTECTED && !PutCtrlWord(CWF_STR, i_protect))
  1403. {
  1404. goto CleanUp;
  1405. }
  1406. if(fInTable)
  1407. {
  1408. if(_fRangeHasEOP && !PutCtrlWord(CWF_STR, i_intbl))
  1409. goto CleanUp;
  1410. }
  1411. else if(PutBorders(FALSE))
  1412. goto CleanUp;
  1413. if(pPF->_wShadingStyle)
  1414. {
  1415. i = pPF->_wShadingStyle & 15; // Shading patterns
  1416. j = (pPF->_wShadingStyle >> 6) & 31; // Shading forecolor
  1417. k = pPF->_wShadingStyle >> 11; // Shading backcolor
  1418. if (i && i <= CSHADINGSTYLES &&
  1419. !PutCtrlWord(CWF_STR, rgiszShadingStyles[i - 1]) ||
  1420. j && !PutCtrlWord(CWF_VAL, i_cfpat, LookupColor(g_Colors[j-1]) + 1) ||
  1421. k && !PutCtrlWord(CWF_VAL, i_cbpat, LookupColor(g_Colors[k-1]) + 1))
  1422. {
  1423. goto CleanUp;
  1424. }
  1425. }
  1426. if(pPF->_wShadingWeight && !PutCtrlWord(CWF_VAL, i_shading, pPF->_wShadingWeight))
  1427. goto CleanUp;
  1428. // Paragraph numbering
  1429. _fBullet = _fBulletPending = FALSE;
  1430. _nNumber = pPF->UpdateNumber(_nNumber, pPFPrev);
  1431. if(pPF->_wNumbering) // Write numbering info
  1432. {
  1433. LONG iFont = _symbolFont;
  1434. WORD wStyle = pPF->_wNumberingStyle & 0xF00;
  1435. if(pPF->IsListNumbered())
  1436. {
  1437. const CCharFormat *pCF;
  1438. WCHAR szNumber[CCHMAXNUMTOSTR];
  1439. CTxtPtr rpTX(prtp->_rpTX);
  1440. CFormatRunPtr rpCF(prtp->_rpCF);
  1441. rpCF.AdvanceCp(rpTX.FindEOP(tomForward));
  1442. rpCF.AdjustBackward();
  1443. pCF = _ped->GetCharFormat(rpCF.GetFormat());
  1444. iFont = LookupFont(pCF);
  1445. if(iFont < 0)
  1446. {
  1447. iFont = 0;
  1448. TRACEERRORSZ("CWRTFW::WriteParaFormat: illegal bullet font");
  1449. }
  1450. _nFont = iFont;
  1451. // TODO: make the following smarter, i.e., may need to increment
  1452. // _nNumber instead of resetting it to 1.
  1453. _cpg = GetCodePage(pCF->_bCharSet);
  1454. i = 0;
  1455. if(pPF->_wNumbering <= tomListNumberAsUCRoman)
  1456. i = pPF->_wNumbering - tomListNumberAsArabic;
  1457. WCHAR ch = (wStyle == PFNS_PARENS || wStyle == PFNS_PAREN) ? ')'
  1458. : (wStyle == PFNS_PERIOD) ? '.' : 0;
  1459. if(wStyle != PFNS_NONUMBER) // Unless number suppressed
  1460. { // write \pntext group
  1461. pPF->NumToStr(szNumber, _nNumber, fRtfWrite);
  1462. if (!printF(szBeginNumberGroup, iFont) ||
  1463. WritePcData(szNumber, _cpg, FALSE) ||
  1464. !printF(szEndNumberGroup))
  1465. {
  1466. goto CleanUp;
  1467. }
  1468. }
  1469. j = pPF->_wNumberingStyle & 3;
  1470. if (!printF(szBeginNumberFmt,
  1471. wStyle == PFNS_NONUMBER ? "cont" : "body",
  1472. iFont, pPF->_wNumberingTab,
  1473. pPF->_wNumberingStart) ||
  1474. IN_RANGE(1, j, 2) && !PutCtrlWord(CWF_STR,
  1475. j == 1 ? i_pnqc : i_pnqr) ||
  1476. !PutCtrlWord(CWF_STR, rgiszNumberStyle[i]) ||
  1477. wStyle == PFNS_PARENS && !printF(szpntxtb) ||
  1478. ch && !printF(szpntxta, ch) ||
  1479. !printF(szEndGroupCRLF))
  1480. {
  1481. goto CleanUp;
  1482. }
  1483. }
  1484. else
  1485. {
  1486. if (!printF(szBulletGroup, iFont) ||
  1487. !printF(szBulletFmt,
  1488. wStyle == PFNS_NONUMBER ? "cont" : "blt",
  1489. iFont, pPF->_wNumberingTab))
  1490. {
  1491. goto CleanUp;
  1492. }
  1493. }
  1494. _fBullet = TRUE;
  1495. }
  1496. dwEffects = pPF->_wEffects & ((1 << CPFEFFECTS) - 1);
  1497. if (_ped->IsBiDi() && !(dwEffects & PFE_RTLPARA) &&
  1498. !PutCtrlWord(CWF_STR, i_ltrpar)) //ltrpar attribute
  1499. {
  1500. goto CleanUp;
  1501. }
  1502. for(c = CPFEFFECTS; dwEffects && c--; // Output PARAFORMAT2 effects
  1503. dwEffects >>= 1)
  1504. {
  1505. // rgiszPFEffects[] contains PF effect keywords in the
  1506. // order max PFE_xx to min PFE-xx
  1507. AssertSz(rgiszPFEffects[2] == i_hyphpar,
  1508. "CRTFWrite::WriteParaFormat(): rgiszPFEffects is out-of-sync with PFE_XXX");
  1509. // \hyphpar has opposite logic to our PFE_DONOTHYPHEN so we emit
  1510. // \hyphpar0 to toggle the property off
  1511. if (dwEffects & 1 &&
  1512. !PutCtrlWord((c == 2) ? CWF_VAL : CWF_STR, rgiszPFEffects[c], 0))
  1513. {
  1514. goto CleanUp;
  1515. }
  1516. }
  1517. // Put out para indents. RTF first indent = -PF.dxOffset
  1518. // RTF left indent = PF.dxStartIndent + PF.dxOffset
  1519. if(IsHeadingStyle(pPF->_sStyle) && !PutCtrlWord(CWF_VAL, i_s, -pPF->_sStyle-1))
  1520. goto CleanUp;
  1521. if(!fInTable &&
  1522. (pPF->_dxOffset &&
  1523. !PutCtrlWord(CWF_VAL, i_fi, -pPF->_dxOffset) ||
  1524. pPF->_dxStartIndent + pPF->_dxOffset &&
  1525. !PutCtrlWord(CWF_VAL, (pPF->IsRtlPara())
  1526. ? i_ri : i_li, pPF->_dxStartIndent + pPF->_dxOffset) ||
  1527. pPF->_dxRightIndent &&
  1528. !PutCtrlWord(CWF_VAL, (pPF->IsRtlPara())
  1529. ? i_li : i_ri, pPF->_dxRightIndent) ||
  1530. pPF->_dySpaceBefore &&
  1531. !PutCtrlWord(CWF_VAL, i_sb, pPF->_dySpaceBefore) ||
  1532. pPF->_dySpaceAfter &&
  1533. !PutCtrlWord(CWF_VAL, i_sa, pPF->_dySpaceAfter)))
  1534. {
  1535. goto CleanUp;
  1536. }
  1537. if(dwRule) // Special line spacing active
  1538. {
  1539. i = 0; // Default "At Least" or
  1540. if (dwRule == tomLineSpaceExactly) // "Exactly" line spacing
  1541. dy = -abs(dy); // Use negative for "Exactly"
  1542. else if(dwRule == tomLineSpaceMultiple) // RichEdit uses 20 units/line
  1543. { // RTF uses 240 units/line
  1544. i++;
  1545. dy *= 12;
  1546. }
  1547. else if (dwRule != tomLineSpaceAtLeast && dy > 0)
  1548. {
  1549. i++; // Multiple line spacing
  1550. if (dwRule <= tomLineSpaceDouble) // 240 units per line
  1551. dy = 120 * (dwRule + 2);
  1552. }
  1553. if (!PutCtrlWord(CWF_VAL, i_sl, dy) ||
  1554. !PutCtrlWord(CWF_VAL, i_slmult, i))
  1555. {
  1556. goto CleanUp;
  1557. }
  1558. }
  1559. if (!fInTable && IN_RANGE(PFA_RIGHT, pPF->_bAlignment, PFA_JUSTIFY) &&
  1560. !PutCtrlWord(CWF_STR, rgiszAlignment[pPF->_bAlignment - 1]))
  1561. {
  1562. goto CleanUp;
  1563. }
  1564. prgxTabs = pPF->GetTabs();
  1565. for (i = 0; i < cTab; i++)
  1566. {
  1567. pPF->GetTab(i, &tabPos, &tabAlign, &tabLead, prgxTabs);
  1568. AssertSz (tabAlign <= tomAlignBar && tabLead <= 5,
  1569. "CRTFWrite::WriteParaFormat: illegal tab leader/alignment");
  1570. i_t = i_tb; // Default \tb (bar tab)
  1571. if (tabAlign != tomAlignBar) // It isn't a bar tab
  1572. {
  1573. i_t = i_tx; // Use \tx for tabPos
  1574. if (tabAlign && // Put nonleft alignment
  1575. !PutCtrlWord(CWF_STR, rgiszTabAlign[tabAlign-1]))
  1576. {
  1577. goto CleanUp;
  1578. }
  1579. }
  1580. if (tabLead && // Put nonzero tab leader
  1581. !PutCtrlWord(CWF_STR, rgiszTabLead[tabLead-1]) ||
  1582. !PutCtrlWord(CWF_VAL, i_t, tabPos))
  1583. {
  1584. goto CleanUp;
  1585. }
  1586. }
  1587. CleanUp:
  1588. return _ecParseError;
  1589. }
  1590. /*
  1591. * CRTFWrite::WriteText(cwch, lpcwstr, nCodePage, fIsDBCS)
  1592. *
  1593. * @mfunc
  1594. * Write out <p cwch> chars from the Unicode text string <p lpcwstr> taking care to
  1595. * escape any special chars. The Unicode text string is scanned for characters which
  1596. * map directly to RTF strings, and the surrounding chunks of Unicode are written
  1597. * by calling WriteTextChunk.
  1598. *
  1599. * @rdesc
  1600. * EC The error code
  1601. */
  1602. EC CRTFWrite::WriteText(
  1603. LONG cwch, //@parm # chars in buffer
  1604. LPCWSTR lpcwstr, //@parm Pointer to text
  1605. INT nCodePage, //@parm Code page to use to convert to DBCS
  1606. BOOL fIsDBCS) //@parm If TRUE, lpcwstr is DBCS string
  1607. // stuffed into a WSTR
  1608. {
  1609. TRACEBEGIN(TRCSUBSYSRTFW, TRCSCOPEINTERN, "CRTFWrite::WriteText");
  1610. WCHAR *pwchScan;
  1611. WCHAR *pwchStart;
  1612. if(_fBulletPending)
  1613. {
  1614. _fBulletPending = FALSE;
  1615. if(!_nNumber)
  1616. {
  1617. if(!printF(szBulletGroup, _symbolFont))
  1618. goto CleanUp;
  1619. }
  1620. else if(!_pPF->IsNumberSuppressed())
  1621. {
  1622. WCHAR szNumber[CCHMAXNUMTOSTR];
  1623. _pPF->NumToStr(szNumber, ++_nNumber, fRtfWrite);
  1624. if (!printF(szBeginNumberGroup, _nFont) ||
  1625. WritePcData(szNumber, _cpg, FALSE) ||
  1626. !printF(szEndNumberGroup))
  1627. {
  1628. goto CleanUp;
  1629. }
  1630. }
  1631. }
  1632. if(_fCheckInTable)
  1633. {
  1634. CheckInTable(TRUE);
  1635. if(_ecParseError)
  1636. goto CleanUp;
  1637. }
  1638. pwchScan = const_cast<LPWSTR>(lpcwstr);
  1639. pwchStart = pwchScan;
  1640. if(_CF._bCharSet == SYMBOL_CHARSET)
  1641. {
  1642. pwchScan += cwch;
  1643. cwch = 0;
  1644. }
  1645. // Step through the Unicode buffer, weeding out characters that have
  1646. // known translations to RTF strings
  1647. while(cwch-- > 0)
  1648. {
  1649. WCHAR wch = *pwchScan;
  1650. // If this is a string for which the MultiByteToUnicode conversion
  1651. // failed, the buffer will be filled with ANSI bytes stuffed into
  1652. // wchar's (one per). In this case, we don't want to map trail bytes
  1653. // to RTF strings.
  1654. if(fIsDBCS && GetTrailBytesCount(wch, nCodePage) && nCodePage != CP_UTF8)
  1655. {
  1656. // If we have more characters in the buffer, then this is the
  1657. // DBC pair. Otherwise, treat it as single character.
  1658. if(cwch > 0)
  1659. {
  1660. cwch--;
  1661. pwchScan += 2;
  1662. continue;
  1663. }
  1664. }
  1665. // if the char is one for which there is an appropriate RTF string
  1666. // write the preceding chars and output the RTF string
  1667. if(!IN_RANGE(' ', wch, 'Z') &&
  1668. !IN_RANGE('a', wch, 'z') &&
  1669. !IN_RANGE(chOptionalHyphen + 1, wch, ENSPACE - 1) &&
  1670. wch <= BULLET &&
  1671. MapsToRTFKeywordW(wch))
  1672. {
  1673. if (pwchScan != pwchStart &&
  1674. WriteTextChunk(pwchScan - pwchStart, pwchStart, nCodePage,
  1675. fIsDBCS))
  1676. {
  1677. goto CleanUp;
  1678. }
  1679. // map the char(s) to the RTF string
  1680. int cwchUsed = MapToRTFKeyword(pwchScan, cwch, MAPTOKWD_UNICODE);
  1681. cwch -= cwchUsed;
  1682. pwchScan += cwchUsed;
  1683. // start of next run of unprocessed chars is one past current char
  1684. pwchStart = pwchScan + 1;
  1685. if(cwch && _fCheckInTable)
  1686. {
  1687. _fCheckInTable = FALSE;
  1688. if(!PutCtrlWord(CWF_STR, i_intbl))
  1689. goto CleanUp;
  1690. }
  1691. }
  1692. pwchScan++;
  1693. }
  1694. // write the last chunk
  1695. if (pwchScan != pwchStart &&
  1696. WriteTextChunk(pwchScan - pwchStart, pwchStart, nCodePage, fIsDBCS))
  1697. {
  1698. goto CleanUp;
  1699. }
  1700. CleanUp:
  1701. return _ecParseError;
  1702. }
  1703. /*
  1704. * CRTFWrite::WriteTextChunk(cwch, lpcwstr, nCodePage, fIsDBCS)
  1705. *
  1706. * @mfunc
  1707. * Write out <p cwch> chars from the Unicode text string <p lpcwstr> taking care to
  1708. * escape any special chars. Unicode chars which cannot be converted to
  1709. * DBCS chars using the supplied codepage, <p nCodePage>, are written using the
  1710. * \u RTF tag.
  1711. *
  1712. * @rdesc
  1713. * EC The error code
  1714. */
  1715. EC CRTFWrite::WriteTextChunk(
  1716. LONG cwch, //@parm # chars in buffer
  1717. LPCWSTR lpcwstr, //@parm Pointer to text
  1718. INT nCodePage, //@parm code page to use to convert to DBCS
  1719. BOOL fIsDBCS) //@parm indicates whether lpcwstr is a Unicode string
  1720. // or a DBCS string stuffed into a WSTR
  1721. {
  1722. // FUTURE(BradO): There is alot of commonality b/t this routine and
  1723. // WritePcData. We should re-examine these routines and consider
  1724. // combining them into a common routine.
  1725. TRACEBEGIN(TRCSUBSYSRTFW, TRCSCOPEINTERN, "CRTFWrite::WriteTextChunk");
  1726. BYTE b;
  1727. LONG cbAnsi;
  1728. LONG cbAnsiBufferSize;
  1729. LONG cbChar;
  1730. UINT ch;
  1731. BOOL fMissingCodePage = FALSE;
  1732. BOOL fMultiByte;
  1733. BOOL fUsedDefault = FALSE;
  1734. BYTE * pbAnsi;
  1735. BYTE * pbAnsiBuffer;
  1736. // When WideCharToMultiByte fails to convert a char, the following default
  1737. // char is used as a placeholder in the string being converted
  1738. const char chToDBCSDefault = 0;
  1739. // Allocate temp buffer for ANSI text we convert to
  1740. cbAnsiBufferSize = cachBufferMost * (nCodePage == CP_UTF8 ? 3 : MB_LEN_MAX);
  1741. if (!_pbAnsiBuffer)
  1742. {
  1743. // If the code page was CP_UTF8, it will always be CP_UTF8 for this instance
  1744. _pbAnsiBuffer = (BYTE *)PvAlloc(cbAnsiBufferSize, GMEM_FIXED);
  1745. if (!_pbAnsiBuffer)
  1746. goto RAMError;
  1747. }
  1748. pbAnsiBuffer = _pbAnsiBuffer;
  1749. // Convert Unicode (or fIsDBCS) buffer to ANSI
  1750. if(fIsDBCS)
  1751. {
  1752. // Supply some bogus code page which will force direct conversion
  1753. // from wchar to bytes (losing high byte of wchar).
  1754. // Also, don't want to use default char replacement in this case.
  1755. cbAnsi = WCTMB(INVALID_CODEPAGE, 0, lpcwstr, cwch,
  1756. (char *)pbAnsiBuffer, cbAnsiBufferSize,
  1757. NULL, NULL, NULL);
  1758. }
  1759. else
  1760. {
  1761. cbAnsi = WCTMB(nCodePage, 0, lpcwstr, cwch,
  1762. (char *)pbAnsiBuffer, cbAnsiBufferSize,
  1763. &chToDBCSDefault, &fUsedDefault,
  1764. &fMissingCodePage);
  1765. }
  1766. Assert(cbAnsi > 0);
  1767. pbAnsi = pbAnsiBuffer;
  1768. fMultiByte = (cbAnsi > cwch) || fIsDBCS || fMissingCodePage;
  1769. while (!_ecParseError && cbAnsi-- > 0)
  1770. {
  1771. b = *pbAnsi;
  1772. ch = *lpcwstr;
  1773. // Compare ASCII chars to their Unicode counterparts to check
  1774. // that we're in sync
  1775. AssertSz(cwch <= 0 || ch > 127 || b == ch,
  1776. "CRTFWrite::WriteText: Unicode and DBCS strings out of sync");
  1777. // If _fNCRForNonASCII, output the \uN tag for all nonASCII chars.
  1778. // This is useful because many Unicode chars that aren't in the
  1779. // target codepage are converted by WideCharToMultiByte() to some
  1780. // "best match char" for the codepage, e.g., alpha (0x3B1) converts
  1781. // to 'a' for cpg 1252.
  1782. //
  1783. // For NT 5, we use WC_NO_BEST_FIT_CHARS, which causes our regular
  1784. // algorithm to output \uN values whenever the system cannot convert
  1785. // a character correctly. This still requires readers that can handle
  1786. // multicodepage RTF, which is problematic for some RTF-to-HTML
  1787. // converters.
  1788. if(!IN_RANGE(' ', b, 'z') && MapsToRTFKeywordA(b))
  1789. {
  1790. int cchUsed = MapToRTFKeyword(pbAnsi, cbAnsi, MAPTOKWD_ANSI);
  1791. cbAnsi -= cchUsed;
  1792. pbAnsi += cchUsed;
  1793. lpcwstr += cchUsed;
  1794. cwch -= cchUsed;
  1795. }
  1796. else if(nCodePage == CP_UTF8)
  1797. {
  1798. PutChar(b); // Output 1st byte in any
  1799. if(b >= 0xC0) // case. At least 2-byte
  1800. { // At least 2-byte lead
  1801. pbAnsi++; // byte, so output a
  1802. Assert(cbAnsi && IN_RANGE(0x80, *pbAnsi, 0xBF));
  1803. cbAnsi--; // trail byte
  1804. PutChar(*pbAnsi);
  1805. if(b >= 0xE0) // 3-byte lead byte, so
  1806. { // output another trail
  1807. pbAnsi++; // byte
  1808. Assert(cbAnsi && IN_RANGE(0x80, *pbAnsi, 0xBF));
  1809. cbAnsi--;
  1810. PutChar(*pbAnsi);
  1811. }
  1812. }
  1813. }
  1814. else
  1815. {
  1816. cbChar = fMultiByte && cbAnsi && GetTrailBytesCount(b, nCodePage)
  1817. ? 2 : 1;
  1818. if(ch >= 0x80 && !fIsDBCS && _fNCRForNonASCII && nCodePage != CP_SYMBOL)
  1819. { // Output /uN for nonASCII
  1820. if(cbChar != _cbCharLast)
  1821. {
  1822. _cbCharLast = cbChar; // cb to follow /uN
  1823. if(!PutCtrlWord(CWF_VAL, i_uc, cbChar))
  1824. goto CleanUp;
  1825. }
  1826. if(!PutCtrlWord(CWF_VAL, i_u, ch))
  1827. goto CleanUp;
  1828. Assert(chToDBCSDefault != '?');
  1829. if(fUsedDefault) // Don't output another /uN
  1830. { // below
  1831. b = '?';
  1832. _fNeedDelimeter = FALSE;
  1833. }
  1834. }
  1835. if(cbChar == 2)
  1836. {
  1837. pbAnsi++; // Output DBCS pair
  1838. cbAnsi--;
  1839. if(fIsDBCS)
  1840. {
  1841. lpcwstr++;
  1842. cwch--;
  1843. }
  1844. printF(szEscape2CharFmt, b, *pbAnsi);
  1845. }
  1846. else
  1847. {
  1848. if(b == chToDBCSDefault && fUsedDefault)
  1849. {
  1850. // WideCharToMultiByte() couldn't complete a conversion so it
  1851. // used the default char we provided (0) used as a placeholder.
  1852. // In this case we want to output the original Unicode char.
  1853. if(!PutCtrlWord(CWF_VAL, i_u, (cwch > 0 ? ch : TEXT('?'))))
  1854. goto CleanUp;
  1855. _fNeedDelimeter = FALSE;
  1856. if(!PutChar('?'))
  1857. goto CleanUp;
  1858. }
  1859. else if(!IN_RANGE(32, b, 127))
  1860. printF(szEscapeCharFmt, b);
  1861. else
  1862. PutChar(b);
  1863. }
  1864. }
  1865. pbAnsi++;
  1866. lpcwstr++;
  1867. cwch--;
  1868. }
  1869. goto CleanUp;
  1870. RAMError:
  1871. _ped->GetCallMgr()->SetOutOfMemory();
  1872. _ecParseError = ecNoMemory;
  1873. CleanUp:
  1874. return _ecParseError;
  1875. }
  1876. /*
  1877. * CRTFWrite::WriteInfo()
  1878. *
  1879. * @mfunc
  1880. * Write out Far East specific data.
  1881. *
  1882. * @rdesc
  1883. * EC The error code
  1884. */
  1885. EC CRTFWrite::WriteInfo()
  1886. {
  1887. TRACEBEGIN(TRCSUBSYSRTFW, TRCSCOPEINTERN, "CRTFWrite::WriteInfo");
  1888. // TODO(BradO): Ultimately it would be nice to set some kind of
  1889. // fRTFFE bit to determine whether to write \info stuff. For now,
  1890. // we rely on the fact that lchars and fchars info actually exists
  1891. // to determine whether to write out the \info group.
  1892. #ifdef UNDER_WORK
  1893. if (!(_dwFlags & fRTFFE) || // Start doc area
  1894. !PutCtrlWord(CWF_GRP, i_info) ||
  1895. !printF("{\\horzdoc}"))
  1896. goto CleanUp;
  1897. // Write out punctuation character info
  1898. CHAR sz[PUNCT_MAX];
  1899. if(UsVGetPunct(_ped->lpPunctObj, PC_FOLLOWING, sz, sizeof(sz))
  1900. > PUNCT_MAX - 2)
  1901. goto CleanUp;
  1902. if(!Puts("{\\*\\fchars") || WritePcData(sz) || !PutChar(chEndGroup))
  1903. goto CleanUp;
  1904. if(UsVGetPunct(ped->lpPunctObj, PC_LEADING, sz, sizeof(sz)) > PUNCT_MAX+2)
  1905. goto CleanUp;
  1906. if(!Puts("{\\*\\lchars") || WritePcData(sz) || !PutChar(chEndGroup))
  1907. goto CleanUp;
  1908. Puts(szEndGroupCRLF); // End info group
  1909. #endif
  1910. LPSTR lpstrLeading = NULL;
  1911. LPSTR lpstrFollowing = NULL;
  1912. // If either succeeds (but evaluate both)
  1913. if(((_ped->GetLeadingPunct(&lpstrLeading) == NOERROR) +
  1914. (_ped->GetFollowingPunct(&lpstrFollowing) == NOERROR)) &&
  1915. (lpstrLeading || lpstrFollowing))
  1916. {
  1917. if (!PutCtrlWord(CWF_GRP, i_info) ||
  1918. !Puts(szHorzdocGroup, sizeof(szHorzdocGroup) - 1))
  1919. {
  1920. goto CleanUp;
  1921. }
  1922. if (lpstrLeading &&
  1923. (!PutCtrlWord(CWF_AST, i_lchars) ||
  1924. !Puts(lpstrLeading, strlen(lpstrLeading)) ||
  1925. !PutChar(chEndGroup)))
  1926. {
  1927. goto CleanUp;
  1928. }
  1929. if (lpstrFollowing &&
  1930. (!PutCtrlWord(CWF_AST, i_fchars) ||
  1931. !Puts(lpstrFollowing, strlen(lpstrFollowing)) ||
  1932. !PutChar(chEndGroup)))
  1933. {
  1934. goto CleanUp;
  1935. }
  1936. Puts(szEndGroupCRLF, sizeof(szEndGroupCRLF) - 1); // End info group
  1937. }
  1938. CleanUp:
  1939. return _ecParseError;
  1940. }
  1941. /*
  1942. * CRTFWrite::WriteRtf()
  1943. *
  1944. * @mfunc
  1945. * Write range _prg to output stream _pes.
  1946. *
  1947. * @rdesc
  1948. * LONG Number of chars inserted into text; 0 means none were
  1949. * inserted, OR an error occurred.
  1950. */
  1951. LONG CRTFWrite::WriteRtf()
  1952. {
  1953. TRACEBEGIN(TRCSUBSYSRTFW, TRCSCOPEINTERN, "CRTFWrite::WriteRtf");
  1954. LONG cch, cchBuffer;
  1955. LONG cchCF, cchPF;
  1956. LONG cchT;
  1957. LONG cpMin, cpMost;
  1958. BOOL fOutputEndGroup;
  1959. LONG i, j;
  1960. LONG lDocDefaultTab;
  1961. TCHAR * pch;
  1962. TCHAR * pchBuffer;
  1963. CTxtEdit * ped = _ped;
  1964. CDocInfo * pDocInfo = ped->GetDocInfo();
  1965. CRchTxtPtr rtp(*_prg);
  1966. WORD wCodePage = CP_ACP;
  1967. AssertSz(_prg && _pes, "CRTFW::WriteRtf: improper initialization");
  1968. cch = _prg->GetRange(cpMin, cpMost); // Get rtp = cpMin, cch > 0
  1969. rtp.SetCp(cpMin);
  1970. _fRangeHasEOP = _prg->fHasEOP(); // Maintained for Selection
  1971. if(!_prg->IsSel()) // Validate range for RTF
  1972. { // writing. Don't write
  1973. CPFRunPtr rp(rtp); // partial table row unless
  1974. CTxtPtr tp(rtp._rpTX); // no CELLs are included
  1975. LONG Results;
  1976. _fRangeHasEOP = tp.IsAtEOP();
  1977. if(tp.FindEOP(cch, &Results)) // Need to get CELL info too
  1978. _fRangeHasEOP = TRUE;
  1979. if(rtp.InTable())
  1980. {
  1981. tp.SetCp(cpMin);
  1982. if(!_fRangeHasEOP) // Partial row
  1983. {
  1984. if(Results & FEOP_CELL) // Has 1 or more CELLs
  1985. {
  1986. while(tp.GetCp() < cpMost) // Stop at first CELL
  1987. {
  1988. if(tp.GetChar() == CELL)
  1989. {
  1990. cch = tp.GetCp() - cpMin;
  1991. break;
  1992. }
  1993. tp.AdvanceCp(1);
  1994. }
  1995. }
  1996. }
  1997. else if(!tp.IsAfterEOP())
  1998. { // Move back to start of row
  1999. rtp.Advance(tp.FindEOP(tomBackward));
  2000. cch += cpMin - rtp.GetCp();
  2001. }
  2002. }
  2003. if(_fRangeHasEOP) // Check cpMost for table
  2004. {
  2005. rp.AdvanceCp(cpMost - cpMin); // Go to cpMost
  2006. if(rp.InTable())
  2007. {
  2008. tp.SetCp(cpMost);
  2009. cch += tp.FindEOP(tomForward); // Include whole row
  2010. }
  2011. }
  2012. }
  2013. // Allocate buffers for text we pick up and for RTF output
  2014. pchBuffer = (TCHAR *) PvAlloc(cachBufferMost * (sizeof(TCHAR) + 1) + 1,
  2015. GMEM_FIXED); // Final 1 is for debug
  2016. if(!pchBuffer)
  2017. {
  2018. fOutputEndGroup = FALSE;
  2019. goto RAMError;
  2020. }
  2021. _pchRTFBuffer = (CHAR *)(pchBuffer + cachBufferMost);
  2022. _pchRTFEnd = _pchRTFBuffer; // Initialize RTF buffer ptr
  2023. _cchBufferOut = 0; // and character count
  2024. _cchOut = 0; // and character output
  2025. // Determine the \ansicpgN value
  2026. if(!pDocInfo)
  2027. {
  2028. fOutputEndGroup = TRUE;
  2029. goto RAMError;
  2030. }
  2031. BOOL fNameIsDBCS;
  2032. if (BuildTables(rtp._rpCF, rtp._rpPF, cch, fNameIsDBCS))
  2033. goto CleanUp;
  2034. wCodePage = (_dwFlags & SF_USECODEPAGE)
  2035. ? HIWORD(_dwFlags) : pDocInfo->wCpg;
  2036. if (fNameIsDBCS && wCodePage == CP_UTF8)
  2037. {
  2038. // Cannot have UTF8 if we have any run containing broken DBCS.
  2039. // Default back to regular rtf
  2040. wCodePage = pDocInfo->wCpg;
  2041. _dwFlags &= ~SF_USECODEPAGE;
  2042. }
  2043. // Start RTF with \rtfN, \urtfN, or \pwdN group
  2044. i = (_dwFlags & SF_RTFVAL) >> 16;
  2045. if (!PutCtrlWord(CWF_GRV,
  2046. (wCodePage == CP_UTF8) ? i_urtf :
  2047. (_dwFlags & SFF_PWD) ? i_pwd : i_rtf, i + 1) ||
  2048. ped->IsBiDi() && !Puts("\\fbidis", 7) ||
  2049. !PutCtrlWord(CWF_STR, i_ansi))
  2050. {
  2051. goto CleanUpNoEndGroup;
  2052. }
  2053. if (wCodePage != tomInvalidCpg && wCodePage != CP_ACP &&
  2054. !PutCtrlWord(CWF_VAL, i_ansicpg, wCodePage == CP_UTF8 ? pDocInfo->wCpg : wCodePage))
  2055. {
  2056. goto CleanUp;
  2057. }
  2058. if(!printF(szDefaultFont))
  2059. goto CleanUp;
  2060. LCID lcid;
  2061. LANGID langid;
  2062. if (_ped->GetDefaultLCID(&lcid) == NOERROR &&
  2063. lcid != tomInvalidLCID && (langid = LANGIDFROMLCID(lcid)) &&
  2064. !PutCtrlWord(CWF_VAL, i_deflang, langid))
  2065. {
  2066. goto CleanUp;
  2067. }
  2068. if (_ped->GetDefaultLCIDFE(&lcid) == NOERROR &&
  2069. lcid != tomInvalidLCID && (langid = LANGIDFROMLCID(lcid)) &&
  2070. !PutCtrlWord(CWF_VAL, i_deflangfe, langid))
  2071. {
  2072. goto CleanUp;
  2073. }
  2074. LONG lDocType;
  2075. _ped->GetDocumentType(&lDocType);
  2076. if (lDocType && _ped->IsBiDi() &&
  2077. !PutCtrlWord(CWF_STR, lDocType == DT_RTLDOC ? i_rtldoc : i_ltrdoc))
  2078. {
  2079. goto CleanUp;
  2080. }
  2081. lDocDefaultTab = pDocInfo->dwDefaultTabStop;
  2082. if(!lDocDefaultTab)
  2083. lDocDefaultTab = lDefaultTab;
  2084. if (lDocDefaultTab != 720 && !PutCtrlWord(CWF_VAL, i_deftab, lDocDefaultTab) ||
  2085. WriteFontTable() || WriteColorTable())
  2086. {
  2087. goto CleanUp;
  2088. }
  2089. if(_nHeadingStyle)
  2090. {
  2091. if(!PutCtrlWord(CWF_GRP, i_stylesheet) || !printF(szNormalStyle))
  2092. goto CleanUp;
  2093. for(i = 1; i < -_nHeadingStyle; i++)
  2094. {
  2095. if(!printF(szHeadingStyle, i, i))
  2096. goto CleanUp;
  2097. }
  2098. Puts(szEndGroupCRLF, sizeof(szEndGroupCRLF) - 1); // End font table group
  2099. }
  2100. LRESULT lres;
  2101. _ped->GetViewKind(&lres);
  2102. _ped->GetViewScale(&j);
  2103. if (WriteInfo() ||
  2104. _fRangeHasEOP && !PutCtrlWord(CWF_VAL, i_viewkind, lres) ||
  2105. (_dwFlags & SFF_PERSISTVIEWSCALE) && j != 100 &&
  2106. !PutCtrlWord(CWF_VAL, i_viewscale, j))
  2107. {
  2108. goto CleanUp;
  2109. }
  2110. // Write Unicode character byte count for use by entire document (since
  2111. // we don't use \plain's and since \ucN behaves as a char formatting tag,
  2112. // we're safe outputting it only once).
  2113. if(!PutCtrlWord(CWF_VAL, i_uc, iUnicodeCChDefault))
  2114. goto CleanUp;
  2115. while (cch > 0)
  2116. {
  2117. // Get next run of chars with same para formatting
  2118. cchPF = rtp.GetCchLeftRunPF();
  2119. cchPF = min(cchPF, cch);
  2120. AssertSz(cchPF, "CRTFW::WriteRtf: Empty para format run!");
  2121. if(WriteParaFormat(&rtp)) // Write paragraph formatting
  2122. goto CleanUp;
  2123. while (cchPF > 0)
  2124. {
  2125. // Get next run of characters with same char formatting
  2126. cchCF = rtp.GetCchLeftRunCF();
  2127. cchCF = min(cchCF, cchPF);
  2128. AssertSz(cchCF, "CRTFW::WriteRtf: Empty char format run!");
  2129. const CCharFormat * pCF = rtp.GetCF();
  2130. if (WriteCharFormat(pCF)) // Write char attributes
  2131. goto CleanUp;
  2132. INT nCodePage = CP_UTF8;
  2133. if(!IsUTF8)
  2134. {
  2135. if(IsPrivateCharSet(pCF->_bCharSet))
  2136. nCodePage = 1252; // Force \uN's
  2137. else
  2138. {
  2139. nCodePage = GetCodePage(pCF->_bCharSet);
  2140. if(nCodePage == CP_ACP && (_dwFlags & SF_USECODEPAGE))
  2141. nCodePage = HIWORD(_dwFlags);
  2142. }
  2143. }
  2144. while (cchCF > 0)
  2145. {
  2146. cchBuffer = min(cachBufferMost, cchCF);
  2147. // FUTURE: since this routine only reads the backing store
  2148. // and GetText only reads it, we can avoid allocating the
  2149. // buffer and use CTxtPtr::GetPch() directly as in
  2150. // CMeasurer::Measure()
  2151. cchBuffer = rtp._rpTX.GetText(cchBuffer, pchBuffer);
  2152. pch = pchBuffer;
  2153. cchT = cchBuffer;
  2154. if(cchT > 0)
  2155. {
  2156. TCHAR * pchWork = pch;
  2157. LONG cchWork = cchT;
  2158. LONG cchTWork;
  2159. LONG cp = rtp.GetCp();
  2160. while (cchWork >0)
  2161. {
  2162. cchT = cchWork ;
  2163. pch = pchWork;
  2164. while (cchWork > 0 ) // search for objects
  2165. {
  2166. if(*pchWork++ == WCH_EMBEDDING)
  2167. break; // Will write out object
  2168. cchWork--;
  2169. }
  2170. cchTWork = cchT - cchWork;
  2171. if(cchTWork) // write text before object
  2172. {
  2173. if(WriteText(cchTWork, pch, nCodePage,
  2174. (pCF->_dwEffects & CFE_RUNISDBCS)))
  2175. {
  2176. goto CleanUp;
  2177. }
  2178. }
  2179. cp += cchTWork;
  2180. if(cchWork > 0) // there is an object
  2181. {
  2182. COleObject *pobj;
  2183. Assert(_ped->GetObjectMgr());
  2184. pobj = _ped->GetObjectMgr()->GetObjectFromCp(cp);
  2185. if(!pobj)
  2186. goto CleanUp;
  2187. // First, commit the object to make sure the pres.
  2188. // caches, etc. are up-to-date. Don't worry
  2189. // about errors here.
  2190. pobj->SafeSaveObject();
  2191. if(_fIncludeObjects)
  2192. WriteObject(cp, pobj);
  2193. else if(!Puts(szObjPosHolder, sizeof(szObjPosHolder) - 1))
  2194. goto CleanUp;
  2195. cp++;
  2196. cchWork--;
  2197. }
  2198. }
  2199. }
  2200. rtp.Advance(cchBuffer);
  2201. cchCF -= cchBuffer;
  2202. cchPF -= cchBuffer;
  2203. cch -= cchBuffer;
  2204. }
  2205. }
  2206. }
  2207. CleanUp:
  2208. // End RTF group
  2209. Puts(szEndGroupCRLF, sizeof(szEndGroupCRLF));
  2210. FlushBuffer();
  2211. CleanUpNoEndGroup:
  2212. FreePv(pchBuffer);
  2213. if (_ecParseError != ecNoError)
  2214. {
  2215. TRACEERRSZSC("CRTFW::WriteRtf()", _ecParseError);
  2216. Tracef(TRCSEVERR, "Writing error: %s", rgszParseError[_ecParseError]);
  2217. if(!_pes->dwError) // Make error code OLE-like
  2218. _pes->dwError = -abs(_ecParseError);
  2219. _cchOut = 0;
  2220. }
  2221. return _cchOut;
  2222. RAMError:
  2223. ped->GetCallMgr()->SetOutOfMemory();
  2224. _ecParseError = ecNoMemory;
  2225. if(fOutputEndGroup)
  2226. goto CleanUp;
  2227. goto CleanUpNoEndGroup;
  2228. }