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

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