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.

4011 lines
144 KiB

  1. /*
  2. * @doc INTERNAL
  3. *
  4. * @module RTFREAD.CPP - RichEdit RTF reader (w/o objects) |
  5. *
  6. * This file contains the nonobject code of RichEdit RTF reader.
  7. * See rtfread2.cpp for embedded-object code.
  8. *
  9. * Authors:<nl>
  10. * Original RichEdit 1.0 RTF converter: Anthony Francisco <nl>
  11. * Conversion to C++ and RichEdit 2.0 w/o objects: Murray Sargent
  12. * Lots of enhancements/maintenance: Brad Olenick
  13. *
  14. * @devnote
  15. * All sz's in the RTF*.? files refer to a LPSTRs, not LPTSTRs, unless
  16. * noted as a szW.
  17. *
  18. * @todo
  19. * 1. Unrecognized RTF. Also some recognized won't round trip <nl>
  20. * 2. In font.c, add overstrike for CFE_DELETED and underscore for
  21. * CFE_REVISED. Would also be good to change color for CF.bRevAuthor
  22. *
  23. * Copyright (c) 1995-1998, Microsoft Corporation. All rights reserved.
  24. */
  25. #include "_common.h"
  26. #include "_rtfread.h"
  27. #include "_util.h"
  28. #include "_font.h"
  29. #include "_disp.h"
  30. ASSERTDATA
  31. /*
  32. * Global Variables
  33. */
  34. #define PFM_ALLRTF (PFM_ALL2 | PFM_COLLAPSED | PFM_OUTLINELEVEL | PFM_BOX)
  35. // for object attachment placeholder list
  36. #define cobPosInitial 8
  37. #define cobPosChunk 8
  38. #if CFE_SMALLCAPS != 0x40 || CFE_ALLCAPS != 0x80 || CFE_HIDDEN != 0x100 \
  39. || CFE_OUTLINE != 0x200 || CFE_SHADOW != 0x400
  40. #error "Need to change RTF char effect conversion routines
  41. #endif
  42. // for RTF tag coverage testing
  43. #if defined(DEBUG)
  44. #define TESTPARSERCOVERAGE() \
  45. { \
  46. if(GetProfileIntA("RICHEDIT DEBUG", "RTFCOVERAGE", 0)) \
  47. { \
  48. TestParserCoverage(); \
  49. } \
  50. }
  51. #define PARSERCOVERAGE_CASE() \
  52. { \
  53. if(_fTestingParserCoverage) \
  54. { \
  55. return ecNoError; \
  56. } \
  57. }
  58. #define PARSERCOVERAGE_DEFAULT() \
  59. { \
  60. if(_fTestingParserCoverage) \
  61. { \
  62. return ecStackOverflow; /* some bogus error */ \
  63. } \
  64. }
  65. #else
  66. #define TESTPARSERCOVERAGE()
  67. #define PARSERCOVERAGE_CASE()
  68. #define PARSERCOVERAGE_DEFAULT()
  69. #endif
  70. // FF's should not have paragraph number prepended to them
  71. inline BOOL CharGetsNumbering(WORD ch) { return ch != FF; }
  72. // V-GUYB: PWord Converter requires loss notification.
  73. #ifdef REPORT_LOSSAGE
  74. typedef struct
  75. {
  76. IStream *pstm;
  77. BOOL bFirstCallback;
  78. LPVOID *ppwdPWData;
  79. BOOL bLoss;
  80. } LOST_COOKIE;
  81. #endif
  82. //======================== OLESTREAM functions =======================================
  83. DWORD CALLBACK RTFGetFromStream (
  84. RTFREADOLESTREAM *OLEStream, //@parm OleStream
  85. void FAR * pvBuffer, //@parm Buffer to read
  86. DWORD cb) //@parm Bytes to read
  87. {
  88. return OLEStream->Reader->ReadData ((BYTE *)pvBuffer, cb);
  89. }
  90. DWORD CALLBACK RTFGetBinaryDataFromStream (
  91. RTFREADOLESTREAM *OLEStream, //@parm OleStream
  92. void FAR * pvBuffer, //@parm Buffer to read
  93. DWORD cb) //@parm Bytes to read
  94. {
  95. return OLEStream->Reader->ReadBinaryData ((BYTE *)pvBuffer, cb);
  96. }
  97. //============================ STATE Structure =================================
  98. /*
  99. * STATE::AddPF(PF, lDefTab, lDocType)
  100. *
  101. * @mfunc
  102. * If the PF contains new info, this info is applied to the PF for the
  103. * state. If this state was sharing a PF with a previous state, a new
  104. * PF is created for the state, and the new info is applied to it.
  105. *
  106. * @rdesc
  107. * TRUE unless needed new PF and couldn't allocate it
  108. */
  109. BOOL STATE::AddPF(
  110. const CParaFormat &PF, //@parm Current RTFRead _PF
  111. LONG lDocType, //@parm Default doc type to use if no prev state
  112. DWORD dwMask) //@parm Mask to use
  113. {
  114. // Create a new PF if:
  115. // 1. The state doesn't have one yet
  116. // 2. The state has one, but it is shared by the previous state and
  117. // there are PF deltas to apply to the state's PF
  118. if(!pPF || dwMask && pstatePrev && pPF == pstatePrev->pPF)
  119. {
  120. Assert(!pstatePrev || pPF);
  121. pPF = new CParaFormat;
  122. if(!pPF)
  123. return FALSE;
  124. // Give the new PF some initial values - either from the previous
  125. // state's PF or by CParaFormat initialization
  126. if(pstatePrev)
  127. {
  128. // Copy the PF from the previous state
  129. *pPF = *pstatePrev->pPF;
  130. dwMaskPF = pstatePrev->dwMaskPF;
  131. }
  132. else
  133. {
  134. // We've just created a new PF for the state - there is no
  135. // previous state to copy from. Use default values.
  136. pPF->InitDefault(lDocType == DT_RTLDOC ? PFE_RTLPARA : 0);
  137. dwMaskPF = PFM_ALLRTF;
  138. }
  139. }
  140. // Apply the new PF deltas to the state's PF
  141. if(dwMask)
  142. {
  143. if(dwMask & PFM_TABSTOPS) // Don't change _iTabs here
  144. {
  145. pPF->_bTabCount = PF._bTabCount;
  146. dwMask &= ~PFM_TABSTOPS;
  147. }
  148. pPF->Apply(&PF, dwMask);
  149. }
  150. return TRUE;
  151. }
  152. /*
  153. * STATE::DeletePF()
  154. *
  155. * @mfunc
  156. * If the state's PF is not shared by the previous state, the PF for this
  157. * state is deleted.
  158. */
  159. void STATE::DeletePF()
  160. {
  161. if(pPF && (!pstatePrev || pPF != pstatePrev->pPF))
  162. delete pPF;
  163. pPF = NULL;
  164. }
  165. /*
  166. * STATE::SetCodePage(CodePage)
  167. *
  168. * @mfunc
  169. * If current nCodePage is CP_UTF8, use it for all conversions (yes, even
  170. * for SYMBOL_CHARSET). Else use CodePage.
  171. */
  172. void STATE::SetCodePage(
  173. LONG CodePage)
  174. {
  175. if(nCodePage != CP_UTF8)
  176. nCodePage = CodePage;
  177. }
  178. //============================ CRTFRead Class ==================================
  179. /*
  180. * CRTFRead::CRTFRead(prg, pes, dwFlags)
  181. *
  182. * @mfunc
  183. * Constructor for RTF reader
  184. */
  185. CRTFRead::CRTFRead (
  186. CTxtRange * prg, //@parm CTxtRange to read into
  187. EDITSTREAM * pes, //@parm Edit stream to read from
  188. DWORD dwFlags //@parm Read flags
  189. )
  190. : CRTFConverter(prg, pes, dwFlags, TRUE)
  191. {
  192. TRACEBEGIN(TRCSUBSYSRTFR, TRCSCOPEINTERN, "CRTFRead::CRTFRead");
  193. Assert(prg->GetCch() == 0);
  194. //TODO(BradO): We should examine the member data in the constructor
  195. // and determine which data we want initialized on construction and
  196. // which at the beginning of every read (in CRTFRead::ReadRtf()).
  197. _sDefaultFont = -1; // No \deff n control word yet
  198. _sDefaultLanguage = INVALID_LANGUAGE;
  199. _sDefaultLanguageFE = INVALID_LANGUAGE;
  200. _sDefaultTabWidth = 0;
  201. _sDefaultBiDiFont = -1;
  202. _dwMaskCF = 0; // No char format changes yet
  203. _dwMaskCF2 = 0;
  204. _nFieldCodePage = 0;
  205. _ptfField = NULL;
  206. _fRestoreFieldFormat= FALSE;
  207. _fSeenFontTable = FALSE; // No \fonttbl yet
  208. _fCharSet = FALSE; // No \fcharset yet
  209. _dwFlagsUnion = 0; // No flags yet
  210. _pes->dwError = 0; // No error yet
  211. _cchUsedNumText = 0; // No numbering text yet
  212. _cCell = 0; // No table cells yet
  213. _iCell = 0;
  214. _cTab = 0;
  215. _pstateStackTop = NULL;
  216. _pstateLast = NULL;
  217. _szText =
  218. _pchRTFBuffer = // No input buffer yet
  219. _pchRTFCurrent =
  220. _szSymbolFieldResult=
  221. _pchRTFEnd = NULL;
  222. _prtfObject = NULL;
  223. _pcpObPos = NULL;
  224. _bTabLeader = 0;
  225. _bTabType = 0;
  226. _pobj = 0;
  227. _bAlignment = PFA_LEFT;
  228. _cbSkipForUnicode = 0;
  229. _fHyperlinkField = FALSE;
  230. _szHyperlinkFldinst = NULL;
  231. _szHyperlinkFldrslt = NULL;
  232. _cCell = 0; // No cell right boundaries
  233. _dxCell = 0; // or half gap defined yet
  234. _xRowOffset = 0;
  235. _bCellBrdrWdths = 0;
  236. _wBorderWidth = 0; // No borders yet
  237. _dwBorderColor = 0;
  238. _bAlignment = PFA_LEFT;
  239. _iTabsTable = -1; // No cell widths yet
  240. // Does story size exceed the maximum text size? Be very careful to
  241. // use unsigned comparisons here since _cchMax has to be unsigned
  242. // (EM_LIMITTEXT allows 0xFFFFFFFF to be a large positive maximum
  243. // value). I.e., don't use signed arithmetic.
  244. DWORD cchAdj = _ped->GetAdjustedTextLength();
  245. _cchMax = _ped->TxGetMaxLength();
  246. if(_cchMax > cchAdj)
  247. _cchMax = _cchMax - cchAdj; // Room left
  248. else
  249. _cchMax = 0; // No room left
  250. ZeroMemory(_rgStyles, sizeof(_rgStyles)); // No style levels yet
  251. _bBiDiCharSet = 0;
  252. if(_ped->IsBiDi())
  253. {
  254. _bBiDiCharSet = ARABIC_CHARSET; // Default Arabic charset
  255. BYTE bCharSet;
  256. CFormatRunPtr rpCF(prg->_rpCF);
  257. // Look backward in text, trying to find a RTL CharSet.
  258. // NB: \fN with an RTL charset updates _bBiDiCharSet.
  259. do
  260. {
  261. bCharSet = _ped->GetCharFormat(rpCF.GetFormat())->_bCharSet;
  262. if(IsRTLCharSet(bCharSet))
  263. {
  264. _bBiDiCharSet = bCharSet;
  265. break;
  266. }
  267. } while (rpCF.PrevRun());
  268. }
  269. // Initialize OleStream
  270. RTFReadOLEStream.Reader = this;
  271. RTFReadOLEStream.lpstbl->Get = (DWORD (CALLBACK*)(LPOLESTREAM, void *, DWORD))
  272. RTFGetFromStream;
  273. RTFReadOLEStream.lpstbl->Put = NULL;
  274. #ifdef DEBUG
  275. // TODO: Implement RTF tag logging for the Mac
  276. #if !defined(MACPORT)
  277. _fTestingParserCoverage = FALSE;
  278. _prtflg = NULL;
  279. if(GetProfileIntA("RICHEDIT DEBUG", "RTFLOG", 0))
  280. {
  281. _prtflg = new CRTFLog;
  282. if(_prtflg && !_prtflg->FInit())
  283. {
  284. delete _prtflg;
  285. _prtflg = NULL;
  286. }
  287. AssertSz(_prtflg, "CRTFRead::CRTFRead: Error creating RTF log");
  288. }
  289. #endif
  290. #endif // DEBUG
  291. }
  292. /*
  293. * CRTFRead::HandleStartGroup()
  294. *
  295. * @mfunc
  296. * Handle start of new group. Alloc and push new state onto state
  297. * stack
  298. *
  299. * @rdesc
  300. * EC The error code
  301. */
  302. EC CRTFRead::HandleStartGroup()
  303. {
  304. TRACEBEGIN(TRCSUBSYSRTFR, TRCSCOPEINTERN, "CRTFRead::HandleStartGroup");
  305. STATE * pstate = _pstateStackTop;
  306. STATE * pstateNext = NULL;
  307. if(pstate) // At least one STATE already
  308. { // allocated
  309. Apply_CF(); // Apply any collected char
  310. // Note (igorzv) we don't Apply_PF() here so as not to change para
  311. // properties before we run into \par i.e. not to use paragraph
  312. // properties if we copy only one word from paragraph. We can use an
  313. // assertion here that neither we nor Word use end of group for
  314. // restoring paragraph properties. So everything will be OK with stack
  315. pstate->iCF = (SHORT)_prg->Get_iCF(); // Save current CF
  316. pstate = pstate->pstateNext; // Use previously allocated
  317. if(pstate) // STATE frame if it exists
  318. pstateNext = pstate->pstateNext; // It does; save its forward
  319. } // link for restoration below
  320. if(!pstate) // No new STATE yet: alloc one
  321. {
  322. pstate = new STATE(IsUTF8 ? CP_UTF8 : _nCodePage);
  323. if(!pstate) // Couldn't alloc new STATE
  324. goto memerror;
  325. _pstateLast = pstate; // Update ptr to last STATE
  326. } // alloc'd
  327. STATE *pstateGetsPF;
  328. // Apply the accumulated PF delta's to the old current state or, if there
  329. // is no current state, to the newly created state.
  330. pstateGetsPF = _pstateStackTop ? _pstateStackTop : pstate;
  331. if(!pstateGetsPF->AddPF(_PF, _bDocType, _dwMaskPF))
  332. goto memerror;
  333. _dwMaskPF = 0; // _PF contains delta's from *_pstateStackTop->pPF
  334. if(_pstateStackTop) // There's a previous STATE
  335. {
  336. *pstate = *_pstateStackTop; // Copy current state info
  337. // N.B. This will cause the current and previous state to share
  338. // the same PF. PF delta's are accumulated in _PF. A new PF
  339. // is created for _pstateStackTop when the _PF deltas are applied.
  340. _pstateStackTop->pstateNext = pstate;
  341. }
  342. pstate->pstatePrev = _pstateStackTop; // Link STATEs both ways
  343. pstate->pstateNext = pstateNext;
  344. _pstateStackTop = pstate; // Push stack
  345. done:
  346. TRACEERRSZSC("HandleStartGroup()", -_ecParseError);
  347. return _ecParseError;
  348. memerror:
  349. _ped->GetCallMgr()->SetOutOfMemory();
  350. _ecParseError = ecStackOverflow;
  351. goto done;
  352. }
  353. /*
  354. * CRTFRead::HandleEndGroup()
  355. *
  356. * @mfunc
  357. * Handle end of new group
  358. *
  359. * @rdesc
  360. * EC The error code
  361. */
  362. EC CRTFRead::HandleEndGroup()
  363. {
  364. TRACEBEGIN(TRCSUBSYSRTFR, TRCSCOPEINTERN, "CRTFRead::HandleEndGroup");
  365. STATE * pstate = _pstateStackTop;
  366. STATE * pstatePrev;
  367. Assert(_PF._iTabs == -1);
  368. if(!pstate) // No stack to pop
  369. {
  370. _ecParseError = ecStackUnderflow;
  371. goto done;
  372. }
  373. _pstateStackTop = // Pop stack
  374. pstatePrev = pstate->pstatePrev;
  375. if(!pstatePrev)
  376. {
  377. Assert(pstate->pPF);
  378. // We're ending the parse. Copy the final PF into _PF so that
  379. // subsequent calls to Apply_PF will have a PF to apply.
  380. _PF = *pstate->pPF;
  381. _dwMaskPF = pstate->dwMaskPF;
  382. _PF._iTabs = -1; // Force recache
  383. _PF._wEffects &= ~PFE_TABLE;
  384. }
  385. // Adjust the PF for the new _pstateStackTop and delete unused PF's.
  386. if(pstate->sDest == destParaNumbering || pstate->sDest == destParaNumText)
  387. {
  388. if(pstatePrev && pstate->pPF != pstatePrev->pPF)
  389. {
  390. // Bleed the PF of the current state into the previous state for
  391. // paragraph numbering groups
  392. Assert(pstatePrev->pPF);
  393. pstatePrev->DeletePF();
  394. pstatePrev->pPF = pstate->pPF;
  395. pstate->pPF = NULL;
  396. }
  397. else
  398. pstate->DeletePF();
  399. // N.B. Here, we retain the _PF diffs since they apply to the
  400. // enclosing group along with the PF of the group we are leaving
  401. }
  402. else
  403. {
  404. // We're popping the state, so delete its PF and discard the _PF diffs
  405. Assert(pstate->pPF);
  406. pstate->DeletePF();
  407. // If !pstatePrev, we're ending the parse, in which case the _PF
  408. // structure contains final PF (so don't toast it).
  409. if(pstatePrev)
  410. _dwMaskPF = 0;
  411. }
  412. if(pstatePrev)
  413. {
  414. _dwMaskCF = 0; // Discard any CF deltas
  415. _dwMaskCF2 = 0;
  416. switch(pstate->sDest)
  417. {
  418. case destParaNumbering:
  419. // {\pn ...}
  420. pstatePrev->sIndentNumbering = pstate->sIndentNumbering;
  421. pstatePrev->fBullet = pstate->fBullet;
  422. break;
  423. case destObject:
  424. // clear our object flags just in case we have corrupt RTF
  425. if(_fNeedPres)
  426. {
  427. _fNeedPres = FALSE;
  428. _fNeedIcon = FALSE;
  429. _pobj = NULL;
  430. }
  431. break;
  432. case destFontTable:
  433. if(pstatePrev->sDest == destFontTable)
  434. {
  435. // We're actually leaving a sub-group within the \fonttbl
  436. // group.
  437. break;
  438. }
  439. // We're leaving the {\fonttbl...} group.
  440. _fSeenFontTable = TRUE;
  441. // Default font should now be defined, so select it (this
  442. // creates CF deltas).
  443. SetPlain(pstate);
  444. // Ensure that a document-level codepage has been determined and
  445. // then scan the font names and retry the conversion to Unicode,
  446. // if necessary.
  447. if(_nCodePage == INVALID_CODEPAGE)
  448. {
  449. // We haven't determined a document-level codepage
  450. // from the \ansicpgN tag, nor from the font table
  451. // \fcharsetN and \cpgN values. As a last resort,
  452. // let's use the \deflangN and \deflangfeN tags
  453. LANGID langid;
  454. if(_sDefaultLanguageFE != INVALID_LANGUAGE)
  455. langid = _sDefaultLanguageFE;
  456. else if(_sDefaultLanguage != INVALID_LANGUAGE &&
  457. _sDefaultLanguage != sLanguageEnglishUS)
  458. {
  459. // _sDefaultLanguage == sLanguageEnglishUS is inreliable
  460. // in the absence of \deflangfeN. Many FE RTF writers
  461. // write \deflang1033 (sLanguageEnglishUS).
  462. langid = _sDefaultLanguage;
  463. }
  464. else if(_dwFlags & SFF_SELECTION)
  465. {
  466. // For copy/paste case, if nothing available, try the system
  467. // default langid. This is to fix FE Excel95 problem.
  468. langid = GetSystemDefaultLangID();
  469. }
  470. else
  471. goto NoLanguageInfo;
  472. _nCodePage = ConvertLanguageIDtoCodePage(langid);
  473. }
  474. NoLanguageInfo:
  475. if(_nCodePage == INVALID_CODEPAGE)
  476. break;
  477. // Fixup mis-converted font face names
  478. TEXTFONT *ptf;
  479. LONG i;
  480. for(i = 0; i < _fonts.Count(); i++)
  481. {
  482. ptf = _fonts.Elem(i);
  483. if (ptf->sCodePage == INVALID_CODEPAGE ||
  484. ptf->sCodePage == CP_SYMBOL)
  485. {
  486. if(ptf->fNameIsDBCS)
  487. {
  488. char szaTemp[LF_FACESIZE];
  489. BOOL fMissingCodePage;
  490. // Un-convert mis-converted face name
  491. SideAssert(WCTMB(ptf->sCodePage, 0,
  492. ptf->szName, -1,
  493. szaTemp, sizeof(szaTemp),
  494. NULL, NULL, &fMissingCodePage) > 0);
  495. Assert(ptf->sCodePage == CP_SYMBOL ||
  496. fMissingCodePage);
  497. // re-convert face name using new codepage info
  498. SideAssert(MBTWC(_nCodePage, 0,
  499. szaTemp, -1,
  500. ptf->szName, sizeof(ptf->szName),
  501. &fMissingCodePage) > 0);
  502. if(!fMissingCodePage)
  503. ptf->fNameIsDBCS = FALSE;
  504. }
  505. }
  506. }
  507. break;
  508. default:;
  509. // nothing
  510. }
  511. _prg->Set_iCF(pstatePrev->iCF); // Restore previous CharFormat
  512. ReleaseFormats(pstatePrev->iCF, -1);
  513. }
  514. done:
  515. TRACEERRSZSC("HandleEndGroup()", - _ecParseError);
  516. return _ecParseError;
  517. }
  518. /*
  519. * CRTFRead::HandleFieldEndGroup()
  520. *
  521. * @mfunc
  522. * Handle end of \field
  523. *
  524. */
  525. void CRTFRead::HandleFieldEndGroup()
  526. {
  527. STATE * pstate = _pstateStackTop;
  528. if(pstate->sDest == destField)
  529. {
  530. // for SYMBOLS
  531. if(!_fHyperlinkField)
  532. {
  533. if(_szSymbolFieldResult) // There is a new field result
  534. {
  535. if(_fRestoreFieldFormat)
  536. {
  537. _fRestoreFieldFormat = FALSE;
  538. _CF = _FieldCF;
  539. pstate->ptf = _ptfField;
  540. pstate->SetCodePage(_nFieldCodePage);
  541. _dwMaskCF = _dwMaskFieldCF;
  542. _dwMaskCF2 = _dwMaskFieldCF2;
  543. }
  544. HandleText(_szSymbolFieldResult, CONTAINS_NONASCII);
  545. FreePv(_szSymbolFieldResult);
  546. _szSymbolFieldResult =NULL;
  547. }
  548. }
  549. else if(pstate->pstateNext)
  550. {
  551. // Setup formatting for the field result
  552. _CF = _FieldCF;
  553. pstate->ptf = _ptfField;
  554. pstate->SetCodePage(_nFieldCodePage);
  555. _dwMaskCF = _dwMaskFieldCF;
  556. _dwMaskCF2 = _dwMaskFieldCF2;
  557. // for HYPERLINK
  558. if(_szHyperlinkFldrslt || _szHyperlinkFldinst)
  559. {
  560. // We have the final hyperlink fldrslt string.
  561. // Check if it is the same as the friendly name
  562. if (_szHyperlinkFldrslt && _szHyperlinkFldinst &&
  563. _szHyperlinkFldinst[1] == '<' &&
  564. !CompareMemory(
  565. (char*)_szHyperlinkFldrslt,
  566. (char*)&_szHyperlinkFldinst[2],
  567. _cchHyperlinkFldrsltUsed - 1))
  568. {
  569. // They are the same, only need to output friendly name
  570. HandleText(&_szHyperlinkFldinst[1], CONTAINS_NONASCII, _cchHyperlinkFldinstUsed);
  571. }
  572. else
  573. {
  574. // Output result string
  575. if(_szHyperlinkFldrslt)
  576. HandleText(_szHyperlinkFldrslt, CONTAINS_NONASCII, _cchHyperlinkFldrsltUsed);
  577. // Output friendly name
  578. if(_szHyperlinkFldinst)
  579. HandleText(_szHyperlinkFldinst, CONTAINS_NONASCII, _cchHyperlinkFldinstUsed);
  580. }
  581. FreePv(_szHyperlinkFldinst);
  582. FreePv(_szHyperlinkFldrslt);
  583. _szHyperlinkFldinst = NULL;
  584. _szHyperlinkFldrslt = NULL;
  585. _fHyperlinkField = FALSE;
  586. }
  587. }
  588. }
  589. else if(pstate->sDest == destFieldResult && _fHyperlinkField)
  590. {
  591. // Save the current formatting for the field result if dwMask is valid.
  592. // NOTE: HandleEndGroup will zero out _dwMaskCF
  593. if(_dwMaskCF)
  594. {
  595. // We should use FE charset in case of mixed of FE and non-FE in the url
  596. // Also, only use codepage other than English in case of a mixed of English
  597. // and non-English (e.g. English and Russian )
  598. if (!IsFECharSet(_FieldCF._bCharSet) && IsFECharSet(_CF._bCharSet) ||
  599. _nFieldCodePage != pstate->nCodePage && _nFieldCodePage == 1252 ||
  600. _FieldCF._bCharSet == _CF._bCharSet && _nFieldCodePage == pstate->nCodePage)
  601. {
  602. _FieldCF = _CF;
  603. _ptfField = pstate->ptf;
  604. _nFieldCodePage = pstate->nCodePage;
  605. _dwMaskFieldCF = _dwMaskCF;
  606. _dwMaskFieldCF2 = _dwMaskCF2;
  607. }
  608. }
  609. }
  610. }
  611. /*
  612. * CRTFRead::SelectCurrentFont(iFont)
  613. *
  614. * @mfunc
  615. * Set active font to that with index <p iFont>. Take into account
  616. * bad font numbers.
  617. */
  618. void CRTFRead::SelectCurrentFont(
  619. INT iFont) //@parm font handle of font to select
  620. {
  621. TRACEBEGIN(TRCSUBSYSRTFR, TRCSCOPEINTERN, "CRTFRead::SelectCurrentFont");
  622. LONG i = _fonts.Count();
  623. STATE * pstate = _pstateStackTop;
  624. TEXTFONT * ptf = _fonts.Elem(0);
  625. AssertSz(i, "CRTFRead::SelectCurrentFont: bad font collection");
  626. for(; i-- && iFont != ptf->sHandle; ptf++) // Search for font with handle
  627. ; // iFont
  628. // Font handle not found: use default, which is valid
  629. // since \rtf copied _prg's
  630. if(i < 0)
  631. ptf = _fonts.Elem(0);
  632. BOOL fDefFontFromSystem = (i == (LONG)_fonts.Count() - 1 || i < 0) &&
  633. !_fReadDefFont;
  634. _CF._iFont = GetFontNameIndex(ptf->szName);
  635. _dwMaskCF2 |= CFM2_FACENAMEISDBCS;
  636. _CF._dwEffects &= ~CFE_FACENAMEISDBCS;
  637. if(ptf->fNameIsDBCS)
  638. _CF._dwEffects |= CFE_FACENAMEISDBCS;
  639. if(pstate->sDest != destFontTable)
  640. {
  641. _CF._bCharSet = ptf->bCharSet;
  642. _CF._bPitchAndFamily = ptf->bPitchAndFamily;
  643. _dwMaskCF |= CFM_FACE | CFM_CHARSET;
  644. if (IsRTLCharSet(_CF._bCharSet) && ptf->sCodePage == 1252)
  645. ptf->sCodePage = (SHORT)GetCodePage(_CF._bCharSet); // Fix sCodePage to match charset
  646. }
  647. if (_ped->Get10Mode() && !_fSeenFontTable
  648. && _nCodePage == INVALID_CODEPAGE && ptf->sCodePage == 1252)
  649. {
  650. if (W32->IsFECodePage(GetACP()))
  651. _nCodePage = GetACP();
  652. }
  653. // Ensure that the state's codepage is not supplied by the system.
  654. // That is, if we are using the codepage info from the default font,
  655. // be sure that the default font info was read from the RTF file.
  656. pstate->SetCodePage((fDefFontFromSystem && _nCodePage != INVALID_CODEPAGE) ||
  657. ptf->sCodePage == INVALID_CODEPAGE
  658. ? _nCodePage : ptf->sCodePage);
  659. pstate->ptf = ptf;
  660. #ifdef CHICAGO
  661. // Win95c 1719: try to match a language to the char set when RTF
  662. // doesn't explicitly set a language
  663. if (!pstate->fExplicitLang && ptf->bCharSet != ANSI_CHARSET &&
  664. (!pstate->sLanguage || pstate->sLanguage == sLanguageEnglishUS))
  665. {
  666. i = AttIkliFromCharset(_ped, ptf->bCharSet);
  667. if(i >= 0)
  668. pstate->sLanguage = LOWORD(rgkli[i].hkl);
  669. }
  670. #endif // CHICAGO
  671. }
  672. /*
  673. * CRTFRead::SetPlain(pstate)
  674. *
  675. * @mfunc
  676. * Setup _CF for \plain
  677. */
  678. void CRTFRead::SetPlain(
  679. STATE *pstate)
  680. {
  681. ZeroMemory(&_CF, sizeof(CCharFormat));
  682. _dwMaskCF = CFM_ALL2;
  683. if(_dwFlags & SFF_SELECTION && _prg->GetCp() == _cpFirst && !_fCharSet)
  684. {
  685. // Let NT 4.0 CharMap use insertion point size
  686. _CF._yHeight = _ped->GetCharFormat(_prg->Get_iFormat())->_yHeight;
  687. }
  688. else
  689. _CF._yHeight = PointsToFontHeight(yDefaultFontSize);
  690. _CF._dwEffects = CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR; // Set default effects
  691. if(_sDefaultLanguage == INVALID_LANGUAGE)
  692. _dwMaskCF &= ~CFM_LCID;
  693. else
  694. _CF._lcid = MAKELCID((WORD)_sDefaultLanguage, SORT_DEFAULT);
  695. _CF._bUnderlineType = CFU_UNDERLINE;
  696. SelectCurrentFont(_sDefaultFont);
  697. // TODO: get rid of pstate->sLanguage, since CHARFORMAT2 has lcid
  698. pstate->sLanguage = _sDefaultLanguage;
  699. pstate->fExplicitLang = FALSE;
  700. }
  701. /*
  702. * CRTFRead::ReadFontName(pstate, iAllASCII)
  703. *
  704. * @mfunc
  705. * read font name _szText into <p pstate>->ptf->szName and deal with
  706. * tagged fonts
  707. */
  708. void CRTFRead::ReadFontName(
  709. STATE * pstate, //@parm state whose font name is to be read into
  710. int iAllASCII) //@parm indicates that _szText is all ASCII chars
  711. {
  712. TRACEBEGIN(TRCSUBSYSRTFR, TRCSCOPEINTERN, "CRTFRead::ReadFontName");
  713. if (pstate->ptf)
  714. {
  715. INT cchName = LF_FACESIZE - 1;
  716. TCHAR * pchDst = pstate->ptf->szName;
  717. char * pachName = (char *)_szText ;
  718. // Append additional text from _szText to TEXTFONT::szName
  719. // We need to append here since some RTF writers decide
  720. // to break up a font name with other RTF groups
  721. while(*pchDst && cchName > 0)
  722. {
  723. pchDst++;
  724. cchName--;
  725. }
  726. if(!cchName) // Probably illegal file
  727. { // e.g., extra {
  728. return;
  729. }
  730. INT cchLimit = cchName;
  731. BOOL fTaggedName = FALSE;
  732. while (*pachName &&
  733. *pachName != ';' &&
  734. cchLimit) // Remove semicolons
  735. {
  736. pachName++;
  737. cchLimit--;
  738. if (*pachName == '(')
  739. fTaggedName = TRUE;
  740. }
  741. *pachName = '\0';
  742. // Use the codepage of the font in all cases except where the font uses
  743. // the symbol charset (and the codepage has been mapped from the charset)
  744. // and UTF-8 isn't being used
  745. LONG nCodePage = pstate->nCodePage != CP_SYMBOL
  746. ? pstate->nCodePage : _nCodePage;
  747. BOOL fMissingCodePage;
  748. Assert(!IsUTF8 || nCodePage == CP_UTF8);
  749. INT cch = MBTWC(nCodePage, 0,
  750. (char *)_szText, -1,
  751. pchDst, cchName, &fMissingCodePage);
  752. if(cch > 0 && fMissingCodePage && iAllASCII == CONTAINS_NONASCII)
  753. pstate->ptf->fNameIsDBCS = TRUE;
  754. else if(pstate->ptf->bCharSet == DEFAULT_CHARSET &&
  755. W32->IsFECodePage(nCodePage) &&
  756. GetTrailBytesCount(*_szText, nCodePage))
  757. pstate->ptf->bCharSet = GetCharSet(nCodePage); // Fix up the charset
  758. // Make sure destination is null terminated
  759. if(cch > 0)
  760. pchDst[cch] = 0;
  761. // Fall through even if MBTWC <= 0, since we may be appending text to an
  762. // existing font name.
  763. if(pstate->ptf == _fonts.Elem(0)) // If it's the default font,
  764. SelectCurrentFont(_sDefaultFont); // update _CF accordingly
  765. TCHAR * szNormalName;
  766. if(pstate->ptf->bCharSet && pstate->fRealFontName)
  767. {
  768. // if we don't know about this font don't use the real name
  769. if(!FindTaggedFont(pstate->ptf->szName,
  770. pstate->ptf->bCharSet, &szNormalName))
  771. {
  772. pstate->fRealFontName = FALSE;
  773. pstate->ptf->szName[0] = 0;
  774. }
  775. }
  776. else if(IsTaggedFont(pstate->ptf->szName,
  777. &pstate->ptf->bCharSet, &szNormalName))
  778. {
  779. wcscpy(pstate->ptf->szName, szNormalName);
  780. pstate->ptf->sCodePage = (SHORT)GetCodePage(pstate->ptf->bCharSet);
  781. pstate->SetCodePage(pstate->ptf->sCodePage);
  782. }
  783. else if(fTaggedName && !fMissingCodePage)
  784. {
  785. // Fix up tagged name by removing characters after the ' ('
  786. INT i = 0;
  787. WCHAR *pwchTag = pstate->ptf->szName;
  788. while (pwchTag[i] && pwchTag[i] != L'(') // Search for '('
  789. i++;
  790. if(pwchTag[i] && i > 0)
  791. {
  792. while (i > 0 && pwchTag[i-1] == 0x20) // Remove spaces before the '('
  793. i--;
  794. pwchTag[i] = 0;
  795. }
  796. }
  797. }
  798. }
  799. /*
  800. * CRTFRead::GetColor (dwMask)
  801. *
  802. * @mfunc
  803. * Store the autocolor or autobackcolor effect bit and return the
  804. * COLORREF for color _iParam
  805. *
  806. * @rdesc
  807. * COLORREF for color _iParam
  808. *
  809. * @devnote
  810. * If the entry in _colors corresponds to tomAutoColor, gets the value
  811. * RGB(0,0,0) (since no \red, \green, and \blue fields are used), but
  812. * isn't used by the RichEdit engine. Entry 1 corresponds to the first
  813. * explicit entry in the \colortbl and is usually RGB(0,0,0). The _colors
  814. * table is built by HandleToken() when it handles the token tokenText
  815. * for text consisting of a ';' for a destination destColorTable.
  816. */
  817. COLORREF CRTFRead::GetColor(
  818. DWORD dwMask) //@parm Color mask bit
  819. {
  820. TRACEBEGIN(TRCSUBSYSRTFR, TRCSCOPEINTERN, "CRTFRead::GetColor");
  821. if(_iParam >= _colors.Count()) // Illegal _iParam
  822. return RGB(0,0,0);
  823. _dwMaskCF |= dwMask; // Turn on appropriate mask bit
  824. _CF._dwEffects &= ~dwMask; // auto(back)color off: color is to be used
  825. COLORREF Color = *_colors.Elem(_iParam);
  826. if(Color == tomAutoColor)
  827. {
  828. _CF._dwEffects |= dwMask; // auto(back)color on
  829. Color = RGB(0,0,0);
  830. }
  831. return Color;
  832. }
  833. /*
  834. * CRTFRead::GetStandardColorIndex ()
  835. *
  836. * @mfunc
  837. * Return the color index into the standard 16-entry Word \colortbl
  838. * corresponding to the color index _iParam for the current \colortbl
  839. *
  840. * @rdesc
  841. * Standard color index corresponding to the color associated with _iParam
  842. */
  843. LONG CRTFRead::GetStandardColorIndex()
  844. {
  845. TRACEBEGIN(TRCSUBSYSRTFR, TRCSCOPEINTERN, "CRTFRead::GetColorIndex");
  846. if(_iParam >= _colors.Count()) // Illegal _iParam:
  847. return 0; // use autocolor
  848. COLORREF Color = *_colors.Elem(_iParam);
  849. for(LONG i = 0; i < 16; i++)
  850. {
  851. if(Color == g_Colors[i])
  852. return i + 1;
  853. }
  854. return 0; // Not there: use autocolor
  855. }
  856. /*
  857. * CRTFRead::HandleChar(ch)
  858. *
  859. * @mfunc
  860. * Handle single Unicode character <p ch>
  861. *
  862. * @rdesc
  863. * EC The error code
  864. */
  865. EC CRTFRead::HandleChar(
  866. WCHAR ch) //@parm char token to be handled
  867. {
  868. TRACEBEGIN(TRCSUBSYSRTFR, TRCSCOPEINTERN, "CRTFRead::HandleChar");
  869. if(!_ped->_pdp->IsMultiLine() && IsASCIIEOP(ch))
  870. _ecParseError = ecTruncateAtCRLF;
  871. else
  872. {
  873. AssertNr(ch <= 0x7F || ch > 0xFF || FTokIsSymbol(ch));
  874. _dwMaskCF2 |= CFM2_RUNISDBCS;
  875. _CF._dwEffects &= ~CFE_RUNISDBCS;
  876. AddText(&ch, 1, CharGetsNumbering(ch));
  877. }
  878. TRACEERRSZSC("HandleChar()", - _ecParseError);
  879. return _ecParseError;
  880. }
  881. /*
  882. * CRTFRead::HandleEndOfPara()
  883. *
  884. * @mfunc
  885. * Insert EOP and apply current paraformat
  886. *
  887. * @rdesc
  888. * EC the error code
  889. */
  890. EC CRTFRead::HandleEndOfPara()
  891. {
  892. TRACEBEGIN(TRCSUBSYSRTFR, TRCSCOPEINTERN, "CRTFRead::HandleEndOfPara");
  893. if(_pstateStackTop->fInTable) // Our simple table model can't
  894. { // have numbering
  895. _PF._wNumbering = 0;
  896. _dwMaskPF |= PFM_NUMBERING;
  897. }
  898. if(!_ped->_pdp->IsMultiLine()) // No EOPs permitted in single-
  899. { // line controls
  900. Apply_PF(); // Apply any paragraph formatting
  901. _ecParseError = ecTruncateAtCRLF; // Cause RTF reader to finish up
  902. return ecTruncateAtCRLF;
  903. }
  904. Apply_CF(); // Apply _CF and save iCF, since
  905. LONG iFormat = _prg->Get_iCF(); // it gets changed if processing
  906. // CFE2_RUNISDBCS chars
  907. EC ec = _ped->fUseCRLF() // If RichEdit 1.0 compatibility
  908. ? HandleText(szaCRLF, ALL_ASCII) // mode, use CRLF; else CR or VT
  909. : HandleChar((unsigned)(_token == tokenLineBreak ? VT :
  910. _token == tokenPage ? FF : CR));
  911. if(ec == ecNoError)
  912. {
  913. Apply_PF();
  914. _cpThisPara = _prg->GetCp(); // New para starts after CRLF
  915. }
  916. _prg->Set_iCF(iFormat); // Restore iFormat if changed
  917. ReleaseFormats(iFormat, -1); // Release iFormat (AddRef'd by
  918. // Get_iCF())
  919. return _ecParseError;
  920. }
  921. /*
  922. * CRTFRead::HandleText(szText, iAllASCII)
  923. *
  924. * @mfunc
  925. * Handle the string of Unicode characters <p szText>
  926. *
  927. * @rdesc
  928. * EC The error code
  929. */
  930. EC CRTFRead::HandleText(
  931. BYTE * szText, //@parm string to be handled
  932. int iAllASCII, //@parm enum indicating if string is all ASCII chars
  933. LONG cchText) //@parm size of szText in bytes
  934. {
  935. TRACEBEGIN(TRCSUBSYSRTFR, TRCSCOPEINTERN, "CRTFRead::HandleText");
  936. LONG cch;
  937. BOOL fStateChng = FALSE;
  938. TCHAR * pch;
  939. STATE * pstate = _pstateStackTop;
  940. TEXTFONT * ptf = pstate->ptf;
  941. struct TEXTFONTSAVE : TEXTFONT
  942. {
  943. TEXTFONTSAVE(TEXTFONT *ptf)
  944. {
  945. if (ptf)
  946. {
  947. bCharSet = ptf->bCharSet;
  948. sCodePage = ptf->sCodePage;
  949. fCpgFromSystem = ptf->fCpgFromSystem;
  950. }
  951. }
  952. };
  953. BOOL fAdjustPtf = FALSE;
  954. if(pstate->fltrch || pstate->frtlch)
  955. {
  956. // CharSet resolution based on directional control words
  957. if(_CF._bCharSet == DEFAULT_CHARSET)
  958. {
  959. _CF._bCharSet = (BYTE)(pstate->fltrch
  960. ? ANSI_CHARSET : _bBiDiCharSet);
  961. _dwMaskCF |= CFM_CHARSET;
  962. fAdjustPtf = TRUE;
  963. }
  964. else
  965. {
  966. BOOL fBiDiCharSet = IsRTLCharSet(_CF._bCharSet);
  967. // If direction token doesn't correspond to current charset
  968. if(fBiDiCharSet ^ pstate->frtlch)
  969. {
  970. _dwMaskCF |= CFM_CHARSET;
  971. fAdjustPtf = TRUE;
  972. if(!fBiDiCharSet) // Wasn't BiDi, but is now
  973. SelectCurrentFont(_sDefaultBiDiFont);
  974. _CF._bCharSet = (BYTE)(pstate->frtlch
  975. ? _bBiDiCharSet : ANSI_CHARSET);
  976. }
  977. else if (fBiDiCharSet && ptf && !W32->IsBiDiCodePage(ptf->sCodePage))
  978. fAdjustPtf = TRUE;
  979. }
  980. }
  981. else if(_ped->IsBiDi() && _CF._bCharSet == DEFAULT_CHARSET)
  982. {
  983. _CF._bCharSet = ANSI_CHARSET;
  984. _dwMaskCF |= CFM_CHARSET;
  985. fAdjustPtf = TRUE;
  986. }
  987. if (fAdjustPtf && ptf)
  988. {
  989. ptf->sCodePage = (SHORT)GetCodePage(_CF._bCharSet);
  990. pstate->SetCodePage(ptf->sCodePage);
  991. }
  992. TEXTFONTSAVE tfSave(ptf);
  993. // TODO: what if szText cuts off in middle of DBCS?
  994. if(!*szText)
  995. goto CleanUp;
  996. if (cchText != -1 && _cchUnicode < cchText)
  997. {
  998. // Re-allocate a bigger buffer
  999. _szUnicode = (TCHAR *)PvReAlloc(_szUnicode, (cchText + 1) * sizeof(TCHAR));
  1000. if(!_szUnicode) // Re-allocate space for Unicode conversions
  1001. {
  1002. _ped->GetCallMgr()->SetOutOfMemory();
  1003. _ecParseError = ecNoMemory;
  1004. goto CleanUp;
  1005. }
  1006. _cchUnicode = cchText + 1;
  1007. }
  1008. if(iAllASCII == ALL_ASCII || pstate->nCodePage == CP_SYMBOL)
  1009. {
  1010. // Don't use MBTWC() in cases where text contains
  1011. // only ASCII chars (which don't require conversion)
  1012. for(cch = 0, pch = _szUnicode; *szText; cch++)
  1013. {
  1014. Assert(*szText <= 0x7F || _CF._bCharSet == SYMBOL_CHARSET);
  1015. *pch++ = (TCHAR)*szText++;
  1016. }
  1017. *pch = 0;
  1018. _dwMaskCF2 |= CFM2_RUNISDBCS;
  1019. _CF._dwEffects &= ~CFE_RUNISDBCS;
  1020. // Fall through to AddText at end of HandleText()
  1021. }
  1022. else
  1023. {
  1024. BOOL fMissingCodePage;
  1025. // Run of text contains bytes > 0x7F.
  1026. // Ensure that we have the correct codepage with which to interpret
  1027. // these (possibly DBCS) bytes.
  1028. if(ptf && ptf->sCodePage == INVALID_CODEPAGE && !ptf->fCpgFromSystem)
  1029. {
  1030. if(_dwFlags & SF_USECODEPAGE)
  1031. {
  1032. _CF._bCharSet = GetCharSet(_nCodePage);
  1033. _dwMaskCF |= CFM_CHARSET;
  1034. }
  1035. // Determine codepage from the font name
  1036. else if(CpgInfoFromFaceName(pstate->ptf))
  1037. {
  1038. fStateChng = TRUE;
  1039. SelectCurrentFont(pstate->ptf->sHandle);
  1040. Assert(ptf->sCodePage != INVALID_CODEPAGE);
  1041. Assert(ptf->fCpgFromSystem);
  1042. }
  1043. else
  1044. {
  1045. // Here, we were not able to determine a cpg/charset value
  1046. // from the font name. We have two choices: (1) either choose
  1047. // some fallback value like 1252/0 or (2) rely on the
  1048. // document-level cpg value.
  1049. //
  1050. // I think choosing the document-level cpg value will give
  1051. // us the best results. In FE cases, it will likely err
  1052. // on the side of tagging too many runs as CFE2_RUNISDBCS, but
  1053. // that is safer than using a western cpg and potentially missing
  1054. // runs which should be CFE2_RUNISDBCS.
  1055. }
  1056. }
  1057. Assert(!IsUTF8 || pstate->nCodePage == CP_UTF8);
  1058. if (pstate->nCodePage == INVALID_CODEPAGE && _ped->Get10Mode() && ptf)
  1059. pstate->nCodePage = ptf->sCodePage;
  1060. cch = MBTWC(pstate->nCodePage, 0,
  1061. (char *)szText, -1,
  1062. _szUnicode, _cchUnicode, &fMissingCodePage);
  1063. AssertSz(cch > 0, "CRTFRead::HandleText(): MBTWC implementation changed"
  1064. " such that it returned a value <= 0");
  1065. if(!fMissingCodePage || !W32->IsFECodePage(pstate->nCodePage))
  1066. {
  1067. // Use result of MBTWC if:
  1068. // (1) we converted some chars and did the conversion with the codepage
  1069. // provided.
  1070. // (2) we converted some chars but couldn't use the codepage provided,
  1071. // but the codepage is invalid. Since the codepage is invalid,
  1072. // we can't do anything more sophisticated with the text before
  1073. // adding to the backing store
  1074. cch--; // don't want char count to including terminating NULL
  1075. _dwMaskCF2 |= CFM2_RUNISDBCS;
  1076. _CF._dwEffects &= ~CFE_RUNISDBCS;
  1077. if(pstate->nCodePage == INVALID_CODEPAGE)
  1078. _CF._dwEffects |= CFE_RUNISDBCS;
  1079. // fall through to AddText at end of HandleText()
  1080. }
  1081. else
  1082. {
  1083. // Conversion to Unicode failed. Break up the string of
  1084. // text into runs of ASCII and non-ASCII characters.
  1085. // FUTURE(BradO): Here, I am saving dwMask and restoring it before
  1086. // each AddText. I'm not sure this is neccessary. When I have
  1087. // the time, I should revisit this save/restoring and
  1088. // determine that it is indeed neccessary.
  1089. BOOL fPrevIsASCII = ((*szText <= 0x7F) ? TRUE : FALSE);
  1090. BOOL fCurrentIsASCII = FALSE;
  1091. BOOL fLastChunk = FALSE;
  1092. DWORD dwMaskSave = _dwMaskCF;
  1093. #if defined(DEBUG) || defined(_RELEASE_ASSERTS_)
  1094. CCharFormat CFSave = _CF;
  1095. #endif
  1096. pch = _szUnicode;
  1097. cch = 0;
  1098. // (!*szText && *pch) is the case where we do the AddText for the
  1099. // last chunk of text
  1100. while(*szText || fLastChunk)
  1101. {
  1102. // fCurrentIsASCII assumes that no byte <= 0x7F is a
  1103. // DBCS lead-byte
  1104. if(fLastChunk ||
  1105. (fPrevIsASCII != (fCurrentIsASCII = (*szText <= 0x7F))))
  1106. {
  1107. _dwMaskCF = dwMaskSave;
  1108. #if defined(DEBUG) || defined(_RELEASE_ASSERTS_)
  1109. _CF = CFSave;
  1110. #endif
  1111. *pch = 0;
  1112. _dwMaskCF2 |= CFM2_RUNISDBCS;
  1113. _CF._dwEffects |= CFE_RUNISDBCS;
  1114. if(fPrevIsASCII)
  1115. _CF._dwEffects &= ~CFE_RUNISDBCS;
  1116. Assert(cch);
  1117. pch = _szUnicode;
  1118. AddText(pch, cch, TRUE);
  1119. cch = 0;
  1120. fPrevIsASCII = fCurrentIsASCII;
  1121. // My assumption in saving _dwMaskCF is that the remainder
  1122. // of the _CF is unchanged by AddText. This assert verifies
  1123. // this assumption.
  1124. AssertSz(!CompareMemory(&CFSave._bCharSet, &_CF._bCharSet,
  1125. sizeof(CCharFormat) - sizeof(DWORD)) &&
  1126. !((CFSave._dwEffects ^ _CF._dwEffects) & ~CFE_RUNISDBCS),
  1127. "CRTFRead::HandleText(): AddText has been changed "
  1128. "and now alters the _CF structure.");
  1129. if(fLastChunk) // Last chunk of text was AddText'd
  1130. break;
  1131. }
  1132. // Not the last chunk of text.
  1133. Assert(*szText);
  1134. // Advance szText pointer
  1135. if (!fCurrentIsASCII && *(szText + 1) &&
  1136. GetTrailBytesCount(*szText, pstate->nCodePage))
  1137. {
  1138. // Current byte is a lead-byte of a DBCS character
  1139. *pch++ = *szText++;
  1140. ++cch;
  1141. }
  1142. *pch++ = *szText++;
  1143. ++cch;
  1144. // Must do an AddText for the last chunk of text
  1145. if(!*szText || cch >= _cchUnicode - 1)
  1146. fLastChunk = TRUE;
  1147. }
  1148. goto CleanUp;
  1149. }
  1150. }
  1151. if(cch > 0)
  1152. {
  1153. AddText(_szUnicode, cch, TRUE);
  1154. if(fStateChng && ptf)
  1155. {
  1156. ptf->bCharSet = tfSave.bCharSet;
  1157. ptf->sCodePage = tfSave.sCodePage;
  1158. ptf->fCpgFromSystem = tfSave.fCpgFromSystem;
  1159. SelectCurrentFont(ptf->sHandle);
  1160. }
  1161. }
  1162. CleanUp:
  1163. TRACEERRSZSC("HandleText()", - _ecParseError);
  1164. return _ecParseError;
  1165. }
  1166. /*
  1167. * CRTFRead::AddText(pch, cch, fNumber)
  1168. *
  1169. * @mfunc
  1170. * Add <p cch> chars of the string <p pch> to the range _prg
  1171. *
  1172. * @rdesc
  1173. * error code placed in _ecParseError
  1174. */
  1175. EC CRTFRead::AddText(
  1176. TCHAR * pch, //@parm text to add
  1177. LONG cch, //@parm count of chars to add
  1178. BOOL fNumber) //@parm indicates whether or not to prepend numbering
  1179. {
  1180. TRACEBEGIN(TRCSUBSYSRTFR, TRCSCOPEINTERN, "CRTFRead::AddText");
  1181. LONG cchAdded;
  1182. LONG cchT;
  1183. STATE * const pstate = _pstateStackTop;
  1184. TCHAR * szDest;
  1185. LONG cchMove;
  1186. // AROO: No saving state before this point (other than pstate)
  1187. // AROO: This would cause recursion problems below
  1188. AssertSz(pstate, "CRTFRead::AddText: no state");
  1189. if((DWORD)cch > _cchMax)
  1190. {
  1191. cch = (LONG)_cchMax;
  1192. _ecParseError = ecTextMax;
  1193. }
  1194. if(!cch)
  1195. return _ecParseError;
  1196. // FUTURE(BradO): This strategy for \pntext is prone to bugs, I believe.
  1197. // The recursive call to AddText to add the \pntext will trounce the
  1198. // accumulated _CF diffs associated with the text for which AddText is
  1199. // called. I believe we should save and restore _CF before and after
  1200. // the recursive call to AddText below. Also, it isn't sufficient to
  1201. // accumulate bits of \pntext as below, since each bit might be formatted
  1202. // with different _CF properties. Instead, we should accumulate a mini-doc
  1203. // complete with multiple text, char and para runs (or some stripped down
  1204. // version of this strategy).
  1205. if(pstate->sDest == destParaNumText)
  1206. {
  1207. szDest = _szNumText + _cchUsedNumText;
  1208. cch = min(cch, cchMaxNumText - 1 - _cchUsedNumText);
  1209. if(cch > 0)
  1210. {
  1211. MoveMemory((BYTE *)szDest, (BYTE *)pch, cch*2);
  1212. szDest[cch] = TEXT('\0'); // HandleText() takes sz
  1213. _cchUsedNumText += cch;
  1214. }
  1215. return ecNoError;
  1216. }
  1217. if(_cchUsedNumText && fNumber) // Some \pntext available
  1218. {
  1219. // Bug 3496 - The fNumber flag is an ugly hack to work around RTF
  1220. // commonly written by Word. Often, to separate a numbered list
  1221. // by page breaks, Word will write:
  1222. // <NUMBERING INFO> \page <PARAGRAPH TEXT>
  1223. // The paragraph numbering should precede the paragraph text rather
  1224. // than the page break. The fNumber flag is set to FALSE when the
  1225. // the text being added should not be prepended with the para-numbering,
  1226. // as is the case with \page (mapped to FF).
  1227. cchT = _cchUsedNumText;
  1228. _cchUsedNumText = 0; // Prevent infinite recursion
  1229. if(!pstate->fBullet)
  1230. {
  1231. // If there are any _CF diffs to be injected, they will be trounced
  1232. // by this recursive call (see FUTURE comment above).
  1233. // Since we didn't save _CF data from calls to AddText with
  1234. // pstate->sDest == destParaNumText, we have no way of setting up
  1235. // CFE2_RUNISDBCS and CFM2_RUNISDBCS (see FUTURE comment above).
  1236. AddText(_szNumText, cchT, FALSE);
  1237. }
  1238. else if(_PF.IsListNumbered() && _szNumText[cchT - 1] == TAB)
  1239. {
  1240. AssertSz(cchT >= 1, "Invalid numbered text count");
  1241. if (cchT > 1)
  1242. {
  1243. WCHAR ch = _szNumText[cchT - 2];
  1244. _wNumberingStyle = (_wNumberingStyle & ~0x300)
  1245. | (ch == '.' ? PFNS_PERIOD :
  1246. ch != ')' ? PFNS_PLAIN :
  1247. _szNumText[0] == '(' ? PFNS_PARENS : PFNS_PAREN);
  1248. }
  1249. else
  1250. {
  1251. // There is only a tab so we will assume they meant to
  1252. // skip numbering.
  1253. _wNumberingStyle = PFNS_NONUMBER;
  1254. }
  1255. }
  1256. }
  1257. if (_cpFirst && _prg->GetCp() == _cpFirst && _prg->GetPF()->InTable() &&
  1258. _cCell && !_prg->_rpTX.IsAfterEOP())
  1259. {
  1260. // FUTURE: handle more general table insertions into other tables
  1261. _iCell = 0;
  1262. return _ecParseError = ecGeneralFailure;
  1263. }
  1264. Apply_CF(); // Apply formatting changes in _CF
  1265. // BUGS 1577 & 1565 -
  1266. // CTxtRange::ReplaceRange will change the character formatting
  1267. // and possibly adjust the _rpCF forward if the current char
  1268. // formatting includes protection. The changes affected by
  1269. // CTxtRange::ReplaceRange are necessary only for non-streaming
  1270. // input, so we save state before and restore it after the call
  1271. // to CTxtRange::ReplaceRange
  1272. LONG iFormatSave = _prg->Get_iCF(); // Save state
  1273. if(_cbSkipForUnicode && pstate->ptf && pstate->ptf->sCodePage == INVALID_CODEPAGE &&
  1274. (!_fSeenFontTable || !(GetCharFlags(*pch) & fOTHER & ~255)))
  1275. {
  1276. // No charset info for \uN, so bind fonts if no font table or
  1277. // else if *pch isn't classifield as "other"
  1278. cchAdded = _prg->CleanseAndReplaceRange(cch, pch, FALSE, NULL, pch);
  1279. }
  1280. else
  1281. {
  1282. cchAdded = _prg->ReplaceRange(cch, pch, NULL, SELRR_IGNORE, &cchMove);
  1283. DWORD dwFlags = 0;
  1284. for(cchT = cch; cchT--; )
  1285. dwFlags |= GetCharFlags(*pch++); // Note if ComplexScript
  1286. _ped->OrCharFlags(dwFlags);
  1287. }
  1288. _prg->Set_iCF(iFormatSave); // Restore state
  1289. ReleaseFormats(iFormatSave, -1);
  1290. Assert(!_prg->GetCch());
  1291. if(cchAdded != cch)
  1292. {
  1293. Tracef(TRCSEVERR, "AddText(): Only added %d out of %d", cchAdded, cch);
  1294. _ecParseError = ecGeneralFailure;
  1295. if(cchAdded <= 0)
  1296. return _ecParseError;
  1297. }
  1298. _cchMax -= cchAdded;
  1299. return _ecParseError;
  1300. }
  1301. /*
  1302. * CRTFRead::Apply_CF()
  1303. *
  1304. * @mfunc
  1305. * Apply character formatting changes collected in _CF
  1306. */
  1307. void CRTFRead::Apply_CF()
  1308. {
  1309. // If any CF changes, update range's _iFormat
  1310. if(_dwMaskCF || _dwMaskCF2)
  1311. {
  1312. AssertSz(_prg->GetCch() == 0,
  1313. "CRTFRead::Apply_CF: nondegenerate range");
  1314. _prg->SetCharFormat(&_CF, 0, NULL, _dwMaskCF, _dwMaskCF2);
  1315. _dwMaskCF = 0;
  1316. _dwMaskCF2 = 0;
  1317. }
  1318. }
  1319. /*
  1320. * CRTFRead::Apply_PF()
  1321. *
  1322. * @mfunc
  1323. * Apply paragraph format given by _PF
  1324. */
  1325. void CRTFRead::Apply_PF()
  1326. {
  1327. LONG cp = _prg->GetCp();
  1328. DWORD dwMask = _dwMaskPF;
  1329. CParaFormat *pPF = &_PF;
  1330. if(_pstateStackTop)
  1331. {
  1332. Assert(_pstateStackTop->pPF);
  1333. // Add PF diffs to *_pstateStackTop->pPF
  1334. if(!_pstateStackTop->AddPF(_PF, _bDocType, _dwMaskPF))
  1335. {
  1336. _ped->GetCallMgr()->SetOutOfMemory();
  1337. _ecParseError = ecNoMemory;
  1338. return;
  1339. }
  1340. _dwMaskPF = 0; // _PF contains delta's from *_pstateStackTop->pPF
  1341. pPF = _pstateStackTop->pPF;
  1342. dwMask = _pstateStackTop->dwMaskPF;
  1343. Assert(dwMask == PFM_ALLRTF);
  1344. if(pPF->_wNumbering)
  1345. {
  1346. pPF->_wNumberingTab = _pstateStackTop->sIndentNumbering;
  1347. pPF->_wNumberingStyle = _wNumberingStyle;
  1348. }
  1349. }
  1350. if(dwMask & PFM_TABSTOPS)
  1351. {
  1352. LONG cTab = _cCell ? _cCell : _cTab;
  1353. // Caching a tabs array AddRefs the corresponding cached tabs entry.
  1354. // Be absolutely sure to release the entry before exiting the routine
  1355. // that caches it (see GetTabsCache()->Release at end of this funtion).
  1356. pPF->_iTabs = GetTabsCache()->Cache(_rgxCell, cTab);
  1357. if(pPF->InTable()) // Save _iTabs when associated
  1358. _iTabsTable = pPF->_iTabs; // with a table
  1359. AssertSz(!cTab || pPF->_iTabs >= 0,
  1360. "CRTFRead::Apply_PF: illegal pPF->_iTabs");
  1361. pPF->_bTabCount = cTab;
  1362. }
  1363. if (!(dwMask & PFM_TABSTOPS) || !pPF->_bTabCount)
  1364. pPF->_wEffects &= ~PFE_TABLE; // No tabs, no table
  1365. _prg->Set(cp, cp - _cpThisPara); // Select back to _cpThisPara
  1366. _prg->SetParaFormat(pPF, NULL, dwMask);
  1367. _prg->Set(cp, 0); // Restore _prg to an IP
  1368. GetTabsCache()->Release(pPF->_iTabs);
  1369. pPF->_iTabs = -1;
  1370. }
  1371. /*
  1372. * CRTFRead::SetBorderParm(&Parm, Value)
  1373. *
  1374. * @mfunc
  1375. * Set the border pen width in half points for the current border
  1376. * (_bBorder)
  1377. */
  1378. void CRTFRead::SetBorderParm(
  1379. WORD& Parm,
  1380. LONG Value)
  1381. {
  1382. Assert(_bBorder <= 3);
  1383. Value = min(Value, 15);
  1384. Value = max(Value, 0);
  1385. Parm &= ~(0xF << 4*_bBorder);
  1386. Parm |= Value << 4*_bBorder;
  1387. _dwMaskPF |= PFM_BORDER;
  1388. }
  1389. /*
  1390. * CRTFRead::HandleToken()
  1391. *
  1392. * @mfunc
  1393. * Grand switch board that handles all tokens. Switches on _token
  1394. *
  1395. * @rdesc
  1396. * EC The error code
  1397. *
  1398. * @comm
  1399. * Token values are chosen contiguously (see tokens.h and tokens.c) to
  1400. * encourage the compiler to use a jump table. The lite-RTF keywords
  1401. * come first, so that an optimized OLE-free version works well. Some
  1402. * groups of keyword tokens are ordered so as to simplify the code, e.g,
  1403. * those for font family names, CF effects, and paragraph alignment.
  1404. */
  1405. EC CRTFRead::HandleToken()
  1406. {
  1407. TRACEBEGIN(TRCSUBSYSRTFR, TRCSCOPEINTERN, "CRTFRead::HandleToken");
  1408. BYTE bT; // Temporary BYTE
  1409. DWORD dwT; // Temporary DWORD
  1410. LONG dy, i;
  1411. LONG iParam = _iParam;
  1412. const CCharFormat * pCF;
  1413. COLORREF * pclrf;
  1414. STATE * pstate = _pstateStackTop;
  1415. TEXTFONT * ptf;
  1416. WORD wT; // Temporary WORD
  1417. #if defined(DEBUG)
  1418. if(!_fTestingParserCoverage)
  1419. #endif
  1420. {
  1421. if(_cbSkipForUnicode &&
  1422. _token != tokenText &&
  1423. _token != tokenStartGroup &&
  1424. _token != tokenEndGroup &&
  1425. _token != tokenBinaryData)
  1426. {
  1427. _cbSkipForUnicode--;
  1428. goto done;
  1429. }
  1430. }
  1431. if(IN_RANGE(tokenPicFirst, _token, tokenObjLast) && !_prtfObject)
  1432. {
  1433. _ecParseError = ecUnexpectedToken;
  1434. return _ecParseError;
  1435. }
  1436. switch (_token)
  1437. {
  1438. case tokenURtf: // \urtfN - Preferred RE format
  1439. PARSERCOVERAGE_CASE(); // Currently we ignore the N
  1440. _dwFlags &= 0xFFFF; // Kill possible codepage
  1441. _dwFlags |= SF_USECODEPAGE | (CP_UTF8 << 16); // Save bit for Asserts
  1442. pstate->SetCodePage(CP_UTF8);
  1443. goto rtf;
  1444. case tokenPocketWord: // \pwd - Pocket Word
  1445. _dwFlags |= SFF_PWD;
  1446. case tokenRtf: // \rtf - Backward compatible
  1447. PARSERCOVERAGE_CASE();
  1448. rtf: pstate->sDest = destRTF;
  1449. Assert(pstate->nCodePage == INVALID_CODEPAGE ||
  1450. pstate->nCodePage == (int)(_dwFlags >> 16) &&
  1451. (_dwFlags & SF_USECODEPAGE));
  1452. if(!_fonts.Count() && !_fonts.Add(1, NULL)) // If can't add a font,
  1453. goto OutOfRAM; // report the bad news
  1454. _sDefaultFont = 0; // Set up valid default font
  1455. ptf = _fonts.Elem(0);
  1456. pstate->ptf = ptf; // Get char set, pitch, family
  1457. pCF = _prg->GetCF(); // from current range font
  1458. ptf->bCharSet = pCF->_bCharSet; // These are guaranteed OK
  1459. ptf->bPitchAndFamily = pCF->_bPitchAndFamily;
  1460. ptf->sCodePage = (SHORT)GetCodePage(pCF->_bCharSet);
  1461. wcscpy(ptf->szName, GetFontName(pCF->_iFont));
  1462. ptf->fNameIsDBCS = (pCF->_dwEffects & CFE_FACENAMEISDBCS) != 0;
  1463. pstate->cbSkipForUnicodeMax = iUnicodeCChDefault;
  1464. break;
  1465. case tokenViewKind: // \viewkind N
  1466. if(!(_dwFlags & SFF_SELECTION) && IsUTF8) // RTF applies to document:
  1467. _ped->SetViewKind(iParam); // For RE 3.0, only settable
  1468. break; // using RichEdit \urtf files
  1469. case tokenViewScale: // \viewscale N
  1470. if(_dwFlags & SFF_PERSISTVIEWSCALE &&
  1471. !(_dwFlags & SFF_SELECTION)) // RTF applies to document:
  1472. _ped->SetViewScale(iParam);
  1473. break;
  1474. case tokenCharacterDefault: // \plain
  1475. PARSERCOVERAGE_CASE();
  1476. SetPlain(pstate);
  1477. break;
  1478. case tokenCharSetAnsi: // \ansi
  1479. PARSERCOVERAGE_CASE();
  1480. _bCharSet = ANSI_CHARSET;
  1481. break;
  1482. case tokenDefaultLanguage: // \deflang
  1483. PARSERCOVERAGE_CASE();
  1484. _sDefaultLanguage = (SHORT)iParam;
  1485. if(!pstate->sLanguage)
  1486. pstate->sLanguage = _sDefaultLanguage;
  1487. break;
  1488. case tokenDefaultLanguageFE: // \deflangfe
  1489. PARSERCOVERAGE_CASE();
  1490. _sDefaultLanguageFE = (SHORT)iParam;
  1491. break;
  1492. case tokenDefaultTabWidth: // \deftab
  1493. PARSERCOVERAGE_CASE();
  1494. _sDefaultTabWidth = (SHORT)iParam;
  1495. break;
  1496. //--------------------------- Font Control Words -------------------------------
  1497. case tokenDefaultFont: // \deff n
  1498. PARSERCOVERAGE_CASE();
  1499. if(iParam >= 0)
  1500. {
  1501. if(!_fonts.Count() && !_fonts.Add(1, NULL)) // If can't add a font,
  1502. goto OutOfRAM; // report the bad news
  1503. _fonts.Elem(0)->sHandle = _sDefaultFont = (SHORT)iParam;
  1504. }
  1505. TRACEERRSZSC("tokenDefaultFont: Negative value", iParam);
  1506. break;
  1507. case tokenDefaultBiDiFont: // \adeff n
  1508. PARSERCOVERAGE_CASE();
  1509. if(iParam >=0 && _fonts.Count() == 1)
  1510. {
  1511. if(!_fonts.Add(1, NULL))
  1512. goto OutOfRAM;
  1513. _fonts.Elem(1)->sHandle = _sDefaultBiDiFont = (SHORT)iParam;
  1514. }
  1515. TRACEERRSZSC("tokenDefaultBiDiFont: Negative value", iParam);
  1516. break;
  1517. case tokenFontTable: // \fonttbl
  1518. PARSERCOVERAGE_CASE();
  1519. pstate->sDest = destFontTable;
  1520. pstate->ptf = NULL;
  1521. break;
  1522. case tokenFontFamilyBidi: // \fbidi
  1523. case tokenFontFamilyTechnical: // \ftech
  1524. case tokenFontFamilyDecorative: // \fdecor
  1525. case tokenFontFamilyScript: // \fscript
  1526. case tokenFontFamilyModern: // \fmodern
  1527. case tokenFontFamilySwiss: // \fswiss
  1528. case tokenFontFamilyRoman: // \froman
  1529. case tokenFontFamilyDefault: // \fnil
  1530. PARSERCOVERAGE_CASE();
  1531. AssertSz(tokenFontFamilyRoman - tokenFontFamilyDefault == 1,
  1532. "CRTFRead::HandleToken: invalid token definition");
  1533. if(pstate->ptf)
  1534. {
  1535. pstate->ptf->bPitchAndFamily
  1536. = (BYTE)((_token - tokenFontFamilyDefault) << 4
  1537. | (pstate->ptf->bPitchAndFamily & 0xF));
  1538. // Setup SYMBOL_CHARSET charset for \ftech if there isn't any charset info
  1539. if(tokenFontFamilyTechnical == _token && pstate->ptf->bCharSet == DEFAULT_CHARSET)
  1540. pstate->ptf->bCharSet = SYMBOL_CHARSET;
  1541. }
  1542. break;
  1543. case tokenPitch: // \fprq
  1544. PARSERCOVERAGE_CASE();
  1545. if(pstate->ptf)
  1546. pstate->ptf->bPitchAndFamily
  1547. = (BYTE)(iParam | (pstate->ptf->bPitchAndFamily & 0xF0));
  1548. break;
  1549. case tokenAnsiCodePage: // \ansicpg
  1550. PARSERCOVERAGE_CASE();
  1551. #ifdef DEBUG
  1552. if(_fSeenFontTable && _nCodePage == INVALID_CODEPAGE)
  1553. TRACEWARNSZ("CRTFRead::HandleToken(): Found an \ansicpgN tag after "
  1554. "the font table. Should have code to fix-up "
  1555. "converted font names and document text.");
  1556. #endif
  1557. if(!(_dwFlags & SF_USECODEPAGE))
  1558. {
  1559. _nCodePage = iParam;
  1560. pstate->SetCodePage(iParam);
  1561. }
  1562. Assert(!IsUTF8 || pstate->nCodePage == CP_UTF8);
  1563. break;
  1564. case tokenCodePage: // \cpg
  1565. PARSERCOVERAGE_CASE();
  1566. pstate->SetCodePage(iParam);
  1567. if(pstate->sDest == destFontTable && pstate->ptf)
  1568. {
  1569. pstate->ptf->sCodePage = (SHORT)iParam;
  1570. pstate->ptf->bCharSet = GetCharSet(iParam);
  1571. // If a document-level code page has not been specified,
  1572. // grab this from the first font table entry containing a
  1573. // \fcharsetN or \cpgN
  1574. if(_nCodePage == INVALID_CODEPAGE)
  1575. _nCodePage = iParam;
  1576. }
  1577. break;
  1578. case tokenCharSet: // \fcharset n
  1579. PARSERCOVERAGE_CASE();
  1580. if(pstate->ptf)
  1581. {
  1582. pstate->ptf->bCharSet = (BYTE)iParam;
  1583. pstate->ptf->sCodePage = (SHORT)GetCodePage((BYTE)iParam);
  1584. pstate->SetCodePage(pstate->ptf->sCodePage);
  1585. // If a document-level code page has not been specified,
  1586. // grab this from the first font table entry containing a
  1587. // \fcharsetN or \cpgN
  1588. if (pstate->nCodePage != CP_SYMBOL &&
  1589. _nCodePage == INVALID_CODEPAGE)
  1590. {
  1591. _nCodePage = pstate->nCodePage;
  1592. }
  1593. if(IsRTLCharSet(iParam))
  1594. {
  1595. if(_sDefaultBiDiFont == -1)
  1596. _sDefaultBiDiFont = pstate->ptf->sHandle;
  1597. else if(_sDefaultBiDiFont != pstate->ptf->sHandle)
  1598. {
  1599. // Validate default BiDi font since Word 2000 may choose
  1600. // a nonBiDi font
  1601. i = _fonts.Count();
  1602. ptf = _fonts.Elem(0);
  1603. for(; i-- && _sDefaultBiDiFont != ptf->sHandle; ptf++)
  1604. ;
  1605. if(i >= 0 && !IsRTLCharSet(ptf->bCharSet))
  1606. _sDefaultBiDiFont = pstate->ptf->sHandle;
  1607. }
  1608. if(!IsRTLCharSet(_bBiDiCharSet))
  1609. _bBiDiCharSet = (BYTE)iParam;
  1610. }
  1611. _fCharSet = TRUE;
  1612. }
  1613. break;
  1614. case tokenRealFontName: // \fname
  1615. PARSERCOVERAGE_CASE();
  1616. pstate->sDest = destRealFontName;
  1617. break;
  1618. case tokenAssocFontSelect: // \af n
  1619. PARSERCOVERAGE_CASE();
  1620. if(pstate->fltrch || pstate->frtlch) // Be sure it's Western or RTL
  1621. { // script and not FE
  1622. i = _fonts.Count();
  1623. ptf = _fonts.Elem(0);
  1624. for(; i-- && iParam != ptf->sHandle; ptf++) // Search for font
  1625. ; // with handle iParam
  1626. if(i >= 0 && IsRTLCharSet(ptf->bCharSet))
  1627. {
  1628. // FUTURE: set new variable _sFontAssocBiDi = iParam
  1629. // and select _sFontAssocBiDi if run is rtlch. This
  1630. // should give same display as Word.
  1631. _bBiDiCharSet = ptf->bCharSet;
  1632. }
  1633. break;
  1634. }
  1635. if(_sDefaultBiDiFont == -1 || !pstate->fdbch)// BiDi & FE script active?
  1636. break; // No
  1637. // Yes: fall thru to \f n
  1638. case tokenFontSelect: // \f n
  1639. PARSERCOVERAGE_CASE();
  1640. if(iParam == -1) // Can't handle this bizarre choice
  1641. goto skip_group;
  1642. pstate->fdbch = FALSE; // Reset DBCS flag
  1643. if(pstate->sDest == destFontTable) // Building font table
  1644. {
  1645. if(iParam == _sDefaultFont)
  1646. {
  1647. _fReadDefFont = TRUE;
  1648. ptf = _fonts.Elem(0);
  1649. }
  1650. else if(iParam == _sDefaultBiDiFont)
  1651. ptf = _fonts.Elem(1);
  1652. else if(!(ptf =_fonts.Add(1,NULL))) // Make room in font table for
  1653. { // font to be parsed
  1654. OutOfRAM:
  1655. _ped->GetCallMgr()->SetOutOfMemory();
  1656. _ecParseError = ecNoMemory;
  1657. break;
  1658. }
  1659. pstate->ptf = ptf;
  1660. ptf->sHandle = (SHORT)iParam; // Save handle
  1661. ptf->szName[0] = '\0'; // Start with null string
  1662. ptf->bPitchAndFamily = 0;
  1663. ptf->fNameIsDBCS = FALSE;
  1664. ptf->sCodePage = INVALID_CODEPAGE;
  1665. ptf->fCpgFromSystem = FALSE;
  1666. ptf->bCharSet = DEFAULT_CHARSET;
  1667. }
  1668. else // Font switch in text
  1669. {
  1670. SelectCurrentFont(iParam);
  1671. if(IsRTLCharSet(pstate->ptf->bCharSet))
  1672. _bBiDiCharSet = pstate->ptf->bCharSet;
  1673. }
  1674. break;
  1675. case tokenFontSize: // \fs n
  1676. PARSERCOVERAGE_CASE();
  1677. _CF._yHeight = PointsToFontHeight(iParam); // Convert font size in
  1678. _dwMaskCF |= CFM_SIZE; // half points to logical
  1679. break; // units
  1680. // NOTE: \*\fontemb and \*\fontfile are discarded. The font mapper will
  1681. // have to do the best it can given font name, family, and pitch.
  1682. // Embedded fonts are particularly nasty because legal use should
  1683. // only support read-only which parser cannot enforce.
  1684. case tokenLanguage: // \lang
  1685. PARSERCOVERAGE_CASE();
  1686. pstate->sLanguage = (SHORT)iParam; // These 2 lines may not be
  1687. pstate->fExplicitLang = TRUE; // needed with the new lcid
  1688. _CF._lcid = MAKELCID(iParam, SORT_DEFAULT);
  1689. if (W32->IsBiDiLcid(_CF._lcid))
  1690. _bBiDiCharSet = GetCharSet(ConvertLanguageIDtoCodePage(iParam));
  1691. _dwMaskCF |= CFM_LCID;
  1692. break;
  1693. //-------------------------- Color Control Words ------------------------------
  1694. case tokenColorTable: // \colortbl
  1695. PARSERCOVERAGE_CASE();
  1696. pstate->sDest = destColorTable;
  1697. _fGetColorYet = FALSE;
  1698. break;
  1699. case tokenColorRed: // \red
  1700. PARSERCOVERAGE_CASE();
  1701. pstate->bRed = (BYTE)iParam;
  1702. _fGetColorYet = TRUE;
  1703. break;
  1704. case tokenColorGreen: // \green
  1705. PARSERCOVERAGE_CASE();
  1706. pstate->bGreen = (BYTE)iParam;
  1707. _fGetColorYet = TRUE;
  1708. break;
  1709. case tokenColorBlue: // \blue
  1710. PARSERCOVERAGE_CASE();
  1711. pstate->bBlue = (BYTE)iParam;
  1712. _fGetColorYet = TRUE;
  1713. break;
  1714. case tokenColorForeground: // \cf
  1715. PARSERCOVERAGE_CASE();
  1716. _CF._crTextColor = GetColor(CFM_COLOR);
  1717. // V-GUYB: Table cell backgrounds (\clcbpat) are not handled in RE 2.0.
  1718. // This means all table cells will have a white background. Therefore
  1719. // change any white text to black here.
  1720. if(_pstateStackTop->fInTable && _CF._crTextColor == RGB(0xFF, 0xFF, 0xFF))
  1721. _CF._crTextColor = RGB(0x00, 0x00, 0x00);
  1722. break;
  1723. case tokenColorBackground: // \highlight
  1724. PARSERCOVERAGE_CASE();
  1725. _CF._crBackColor = GetColor(CFM_BACKCOLOR);
  1726. break;
  1727. case tokenExpand: // \expndtw N
  1728. PARSERCOVERAGE_CASE();
  1729. _CF._sSpacing = (SHORT) iParam;
  1730. _dwMaskCF |= CFM_SPACING;
  1731. break;
  1732. case tokenCharStyle: // \cs N
  1733. PARSERCOVERAGE_CASE();
  1734. /* FUTURE (alexgo): we may want to support character styles
  1735. in some future version.
  1736. _CF._sStyle = (SHORT)iParam;
  1737. _dwMaskCF |= CFM_STYLE; */
  1738. if(pstate->sDest == destStyleSheet)
  1739. goto skip_group;
  1740. break;
  1741. case tokenAnimText: // \animtext N
  1742. PARSERCOVERAGE_CASE();
  1743. _CF._bAnimation = (BYTE)iParam;
  1744. _dwMaskCF |= CFM_ANIMATION;
  1745. break;
  1746. case tokenKerning: // \kerning N
  1747. PARSERCOVERAGE_CASE();
  1748. _CF._wKerning = (WORD)(10 * iParam); // Convert to twips
  1749. _dwMaskCF |= CFM_KERNING;
  1750. break;
  1751. case tokenFollowingPunct: // \*\fchars
  1752. PARSERCOVERAGE_CASE();
  1753. pstate->sDest = destFollowingPunct;
  1754. {
  1755. char *pwchBuf=NULL;
  1756. if (ReadRawText((_dwFlags & SFF_SELECTION) ? NULL : &pwchBuf) && pwchBuf)
  1757. {
  1758. if (_ped->SetFollowingPunct(pwchBuf) != NOERROR) // Store this buffer inside doc
  1759. FreePv(pwchBuf);
  1760. }
  1761. else if (pwchBuf)
  1762. FreePv(pwchBuf);
  1763. }
  1764. break;
  1765. case tokenLeadingPunct: // \*\lchars
  1766. PARSERCOVERAGE_CASE();
  1767. pstate->sDest = destLeadingPunct;
  1768. {
  1769. char *pwchBuf=NULL;
  1770. if (ReadRawText((_dwFlags & SFF_SELECTION) ? NULL : &pwchBuf) && pwchBuf)
  1771. {
  1772. if (_ped->SetLeadingPunct(pwchBuf) != NOERROR) // Store this buffer inside doc
  1773. FreePv(pwchBuf);
  1774. }
  1775. else if (pwchBuf)
  1776. FreePv(pwchBuf);
  1777. }
  1778. break;
  1779. case tokenDocumentArea: // \info
  1780. PARSERCOVERAGE_CASE();
  1781. pstate->sDest = destDocumentArea;
  1782. break;
  1783. #ifdef FE
  1784. USHORT usPunct; // Used for FE word breaking
  1785. case tokenNoOverflow: // \nooverflow
  1786. PARSERCOVERAGE_CASE();
  1787. TRACEINFOSZ("No Overflow");
  1788. usPunct = ~WBF_OVERFLOW;
  1789. goto setBrkOp;
  1790. case tokenNoWordBreak: // \nocwrap
  1791. PARSERCOVERAGE_CASE();
  1792. TRACEINFOSZ("No Word Break" );
  1793. usPunct = ~WBF_WORDBREAK;
  1794. goto setBrkOp;
  1795. case tokenNoWordWrap: // \nowwrap
  1796. PARSERCOVERAGE_CASE();
  1797. TRACEINFOSZ("No Word Word Wrap" );
  1798. usPunct = ~WBF_WORDWRAP;
  1799. setBrkOp:
  1800. if(!(_dwFlags & fRTFFE))
  1801. {
  1802. usPunct &= UsVGetBreakOption(_ped->lpPunctObj);
  1803. UsVSetBreakOption(_ped->lpPunctObj, usPunct);
  1804. }
  1805. break;
  1806. case tokenVerticalRender: // \vertdoc
  1807. PARSERCOVERAGE_CASE();
  1808. TRACEINFOSZ("Vertical" );
  1809. if(pstate->sDest == destDocumentArea && !(_dwFlags & fRTFFE))
  1810. _ped->fModeDefer = TRUE;
  1811. break;
  1812. case tokenHorizontalRender: // \horzdoc
  1813. PARSERCOVERAGE_CASE();
  1814. TRACEINFOSZ("Horizontal" );
  1815. if(pstate->sDest == destDocumentArea && !(_dwFlags & fRTFFE))
  1816. _ped->fModeDefer = FALSE;
  1817. break;
  1818. #endif
  1819. //-------------------- Character Format Control Words -----------------------------
  1820. case tokenUnderlineHairline: // \ulhair [10]
  1821. case tokenUnderlineThick: // \ulth [9]
  1822. case tokenUnderlineWave: // \ulwave [8]
  1823. case tokenUnderlineDashDotDotted: // \uldashdd [7]
  1824. case tokenUnderlineDashDotted: // \uldashd [6]
  1825. case tokenUnderlineDash: // \uldash [5]
  1826. case tokenUnderlineDotted: // \uld [4]
  1827. case tokenUnderlineDouble: // \uldb [3]
  1828. case tokenUnderlineWord: // \ulw [2]
  1829. PARSERCOVERAGE_CASE();
  1830. _CF._bUnderlineType = (BYTE)(_token - tokenUnderlineWord + 2);
  1831. _token = tokenUnderline; // CRenderer::RenderUnderline()
  1832. goto under; // reveals which of these are
  1833. // rendered specially
  1834. case tokenUnderline: // \ul [Effect 4]
  1835. PARSERCOVERAGE_CASE(); // (see handleCF)
  1836. _CF._bUnderlineType = CFU_UNDERLINE;
  1837. under: _dwMaskCF |= CFM_UNDERLINETYPE;
  1838. goto handleCF;
  1839. case tokenDeleted: // \deleted
  1840. PARSERCOVERAGE_CASE();
  1841. _dwMaskCF2 = CFM2_DELETED;
  1842. dwT = CFE_DELETED;
  1843. goto hndlCF;
  1844. // These effects are turned on if their control word parameter is missing
  1845. // or nonzero. They are turned off if the parameter is zero. This
  1846. // behavior is usually identified by an asterisk (*) in the RTF spec.
  1847. // The code uses fact that CFE_xxx = CFM_xxx
  1848. handleCF:
  1849. case tokenRevised: // \revised [4000]
  1850. case tokenDisabled: // \disabled [2000]
  1851. case tokenImprint: // \impr [1000]
  1852. case tokenEmboss: // \embo [800]
  1853. case tokenShadow: // \shad [400]
  1854. case tokenOutline: // \outl [200]
  1855. case tokenHiddenText: // \v [100]
  1856. case tokenCaps: // \caps [80]
  1857. case tokenSmallCaps: // \scaps [40]
  1858. case tokenLink: // \link [20]
  1859. case tokenProtect: // \protect [10]
  1860. case tokenStrikeOut: // \strike [8]
  1861. case tokenItalic: // \i [2]
  1862. case tokenBold: // \b [1]
  1863. PARSERCOVERAGE_CASE();
  1864. dwT = 1 << (_token - tokenBold); // Generate effect mask
  1865. _dwMaskCF |= dwT;
  1866. hndlCF: _CF._dwEffects &= ~dwT; // Default attribute off
  1867. if(!*_szParam || _iParam) // Effect is on
  1868. _CF._dwEffects |= dwT; // In either case, the effect
  1869. break; // is defined
  1870. case tokenStopUnderline: // \ulnone
  1871. PARSERCOVERAGE_CASE();
  1872. _CF._dwEffects &= ~CFE_UNDERLINE; // Kill all underlining
  1873. _dwMaskCF |= CFM_UNDERLINE;
  1874. break;
  1875. case tokenRevAuthor: // \revauth
  1876. PARSERCOVERAGE_CASE();
  1877. /* FUTURE: (alexgo) this doesn't work well now since we don't support
  1878. revision tables. We may want to support this better in the future.
  1879. So what we do now is the 1.0 technique of using a color for the
  1880. author */
  1881. if(iParam > 0)
  1882. {
  1883. _CF._dwEffects &= ~CFE_AUTOCOLOR;
  1884. _dwMaskCF |= CFM_COLOR;
  1885. _CF._crTextColor = rgcrRevisions[(iParam - 1) & REVMASK];
  1886. }
  1887. break;
  1888. case tokenUp: // \up
  1889. PARSERCOVERAGE_CASE();
  1890. dy = 10;
  1891. goto StoreOffset;
  1892. case tokenDown: // \down
  1893. PARSERCOVERAGE_CASE();
  1894. dy = -10;
  1895. StoreOffset:
  1896. if(!*_szParam)
  1897. iParam = dyDefaultSuperscript;
  1898. _CF._yOffset = iParam * dy; // Half points->twips
  1899. _dwMaskCF |= CFM_OFFSET;
  1900. break;
  1901. case tokenSuperscript: // \super
  1902. PARSERCOVERAGE_CASE();
  1903. dwT = CFE_SUPERSCRIPT;
  1904. goto SetSubSuperScript;
  1905. case tokenSubscript: // \sub
  1906. PARSERCOVERAGE_CASE();
  1907. dwT = CFE_SUBSCRIPT;
  1908. goto SetSubSuperScript;
  1909. case tokenNoSuperSub: // \nosupersub
  1910. PARSERCOVERAGE_CASE();
  1911. dwT = 0;
  1912. SetSubSuperScript:
  1913. _dwMaskCF |= (CFE_SUPERSCRIPT | CFE_SUBSCRIPT);
  1914. _CF._dwEffects &= ~(CFE_SUPERSCRIPT | CFE_SUBSCRIPT);
  1915. _CF._dwEffects |= dwT;
  1916. break;
  1917. //--------------------- Paragraph Control Words -----------------------------
  1918. case tokenStyleSheet: // \stylesheet
  1919. PARSERCOVERAGE_CASE();
  1920. pstate->sDest = destStyleSheet;
  1921. _Style = 0; // Default normal style
  1922. break;
  1923. case tokenTabBar: // \tb
  1924. PARSERCOVERAGE_CASE();
  1925. _bTabType = PFT_BAR; // Fall thru to \tx
  1926. case tokenTabPosition: // \tx. Ignore if in table
  1927. PARSERCOVERAGE_CASE(); // since our simple model
  1928. if(!pstate->fInTable) // uses tab positions for
  1929. { // cell widths
  1930. if(_cTab < MAX_TAB_STOPS && (unsigned)iParam < 0x1000000)
  1931. {
  1932. _rgxCell[_cTab++] = GetTabPos(iParam)
  1933. + (_bTabType << 24) + (_bTabLeader << 28);
  1934. }
  1935. _dwMaskPF |= PFM_TABSTOPS;
  1936. }
  1937. break;
  1938. case tokenDecimalTab: // \tqdec
  1939. case tokenFlushRightTab: // \tqr
  1940. case tokenCenterTab: // \tqc
  1941. PARSERCOVERAGE_CASE();
  1942. _bTabType = (BYTE)(_token - tokenCenterTab + PFT_CENTER);
  1943. break;
  1944. case tokenTabLeaderEqual: // \tleq
  1945. case tokenTabLeaderThick: // \tlth
  1946. case tokenTabLeaderUnderline: // \tlul
  1947. case tokenTabLeaderHyphen: // \tlhyph
  1948. case tokenTabLeaderDots: // \tldot
  1949. PARSERCOVERAGE_CASE();
  1950. _bTabLeader = (BYTE)(_token - tokenTabLeaderDots + PFTL_DOTS);
  1951. break;
  1952. // The following need to be kept in sync with PFE_xxx
  1953. case tokenCollapsed: // \collapsed
  1954. case tokenSideBySide: // \sbys
  1955. case tokenHyphPar: // \hyphpar
  1956. case tokenNoWidCtlPar: // \nowidctlpar
  1957. case tokenNoLineNumber: // \noline
  1958. case tokenPageBreakBefore: // \pagebb
  1959. case tokenKeepNext: // \keepn
  1960. case tokenKeep: // \keep
  1961. case tokenRToLPara: // \rtlpar
  1962. PARSERCOVERAGE_CASE();
  1963. wT = (WORD)(1 << (_token - tokenRToLPara));
  1964. _PF._wEffects |= wT;
  1965. _dwMaskPF |= (wT << 16);
  1966. break;
  1967. case tokenLToRPara: // \ltrpar
  1968. PARSERCOVERAGE_CASE();
  1969. _PF._wEffects &= ~PFE_RTLPARA;
  1970. _dwMaskPF |= PFM_RTLPARA;
  1971. break;
  1972. case tokenLineSpacing: // \sl N
  1973. PARSERCOVERAGE_CASE();
  1974. _PF._dyLineSpacing = abs(iParam);
  1975. _PF._bLineSpacingRule // Handle nonmultiple rules
  1976. = (BYTE)(!iParam || iParam == 1000
  1977. ? 0 : (iParam > 0) ? tomLineSpaceAtLeast
  1978. : tomLineSpaceExactly); // \slmult can change (has to
  1979. _dwMaskPF |= PFM_LINESPACING; // follow if it appears)
  1980. break;
  1981. case tokenDropCapLines: // \dropcapliN
  1982. if(_PF._bLineSpacingRule == tomLineSpaceExactly) // Don't chop off
  1983. _PF._bLineSpacingRule = tomLineSpaceAtLeast; // drop cap
  1984. break;
  1985. case tokenLineSpacingRule: // \slmult N
  1986. PARSERCOVERAGE_CASE();
  1987. if(iParam)
  1988. { // It's multiple line spacing
  1989. _PF._bLineSpacingRule = tomLineSpaceMultiple;
  1990. _PF._dyLineSpacing /= 12; // RE line spacing multiple is
  1991. _dwMaskPF |= PFM_LINESPACING; // given in 20ths of a line,
  1992. } // while RTF uses 240ths
  1993. break;
  1994. case tokenSpaceBefore: // \sb N
  1995. PARSERCOVERAGE_CASE();
  1996. _PF._dySpaceBefore = iParam;
  1997. _dwMaskPF |= PFM_SPACEBEFORE;
  1998. break;
  1999. case tokenSpaceAfter: // \sa N
  2000. PARSERCOVERAGE_CASE();
  2001. _PF._dySpaceAfter = iParam;
  2002. _dwMaskPF |= PFM_SPACEAFTER;
  2003. break;
  2004. case tokenStyle: // \s N
  2005. PARSERCOVERAGE_CASE();
  2006. _Style = iParam; // Save it in case in StyleSheet
  2007. if(pstate->sDest != destStyleSheet)
  2008. { // Select possible heading level
  2009. _PF._sStyle = STYLE_NORMAL; // Default Normal style
  2010. _PF._bOutlineLevel |= 1;
  2011. for(i = 0; i < NSTYLES && iParam != _rgStyles[i]; i++)
  2012. ; // Check for heading style
  2013. if(i < NSTYLES) // Found one
  2014. {
  2015. _PF._sStyle = (SHORT)(-i - 1); // Store desired heading level
  2016. _PF._bOutlineLevel = (BYTE)(2*(i-1));// Update outline level for
  2017. } // nonheading styles
  2018. _dwMaskPF |= PFM_ALLRTF;
  2019. }
  2020. break;
  2021. case tokenIndentFirst: // \fi N
  2022. PARSERCOVERAGE_CASE();
  2023. _PF._dxStartIndent += _PF._dxOffset // Cancel current offset
  2024. + iParam; // and add in new one
  2025. _PF._dxOffset = -iParam; // Offset for all but 1st line
  2026. // = -RTF_FirstLineIndent
  2027. _dwMaskPF |= (PFM_STARTINDENT | PFM_OFFSET);
  2028. break;
  2029. case tokenIndentLeft: // \li N
  2030. case tokenIndentRight: // \ri N
  2031. PARSERCOVERAGE_CASE();
  2032. // AymanA: For RtL para indents has to be flipped.
  2033. Assert(PFE_RTLPARA == 0x0001);
  2034. if((_token == tokenIndentLeft) ^ (_PF.IsRtlPara()))
  2035. {
  2036. _PF._dxStartIndent = iParam - _PF._dxOffset;
  2037. _dwMaskPF |= PFM_STARTINDENT;
  2038. }
  2039. else
  2040. {
  2041. _PF._dxRightIndent = iParam;
  2042. _dwMaskPF |= PFM_RIGHTINDENT;
  2043. }
  2044. break;
  2045. case tokenAlignLeft: // \ql
  2046. case tokenAlignRight: // \qr
  2047. case tokenAlignCenter: // \qc
  2048. case tokenAlignJustify: // \qj
  2049. PARSERCOVERAGE_CASE();
  2050. if(!pstate->fInTable)
  2051. {
  2052. _PF._bAlignment = (WORD)(_token - tokenAlignLeft + PFA_LEFT);
  2053. _dwMaskPF |= PFM_ALIGNMENT;
  2054. }
  2055. break;
  2056. case tokenBorderOutside: // \brdrbar
  2057. case tokenBorderBetween: // \brdrbtw
  2058. case tokenBorderShadow: // \brdrsh
  2059. PARSERCOVERAGE_CASE();
  2060. _PF._dwBorderColor |= 1 << (_token - tokenBorderShadow + 20);
  2061. _dwBorderColor = _PF._dwBorderColor;
  2062. break;
  2063. // Paragraph and cell border segments
  2064. case tokenBox: // \box
  2065. PARSERCOVERAGE_CASE();
  2066. _PF._wEffects |= PFE_BOX;
  2067. _dwMaskPF |= PFM_BOX;
  2068. _bBorder = 0; // Store parms as if for
  2069. break; // \brdrt
  2070. case tokenCellBorderRight: // \clbrdrr
  2071. case tokenCellBorderBottom: // \clbrdrb
  2072. case tokenCellBorderLeft: // \clbrdrl
  2073. case tokenCellBorderTop: // \clbrdrt
  2074. case tokenBorderRight: // \brdrr
  2075. case tokenBorderBottom: // \brdrb
  2076. case tokenBorderLeft: // \brdrl
  2077. case tokenBorderTop: // \brdrt
  2078. PARSERCOVERAGE_CASE();
  2079. _bBorder = (BYTE)(_token - tokenBorderTop);
  2080. break;
  2081. // Paragraph border styles
  2082. case tokenBorderTriple: // \brdrtriple
  2083. case tokenBorderDoubleThick: // \brdrth
  2084. case tokenBorderSingleThick: // \brdrs
  2085. case tokenBorderHairline: // \brdrhair
  2086. case tokenBorderDot: // \brdrdot
  2087. case tokenBorderDouble: // \brdrdb
  2088. case tokenBorderDashSmall: // \brdrdashsm
  2089. case tokenBorderDash: // \brdrdash
  2090. PARSERCOVERAGE_CASE();
  2091. if(_bBorder < 4) // Only for paragraphs
  2092. SetBorderParm(_PF._wBorders, _token - tokenBorderDash);
  2093. break;
  2094. case tokenBorderColor: // \brdrcf
  2095. PARSERCOVERAGE_CASE();
  2096. if(_bBorder < 4) // Only for paragraphs
  2097. {
  2098. iParam = GetStandardColorIndex();
  2099. _PF._dwBorderColor &= ~(0x1F << (5*_bBorder));
  2100. _PF._dwBorderColor |= iParam << (5*_bBorder);
  2101. _dwBorderColor = _PF._dwBorderColor;
  2102. }
  2103. break;
  2104. case tokenBorderWidth: // \brdrw
  2105. PARSERCOVERAGE_CASE(); // Width is in half pts
  2106. if(_bBorder < 4) // For paragraphs
  2107. { // iParam is in twips
  2108. if(IN_RANGE(1, iParam, 4)) // Give small but nonzero
  2109. iParam = 1; // values our minimum
  2110. else // size
  2111. iParam = (iParam + 5)/10;
  2112. SetBorderParm(_PF._wBorderWidth, iParam);
  2113. }
  2114. else // For cells only have 2 bits
  2115. {
  2116. iParam = (iParam + 10)/20;
  2117. iParam = max(iParam, 1);
  2118. iParam = min(iParam, 3);
  2119. _bCellBrdrWdths |= iParam << 2*(_bBorder - 4);
  2120. }
  2121. break;
  2122. case tokenBorderSpace: // \brsp
  2123. PARSERCOVERAGE_CASE(); // Space is in pts
  2124. if(_bBorder < 4) // Only for paragraphs
  2125. SetBorderParm(_PF._wBorderSpace, iParam/20);// iParam is in twips
  2126. break;
  2127. // Paragraph shading
  2128. case tokenBckgrndVert: // \bgvert
  2129. case tokenBckgrndHoriz: // \bghoriz
  2130. case tokenBckgrndFwdDiag: // \bgfdiag
  2131. case tokenBckgrndDrkVert: // \bgdkvert
  2132. case tokenBckgrndDrkHoriz: // \bgdkhoriz
  2133. case tokenBckgrndDrkFwdDiag: // \bgdkfdiag
  2134. case tokenBckgrndDrkDiagCross: // \bgdkdcross
  2135. case tokenBckgrndDrkCross: // \bgdkcross
  2136. case tokenBckgrndDrkBckDiag: // \bgdkbdiag
  2137. case tokenBckgrndDiagCross: // \bgdcross
  2138. case tokenBckgrndCross: // \bgcross
  2139. case tokenBckgrndBckDiag: // \bgbdiag
  2140. PARSERCOVERAGE_CASE();
  2141. _PF._wShadingStyle = (WORD)((_PF._wShadingStyle & 0xFFC0)
  2142. | (_token - tokenBckgrndBckDiag + 1));
  2143. _dwMaskPF |= PFM_SHADING;
  2144. break;
  2145. case tokenColorBckgrndPat: // \cbpat
  2146. PARSERCOVERAGE_CASE();
  2147. iParam = GetStandardColorIndex();
  2148. _PF._wShadingStyle = (WORD)((_PF._wShadingStyle & 0x07FF) | (iParam << 11));
  2149. _dwMaskPF |= PFM_SHADING;
  2150. break;
  2151. case tokenColorForgrndPat: // \cfpat
  2152. PARSERCOVERAGE_CASE();
  2153. iParam = GetStandardColorIndex();
  2154. _PF._wShadingStyle = (WORD)((_PF._wShadingStyle & 0xF83F) | (iParam << 6));
  2155. _dwMaskPF |= PFM_SHADING;
  2156. break;
  2157. case tokenShading: // \shading
  2158. PARSERCOVERAGE_CASE();
  2159. _PF._wShadingWeight = (WORD)iParam;
  2160. _dwMaskPF |= PFM_SHADING;
  2161. break;
  2162. // Paragraph numbering
  2163. case tokenParaNum: // \pn
  2164. PARSERCOVERAGE_CASE();
  2165. pstate->sDest = destParaNumbering;
  2166. pstate->fBullet = FALSE;
  2167. _PF._wNumberingStart = 1;
  2168. _dwMaskPF |= PFM_NUMBERINGSTART;
  2169. break;
  2170. case tokenParaNumIndent: // \pnindent N
  2171. PARSERCOVERAGE_CASE();
  2172. if(pstate->sDest == destParaNumbering)
  2173. pstate->sIndentNumbering = (SHORT)iParam;
  2174. break;
  2175. case tokenParaNumStart: // \pnstart N
  2176. PARSERCOVERAGE_CASE();
  2177. if(pstate->sDest == destParaNumbering)
  2178. {
  2179. _PF._wNumberingStart = (WORD)iParam;
  2180. _dwMaskPF |= PFM_NUMBERINGSTART;
  2181. }
  2182. break;
  2183. case tokenParaNumCont: // \pnlvlcont
  2184. PARSERCOVERAGE_CASE();
  2185. _prg->_rpPF.AdjustBackward(); // Maintain numbering mode
  2186. _PF._wNumbering = _prg->GetPF()->_wNumbering;
  2187. _prg->_rpPF.AdjustForward();
  2188. _wNumberingStyle = PFNS_NONUMBER; // Signal no number
  2189. _dwMaskPF |= PFM_NUMBERING; // Note: can be new para with
  2190. pstate->fBullet = TRUE; // its own indents
  2191. break;
  2192. case tokenParaNumBody: // \pnlvlbody
  2193. PARSERCOVERAGE_CASE();
  2194. _wNumberingStyle = PFNS_PAREN;
  2195. _token = tokenParaNumDecimal; // Default to decimal
  2196. goto setnum;
  2197. case tokenParaNumBullet: // \pnlvlblt
  2198. _wNumberingStyle = 0; // Reset numbering styles
  2199. goto setnum;
  2200. case tokenParaNumDecimal: // \pndec
  2201. case tokenParaNumLCLetter: // \pnlcltr
  2202. case tokenParaNumUCLetter: // \pnucltr
  2203. case tokenParaNumLCRoman: // \pnlcrm
  2204. case tokenParaNumUCRoman: // \pnucrm
  2205. PARSERCOVERAGE_CASE();
  2206. if(_PF._wNumbering == PFN_BULLET && pstate->fBullet)
  2207. break; // Ignore above for bullets
  2208. setnum: if(pstate->sDest == destParaNumbering)
  2209. {
  2210. _PF._wNumbering = (WORD)(PFN_BULLET + _token - tokenParaNumBullet);
  2211. _dwMaskPF |= PFM_NUMBERING;
  2212. pstate->fBullet = TRUE; // We do bullets, so don't
  2213. } // output the \pntext group
  2214. break;
  2215. case tokenParaNumText: // \pntext
  2216. PARSERCOVERAGE_CASE();
  2217. // Throw away previously read paragraph numbering and use
  2218. // the most recently read to apply to next run of text.
  2219. _cchUsedNumText = 0;
  2220. pstate->sDest = destParaNumText;
  2221. break;
  2222. case tokenParaNumAlignCenter: // \pnqc
  2223. case tokenParaNumAlignRight: // \pnqr
  2224. PARSERCOVERAGE_CASE();
  2225. _wNumberingStyle = (_wNumberingStyle & ~3) | _token - tokenParaNumAlignCenter + 1;
  2226. break;
  2227. case tokenParaNumAfter: // \pntxta
  2228. case tokenParaNumBefore: // \pntxtb
  2229. case tokenPictureQuickDraw: // \macpict
  2230. case tokenPictureOS2Metafile: // \pmmetafile
  2231. PARSERCOVERAGE_CASE();
  2232. skip_group:
  2233. if(!SkipToEndOfGroup())
  2234. {
  2235. // During \fonttbl processing, we may hit unknown destinations,
  2236. // e.g., \panose, that cause the HandleEndGroup to select the
  2237. // default font, which may not be defined yet. So, we change
  2238. // sDest to avoid this problem.
  2239. if(pstate->sDest == destFontTable || pstate->sDest == destStyleSheet)
  2240. pstate->sDest = destNULL;
  2241. HandleEndGroup();
  2242. }
  2243. break;
  2244. // Tables
  2245. case tokenInTable: // \intbl
  2246. PARSERCOVERAGE_CASE();
  2247. // Our simple table model has one para per row, i.e., no paras in
  2248. // cells. Also no tabs in cells (both are converted to blanks). On
  2249. // receipt of \intbl, transfer stored table info into _PF.
  2250. if(_fInTable)
  2251. _ecParseError = ecGeneralFailure;
  2252. _dwMaskPF |= PFM_TABSTOPS;
  2253. if(_wBorderWidth) // Store any border info
  2254. {
  2255. _PF._dwBorderColor = _dwBorderColor;
  2256. _PF._wBorders = _wBorders;
  2257. _PF._wBorderSpace = _wBorderSpace;
  2258. _PF._wBorderWidth = _wBorderWidth;
  2259. _dwMaskPF |= PFM_BORDER;
  2260. }
  2261. _PF._bAlignment = _bAlignment; // Row alignment (no cell align)
  2262. _PF._dxStartIndent = _xRowOffset; // \trleft N
  2263. _PF._dxOffset = _dxCell; // \trgaph N
  2264. _PF._wEffects |= PFE_TABLE;
  2265. _dwMaskPF |= PFM_TABLE | PFM_OFFSET | PFM_ALIGNMENT;
  2266. pstate->fInTable = TRUE;
  2267. break;
  2268. case tokenCell: // \cell
  2269. PARSERCOVERAGE_CASE();
  2270. if(_fInTable)
  2271. _ecParseError = ecGeneralFailure;
  2272. else if(pstate->fInTable)
  2273. {
  2274. if(!_cCell && _iTabsTable >= 0 && // No cells defined here;
  2275. _iTabsTable < GetTabsCache()->Count()) // Use previous table defs
  2276. {
  2277. CTabs *pTabs = GetTabsCache()->Elem(_iTabsTable);
  2278. if (pTabs)
  2279. {
  2280. _cCell = pTabs->_cTab;
  2281. for(int i = _cCell; i--; )
  2282. _rgxCell[i] = pTabs->_prgxTabs[i];
  2283. }
  2284. }
  2285. if(_iCell < _cCell) // Don't add more cells than
  2286. { // defined, since Word crashes
  2287. _iCell++; // Count cells inserted
  2288. HandleChar(CELL); // Insert cell delimiter
  2289. }
  2290. }
  2291. break;
  2292. case tokenCellHalfGap: // \trgaph N
  2293. PARSERCOVERAGE_CASE(); // Save half space between
  2294. _dxCell = iParam; // cells to add to tabs
  2295. break; // Roundtrip value at end of
  2296. // tab array
  2297. case tokenCellX: // \cellx N
  2298. PARSERCOVERAGE_CASE();
  2299. if(_cCell < MAX_TAB_STOPS) // Save cell right boundaries
  2300. { // for tab settings in our
  2301. if(!_cCell) // primitive table model
  2302. { // Save border info
  2303. _wBorders = _PF._wBorders;
  2304. _wBorderSpace = _PF._wBorderSpace;
  2305. _wBorderWidth = _PF._wBorderWidth;
  2306. }
  2307. _rgxCell[_cCell++] = iParam + (_bCellBrdrWdths << 24);
  2308. _bCellBrdrWdths = 0;
  2309. }
  2310. break;
  2311. case tokenRowDefault: // \trowd
  2312. PARSERCOVERAGE_CASE();
  2313. if(_fInTable) // Can't insert a table into
  2314. { // a table in RE 3.0
  2315. _ecParseError = ecGeneralFailure;
  2316. break;
  2317. }
  2318. // Insert newline if we are inserting a table behind characters in the
  2319. // same line. This follows the Word9 model.
  2320. if (_cpFirst == _prg->GetCp() && _cpThisPara != _cpFirst)
  2321. {
  2322. EC ec = _ped->fUseCRLF() // If RichEdit 1.0 compatibility
  2323. ? HandleText(szaCRLF, ALL_ASCII)// mode, use CRLF; else CR
  2324. : HandleChar((unsigned)(CR));
  2325. if(ec == ecNoError)
  2326. _cpThisPara = _prg->GetCp(); // New para starts after CRLF
  2327. }
  2328. _cCell = 0; // No cell right boundaries
  2329. _dxCell = 0; // or half gap defined yet
  2330. _xRowOffset = 0;
  2331. _bCellBrdrWdths = 0;
  2332. _wBorderWidth = 0; // No borders yet
  2333. _dwBorderColor = 0;
  2334. _bAlignment = PFA_LEFT;
  2335. _iTabsTable = -1; // No cell widths yet
  2336. break;
  2337. case tokenRowLeft: // \trleft
  2338. PARSERCOVERAGE_CASE();
  2339. _xRowOffset = iParam;
  2340. break;
  2341. case tokenRowAlignCenter: // \trqc
  2342. case tokenRowAlignRight: // \trqr
  2343. PARSERCOVERAGE_CASE();
  2344. _bAlignment = (WORD)(_token - tokenRowAlignRight + PFA_RIGHT);
  2345. break;
  2346. case tokenPage: // \page
  2347. // FUTURE: we want to be smarter about handling FF. But for
  2348. // now we just ignore it for bulletted and number paragraphs
  2349. // and just make it an EOP otherwise.
  2350. if (_PF._wNumbering != 0)
  2351. break;
  2352. // Intentional fall thru to EOP.
  2353. case tokenEndParagraph: // \par
  2354. case tokenLineBreak: // \line
  2355. PARSERCOVERAGE_CASE();
  2356. if(_pstateStackTop->fInTable)
  2357. HandleChar(' '); // Just use a blank for \par
  2358. else // in table
  2359. {
  2360. _cCell = 0;
  2361. HandleEndOfPara();
  2362. }
  2363. break;
  2364. case tokenRow: // \row. Treat as hard CR
  2365. PARSERCOVERAGE_CASE();
  2366. for( ; _iCell < _cCell; _iCell++) // If not enuf cells, add
  2367. HandleChar(CELL); // them since Word crashes
  2368. _iCell = 0; // if \cellx count differs
  2369. HandleEndOfPara(); // from \cell count
  2370. break;
  2371. case tokenParagraphDefault: // \pard
  2372. PARSERCOVERAGE_CASE();
  2373. if(IN_RANGE(destColorTable, pstate->sDest, destPicture))
  2374. {
  2375. _ecParseError = ecUnexpectedChar;
  2376. break;
  2377. }
  2378. if(pstate->sDest == destParaNumText) // Ignore if \pn destination
  2379. break;
  2380. // Else fall thru to \secd
  2381. case tokenEndSection: // \sect
  2382. case tokenSectionDefault: // \sectd
  2383. PARSERCOVERAGE_CASE();
  2384. bT = _PF._bOutlineLevel;
  2385. // Save outline level
  2386. _PF.InitDefault(_bDocType == DT_RTLDOC ? PFE_RTLPARA : 0);
  2387. // Reset para formatting
  2388. pstate->fInTable = FALSE; // Reset in table flag
  2389. pstate->fBullet = FALSE;
  2390. pstate->sIndentNumbering = 0;
  2391. _cTab = 0; // No tabs defined
  2392. _bTabLeader = 0;
  2393. _bTabType = 0;
  2394. _bBorder = 0;
  2395. _PF._bOutlineLevel = (BYTE)(bT | 1);
  2396. _dwMaskPF = PFM_ALLRTF;
  2397. break;
  2398. //----------------------- Field and Group Control Words --------------------------------
  2399. // Note that we currently don't support nested fields. For nested
  2400. // fields, the usage of _szSymbolFieldResult, _FieldCF, _ptfField
  2401. // and _sFieldCodePage needs to be rethought.
  2402. case tokenField: // \field
  2403. PARSERCOVERAGE_CASE();
  2404. if (pstate->sDest == destDocumentArea ||
  2405. pstate->sDest == destLeadingPunct ||
  2406. pstate->sDest == destFollowingPunct ||
  2407. pstate->fFieldInst)
  2408. {
  2409. // We're not equipped to handle symbols in these destinations, and
  2410. // we don't want the fields added accidentally to document text.
  2411. goto skip_group;
  2412. }
  2413. pstate->sDest = destField;
  2414. _nFieldCodePage = pstate->nCodePage; // init, for safety
  2415. _ptfField = NULL;
  2416. _fRestoreFieldFormat = TRUE;
  2417. break;
  2418. case tokenFieldResult: // \fldrslt
  2419. PARSERCOVERAGE_CASE();
  2420. pstate->fFieldInst = FALSE;
  2421. pstate->fFieldRslt = TRUE;
  2422. // Restore the formatting from the field instruction
  2423. // when we are doing Hyperlink
  2424. if(_fRestoreFieldFormat && _fHyperlinkField)
  2425. {
  2426. _CF = _FieldCF;
  2427. pstate->ptf = _ptfField;
  2428. pstate->SetCodePage(_nFieldCodePage);
  2429. _dwMaskCF = _dwMaskFieldCF;
  2430. _dwMaskCF2 = _dwMaskFieldCF2;
  2431. }
  2432. _fRestoreFieldFormat = FALSE;
  2433. if(!_fHyperlinkField)
  2434. {
  2435. // for SYMBOL
  2436. pstate->sDest = destField;
  2437. break;
  2438. }
  2439. // for HYPERLINK
  2440. // By now, we should have the whole hyperlink fldinst string
  2441. if(_szHyperlinkFldinst)
  2442. {
  2443. // V-GUYB: PWord Converter requires loss notification.
  2444. // (Hyperlinks are NOT streamed out later)
  2445. #ifdef REPORT_LOSSAGE
  2446. if(!(_dwFlags & SFF_SELECTION)) // SFF_SELECTION is set if any kind of paste is being done.
  2447. {
  2448. ((LOST_COOKIE*)(_pes->dwCookie))->bLoss = TRUE;
  2449. }
  2450. #endif // REPORT_LOSSAGE
  2451. BYTE * pNewBuffer = NULL;
  2452. // Check if this is a friendly name
  2453. if(_szHyperlinkFldinst[1] == '\"')
  2454. {
  2455. // This is a friendly name, replace quotes with <>.
  2456. // Also, for an unknown reason, Word escapes some chars in its HYPERLINK presentation
  2457. // we have to get rid of the backslashes
  2458. BYTE * pSrc = &_szHyperlinkFldinst[2];
  2459. BYTE * pBuffer;
  2460. BOOL fLeadByte = FALSE;
  2461. LONG CodePage;
  2462. CodePage = IsFECharSet(_bInstFieldCharSet) ? GetCodePage(_bInstFieldCharSet): 0;
  2463. pNewBuffer = (BYTE *)PvAlloc(_cchHyperlinkFldinstUsed+1, GMEM_ZEROINIT);
  2464. if(!pNewBuffer)
  2465. {
  2466. _ecParseError = ecNoMemory;
  2467. break;
  2468. }
  2469. pBuffer = pNewBuffer;
  2470. *pBuffer++ = ' ';
  2471. *pBuffer++ = '<';
  2472. do
  2473. {
  2474. if(!fLeadByte && *pSrc == '\\') // Get rid of backslashes
  2475. pSrc++;
  2476. else if(*pSrc == '\"')
  2477. {
  2478. *pBuffer = '>'; // Find end quote
  2479. break;
  2480. }
  2481. else if(CodePage)
  2482. {
  2483. // Check if this is a valid Lead byte.
  2484. fLeadByte = fLeadByte ? FALSE : GetTrailBytesCount(*pSrc, CodePage);
  2485. }
  2486. } while (*pBuffer++ = *pSrc++);
  2487. }
  2488. // No longer need this buffer...
  2489. FreePv(_szHyperlinkFldinst);
  2490. // Setup for the new scanned buffer
  2491. _szHyperlinkFldinst = pNewBuffer;
  2492. _cchHyperlinkFldinst = _cchHyperlinkFldinstUsed+1;
  2493. }
  2494. pstate->sDest = destFieldResult;
  2495. if(_szHyperlinkFldinst)
  2496. {
  2497. // Pre-alloc a buffer for the fldrslt strings
  2498. _cchHyperlinkFldrslt = MAX_PATH;
  2499. _cchHyperlinkFldrsltUsed = 0;
  2500. _szHyperlinkFldrslt = (BYTE *)PvAlloc(_cchHyperlinkFldrslt, GMEM_FIXED);
  2501. if(!_szHyperlinkFldrslt)
  2502. {
  2503. _ecParseError = ecNoMemory;
  2504. break;
  2505. }
  2506. _szHyperlinkFldrslt[0] = 0; // No text yet
  2507. }
  2508. else
  2509. {
  2510. _cchHyperlinkFldrslt = 0;
  2511. _cchHyperlinkFldrsltUsed = 0;
  2512. FreePv(_szHyperlinkFldrslt);
  2513. // No friendly HYPERLINK name, no need to accumulate fldrslt strings
  2514. _szHyperlinkFldrslt = 0;
  2515. _fHyperlinkField = FALSE;
  2516. }
  2517. break;
  2518. case tokenFieldInstruction: // \fldinst
  2519. PARSERCOVERAGE_CASE();
  2520. if(pstate->fFieldInst || pstate->fFieldRslt)
  2521. goto skip_group; // Skip nested field instr
  2522. pstate->fFieldInst = TRUE; // TODO: skip what follows up to \fldrslt
  2523. pstate->sDest = destFieldInstruction;
  2524. break;
  2525. case tokenStartGroup: // Save current state by
  2526. PARSERCOVERAGE_CASE(); // pushing it onto stack
  2527. _cbSkipForUnicode = 0;
  2528. HandleStartGroup();
  2529. if (_fNoRTFtoken)
  2530. {
  2531. // Hack Alert !!!!! FOr 1.0 compatibility to allow no \rtf token.
  2532. _fNoRTFtoken = FALSE;
  2533. pstate = _pstateStackTop;
  2534. goto rtf;
  2535. }
  2536. break;
  2537. case tokenEndGroup:
  2538. PARSERCOVERAGE_CASE();
  2539. _cbSkipForUnicode = 0;
  2540. HandleFieldEndGroup(); // Special end group handling for \field
  2541. HandleEndGroup(); // Restore save state by
  2542. break; // popping stack
  2543. case tokenOptionalDestination: // (see case tokenUnknown)
  2544. PARSERCOVERAGE_CASE();
  2545. break;
  2546. case tokenNullDestination: // We've found a destination
  2547. PARSERCOVERAGE_CASE();
  2548. // tokenNullDestination triggers a loss notifcation here for...
  2549. // Footer related tokens - "footer", "footerf", "footerl", "footerr",
  2550. // "footnote", "ftncn", "ftnsep", "ftnsepc"
  2551. // Header related tokens - "header", "headerf", "headerl", "headerr"
  2552. // Table of contents - "tc"
  2553. // Index entries - "xe"
  2554. // V-GUYB: PWord Converter requires loss notification.
  2555. #ifdef REPORT_LOSSAGE
  2556. if(!(_dwFlags & SFF_SELECTION)) // SFF_SELECTION is set if any kind of paste is being done.
  2557. {
  2558. ((LOST_COOKIE*)(_pes->dwCookie))->bLoss = TRUE;
  2559. }
  2560. #endif // REPORT_LOSSAGE
  2561. goto skip_group; // for which we should ignore
  2562. // the remainder of the group
  2563. case tokenUnknownKeyword:
  2564. PARSERCOVERAGE_CASE();
  2565. if(_tokenLast == tokenOptionalDestination)
  2566. goto skip_group;
  2567. break; // Nother place for
  2568. // unrecognized RTF
  2569. //-------------------------- Text Control Words --------------------------------
  2570. case tokenUnicode: // \u <n>
  2571. PARSERCOVERAGE_CASE();
  2572. // FUTURE: for now, we will ignore \u <n> when we are handling fields.
  2573. // We should re-visit the HYPERLINK handling where we are accumulating
  2574. // ASCII text. We will have to switch to aaccumulate Unicode in order
  2575. // to insert this \u <n> into the string.
  2576. if(_fHyperlinkField || pstate->fFieldInst)
  2577. break;
  2578. _dwMaskCF2 |= CFM2_RUNISDBCS;
  2579. _CF._dwEffects &= ~CFE_RUNISDBCS;
  2580. wT = (WORD)iParam; // Treat as unsigned integer
  2581. if(pstate->ptf && pstate->ptf->bCharSet == SYMBOL_CHARSET)
  2582. {
  2583. if(IN_RANGE(0xF000, wT, 0xF0FF)) // Compensate for converters
  2584. wT -= 0xF000; // that write symbol codes
  2585. // up high
  2586. else if(wT > 255) // Whoops, RTF is using con-
  2587. { // verted value for symbol:
  2588. char ach; // convert back
  2589. WCTMB(1252, 0, &wT, 1, &ach, 1, NULL, NULL, NULL);
  2590. wT = (BYTE)ach; // Review: use CP_ACP??
  2591. }
  2592. }
  2593. _cbSkipForUnicode = pstate->cbSkipForUnicodeMax;
  2594. AddText((TCHAR *)&wT, 1, TRUE); // (avoids endian problems)
  2595. break;
  2596. case tokenUnicodeCharByteCount: // \ucN
  2597. PARSERCOVERAGE_CASE();
  2598. if(IN_RANGE(0, iParam, 2))
  2599. pstate->cbSkipForUnicodeMax = (WORD)iParam;
  2600. break;
  2601. case tokenText: // Lexer concludes tokenText
  2602. case tokenASCIIText:
  2603. PARSERCOVERAGE_CASE();
  2604. switch (pstate->sDest)
  2605. {
  2606. case destColorTable:
  2607. pclrf = _colors.Add(1, NULL);
  2608. if(!pclrf)
  2609. goto OutOfRAM;
  2610. *pclrf = _fGetColorYet ?
  2611. RGB(pstate->bRed, pstate->bGreen, pstate->bBlue) : tomAutoColor;
  2612. // Prepare for next color table entry
  2613. pstate->bRed =
  2614. pstate->bGreen =
  2615. pstate->bBlue = 0;
  2616. _fGetColorYet = FALSE; // in case more "empty" color
  2617. break;
  2618. case destFontTable:
  2619. if(!pstate->fRealFontName)
  2620. {
  2621. ReadFontName(pstate,
  2622. _token == tokenASCIIText ?
  2623. ALL_ASCII : CONTAINS_NONASCII);
  2624. }
  2625. break;
  2626. case destRealFontName:
  2627. {
  2628. STATE * const pstatePrev = pstate->pstatePrev;
  2629. if(pstatePrev && pstatePrev->sDest == destFontTable)
  2630. {
  2631. // Mark previous state so that tagged font name will be ignored
  2632. // AROO: Do this before calling ReadFontName so that
  2633. // AROO: it doesn't try to match font name
  2634. pstatePrev->fRealFontName = TRUE;
  2635. ReadFontName(pstatePrev,
  2636. _token == tokenASCIIText ? ALL_ASCII : CONTAINS_NONASCII);
  2637. }
  2638. break;
  2639. }
  2640. case destFieldInstruction:
  2641. if(_szHyperlinkFldinst)
  2642. {
  2643. if(!IsFECharSet(_bInstFieldCharSet) && IsFECharSet(_CF._bCharSet))
  2644. _bInstFieldCharSet = _CF._bCharSet;
  2645. _ecParseError = AppendString(& _szHyperlinkFldinst, _szText, &_cchHyperlinkFldinst, &_cchHyperlinkFldinstUsed );
  2646. }
  2647. else
  2648. {
  2649. HandleFieldInstruction();
  2650. _bInstFieldCharSet = _CF._bCharSet;
  2651. }
  2652. break;
  2653. case destObjectClass:
  2654. if(StrAlloc(&_prtfObject->szClass, _szText))
  2655. goto OutOfRAM;
  2656. break;
  2657. case destObjectName:
  2658. if(StrAlloc(&_prtfObject->szName, _szText))
  2659. goto OutOfRAM;
  2660. break;
  2661. case destStyleSheet:
  2662. // _szText has style name, e.g., "heading 1"
  2663. if(W32->ASCIICompareI(_szText, (unsigned char *)"heading", 7))
  2664. {
  2665. dwT = (unsigned)(_szText[8] - '0');
  2666. if(dwT < NSTYLES)
  2667. _rgStyles[dwT] = (BYTE)_Style;
  2668. }
  2669. break;
  2670. case destDocumentArea:
  2671. case destFollowingPunct:
  2672. case destLeadingPunct:
  2673. break;
  2674. // This has been changed now. We will store the Punct strings as
  2675. // raw text strings. So, we don't have to convert them.
  2676. // This code is keep here just in case we want to change back.
  2677. #if 0
  2678. case destDocumentArea:
  2679. if (_tokenLast != tokenFollowingPunct &&
  2680. _tokenLast != tokenLeadingPunct)
  2681. {
  2682. break;
  2683. } // Else fall thru to
  2684. // destFollowingPunct
  2685. case destFollowingPunct:
  2686. case destLeadingPunct:
  2687. // TODO(BradO): Consider some kind of merging heuristic when
  2688. // we paste FE RTF (for lead and follow chars, that is).
  2689. if(!(_dwFlags & SFF_SELECTION))
  2690. {
  2691. int cwch = MBTWC(INVALID_CODEPAGE, 0,
  2692. (char *)_szText, -1,
  2693. NULL, 0,
  2694. NULL);
  2695. Assert(cwch);
  2696. WCHAR *pwchBuf = (WCHAR *)PvAlloc(cwch * sizeof(WCHAR), GMEM_ZEROINIT);
  2697. if(!pwchBuf)
  2698. goto OutOfRAM;
  2699. SideAssert(MBTWC(INVALID_CODEPAGE, 0,
  2700. (char *)_szText, -1,
  2701. pwchBuf, cwch,
  2702. NULL) > 0);
  2703. if(pstate->sDest == destFollowingPunct)
  2704. _ped->SetFollowingPunct(pwchBuf);
  2705. else
  2706. {
  2707. Assert(pstate->sDest == destLeadingPunct);
  2708. _ped->SetLeadingPunct(pwchBuf);
  2709. }
  2710. FreePv(pwchBuf);
  2711. }
  2712. break;
  2713. #endif
  2714. case destFieldResult:
  2715. if(_szSymbolFieldResult) // Field has been recalculated
  2716. break; // old result out of use
  2717. if(_szHyperlinkFldrslt) // Append _szText to _szHyperlinkFldrslt
  2718. {
  2719. _ecParseError = AppendString(&_szHyperlinkFldrslt, _szText,
  2720. &_cchHyperlinkFldrslt, &_cchHyperlinkFldrsltUsed);
  2721. break;
  2722. }
  2723. // FALL THRU to default case
  2724. default:
  2725. HandleText(_szText, _token == tokenASCIIText ? ALL_ASCII : CONTAINS_NONASCII);
  2726. }
  2727. break;
  2728. // \ltrmark, \rtlmark, \zwj, and \zwnj are translated directly into
  2729. // their Unicode values. \ltrmark and \rtlmark cause no further
  2730. // processing here because we assume that the current font has the
  2731. // CharSet needed to identify the direction.
  2732. case tokenLToRChars: // \ltrch
  2733. case tokenRToLChars: // \rtlch
  2734. pstate->fltrch = (_token == tokenLToRChars);
  2735. pstate->frtlch = !pstate->fltrch;
  2736. _ped->OrCharFlags(fBIDI);
  2737. break;
  2738. case tokenDBChars: // \dbch
  2739. pstate->fdbch = TRUE;
  2740. break;
  2741. case tokenLToRDocument: // \ltrdoc
  2742. PARSERCOVERAGE_CASE();
  2743. _bDocType = DT_LTRDOC;
  2744. break;
  2745. case tokenRToLDocument: // \rtldoc
  2746. PARSERCOVERAGE_CASE();
  2747. _bDocType = DT_RTLDOC;
  2748. break;
  2749. //------------------------- Object Control Words --------------------------------
  2750. case tokenObject: // \object
  2751. PARSERCOVERAGE_CASE();
  2752. // V-GUYB: PWord Converter requires loss notification.
  2753. #ifdef REPORT_LOSSAGE
  2754. if(!(_dwFlags & SFF_SELECTION)) // SFF_SELECTION is set if any kind of paste is being done.
  2755. {
  2756. ((LOST_COOKIE*)(_pes->dwCookie))->bLoss = TRUE;
  2757. }
  2758. #endif // REPORT_LOSSAGE
  2759. // Assume that the object failed to load until proven otherwise
  2760. // by RTFRead::ObjectReadFromEditStream
  2761. // This works for both:
  2762. // - an empty \objdata tag
  2763. // - a non-existent \objdata tag
  2764. _fFailedPrevObj = TRUE;
  2765. case tokenPicture: // \pict
  2766. PARSERCOVERAGE_CASE();
  2767. pstate->sDest = (SHORT)(_token == tokenPicture ? destPicture : destObject);
  2768. FreeRtfObject();
  2769. _prtfObject = (RTFOBJECT *) PvAlloc(sizeof(RTFOBJECT), GMEM_ZEROINIT);
  2770. if(!_prtfObject)
  2771. goto OutOfRAM;
  2772. _prtfObject->xScale = _prtfObject->yScale = 100;
  2773. _prtfObject->cBitsPerPixel = 1;
  2774. _prtfObject->cColorPlanes = 1;
  2775. _prtfObject->szClass = NULL;
  2776. _prtfObject->szName = NULL;
  2777. _prtfObject->sType = -1;
  2778. break;
  2779. case tokenObjectEmbedded: // \objemb
  2780. case tokenObjectLink: // \objlink
  2781. case tokenObjectAutoLink: // \objautlink
  2782. PARSERCOVERAGE_CASE();
  2783. _prtfObject->sType = (SHORT)(_token - tokenObjectEmbedded + ROT_Embedded);
  2784. break;
  2785. case tokenObjectMacSubscriber: // \objsub
  2786. case tokenObjectMacPublisher: // \objpub
  2787. case tokenObjectMacICEmbedder:
  2788. PARSERCOVERAGE_CASE();
  2789. _prtfObject->sType = ROT_MacEdition;
  2790. break;
  2791. case tokenWidth: // \picw or \objw
  2792. PARSERCOVERAGE_CASE();
  2793. _prtfObject->xExt = iParam;
  2794. break;
  2795. case tokenHeight: // \pic or \objh
  2796. PARSERCOVERAGE_CASE();
  2797. _prtfObject->yExt = iParam;
  2798. break;
  2799. case tokenObjectSetSize: // \objsetsize
  2800. PARSERCOVERAGE_CASE();
  2801. _prtfObject->fSetSize = TRUE;
  2802. break;
  2803. case tokenScaleX: // \picscalex or \objscalex
  2804. PARSERCOVERAGE_CASE();
  2805. _prtfObject->xScale = iParam;
  2806. break;
  2807. case tokenScaleY: // \picscaley or \objscaley
  2808. PARSERCOVERAGE_CASE();
  2809. _prtfObject->yScale = iParam;
  2810. break;
  2811. case tokenCropLeft: // \piccropl or \objcropl
  2812. case tokenCropTop: // \piccropt or \objcropt
  2813. case tokenCropRight: // \piccropr or \objcropr
  2814. case tokenCropBottom: // \piccropb or \objcropb
  2815. PARSERCOVERAGE_CASE();
  2816. *((LONG *)&_prtfObject->rectCrop
  2817. + (_token - tokenCropLeft)) = iParam;
  2818. break;
  2819. case tokenObjectClass: // \objclass
  2820. PARSERCOVERAGE_CASE();
  2821. pstate->sDest = destObjectClass;
  2822. break;
  2823. case tokenObjectName: // \objname
  2824. PARSERCOVERAGE_CASE();
  2825. pstate->sDest = destObjectName;
  2826. break;
  2827. case tokenObjectResult: // \result
  2828. PARSERCOVERAGE_CASE();
  2829. if(_prtfObject->sType==ROT_MacEdition ||// If it's Mac stuff, we don't understand the data,
  2830. _fFailedPrevObj || _fNeedPres) // or if we need an obj presentation,
  2831. {
  2832. pstate->sDest = destRTF; // use the object results
  2833. break;
  2834. }
  2835. goto skip_group;
  2836. case tokenObjectData: // \objdata
  2837. PARSERCOVERAGE_CASE();
  2838. pstate->sDest = destObjectData;
  2839. if(_prtfObject->sType==ROT_MacEdition) // It's Mac stuff so just
  2840. goto skip_group; // throw away the data
  2841. break;
  2842. case tokenPictureWindowsMetafile: // wmetafile
  2843. #ifdef NOMETAFILES
  2844. goto skip_group;
  2845. #endif NOMETAFILES
  2846. case tokenPictureWindowsBitmap: // wbitmap
  2847. case tokenPictureWindowsDIB: // dibitmap
  2848. PARSERCOVERAGE_CASE();
  2849. _prtfObject->sType = (SHORT)(_token - tokenPictureWindowsBitmap + ROT_Bitmap);
  2850. _prtfObject->sPictureType = (SHORT)iParam;
  2851. break;
  2852. case tokenBitmapBitsPerPixel: // \wbmbitspixel
  2853. PARSERCOVERAGE_CASE();
  2854. _prtfObject->cBitsPerPixel = (SHORT)iParam;
  2855. break;
  2856. case tokenBitmapNumPlanes: // \wbmplanes
  2857. PARSERCOVERAGE_CASE();
  2858. _prtfObject->cColorPlanes = (SHORT)iParam;
  2859. break;
  2860. case tokenBitmapWidthBytes: // \wbmwidthbytes
  2861. PARSERCOVERAGE_CASE();
  2862. _prtfObject->cBytesPerLine = (SHORT)iParam;
  2863. break;
  2864. case tokenDesiredWidth: // \picwgoal
  2865. PARSERCOVERAGE_CASE();
  2866. _prtfObject->xExtGoal = (SHORT)iParam;
  2867. break;
  2868. case tokenDesiredHeight: // \pichgoal
  2869. PARSERCOVERAGE_CASE();
  2870. _prtfObject->yExtGoal = (SHORT)iParam;
  2871. break;
  2872. case tokenBinaryData: // \bin
  2873. PARSERCOVERAGE_CASE();
  2874. if(_cbSkipForUnicode)
  2875. {
  2876. // a \binN and its associated binary data count as a single
  2877. // character for the purposes of skipping over characters
  2878. // following a \uN
  2879. _cbSkipForUnicode--;
  2880. SkipBinaryData(iParam);
  2881. }
  2882. else
  2883. {
  2884. // update OleGet function
  2885. RTFReadOLEStream.lpstbl->Get =
  2886. (DWORD (CALLBACK* )(LPOLESTREAM, void FAR*, DWORD))
  2887. RTFGetBinaryDataFromStream;
  2888. // set data length
  2889. _cbBinLeft = iParam;
  2890. switch (pstate->sDest)
  2891. {
  2892. case destObjectData:
  2893. _fFailedPrevObj = !ObjectReadFromEditStream();
  2894. break;
  2895. case destPicture:
  2896. StaticObjectReadFromEditStream(iParam);
  2897. break;
  2898. default:
  2899. AssertSz(FALSE, "Binary data hit but don't know where to put it");
  2900. }
  2901. // restore OleGet function
  2902. RTFReadOLEStream.lpstbl->Get =
  2903. (DWORD (CALLBACK* )(LPOLESTREAM, void FAR*, DWORD))
  2904. RTFGetFromStream;
  2905. }
  2906. break;
  2907. case tokenObjectDataValue:
  2908. PARSERCOVERAGE_CASE();
  2909. _fFailedPrevObj = !ObjectReadFromEditStream();
  2910. goto EndOfObjectStream;
  2911. case tokenPictureDataValue:
  2912. PARSERCOVERAGE_CASE();
  2913. StaticObjectReadFromEditStream();
  2914. EndOfObjectStream:
  2915. if(!SkipToEndOfGroup())
  2916. HandleEndGroup();
  2917. break;
  2918. case tokenObjectPlaceholder:
  2919. PARSERCOVERAGE_CASE();
  2920. if(_ped->GetEventMask() & ENM_OBJECTPOSITIONS)
  2921. {
  2922. if(!_pcpObPos)
  2923. {
  2924. _pcpObPos = (LONG *)PvAlloc(sizeof(ULONG) * cobPosInitial, GMEM_ZEROINIT);
  2925. if(!_pcpObPos)
  2926. {
  2927. _ecParseError = ecNoMemory;
  2928. break;
  2929. }
  2930. _cobPosFree = cobPosInitial;
  2931. _cobPos = 0;
  2932. }
  2933. if(_cobPosFree-- <= 0)
  2934. {
  2935. const int cobPosNew = _cobPos + cobPosChunk;
  2936. LPVOID pv;
  2937. pv = PvReAlloc(_pcpObPos, sizeof(ULONG) * cobPosNew);
  2938. if(!pv)
  2939. {
  2940. _ecParseError = ecNoMemory;
  2941. break;
  2942. }
  2943. _pcpObPos = (LONG *)pv;
  2944. _cobPosFree = cobPosChunk - 1;
  2945. }
  2946. _pcpObPos[_cobPos++] = _prg->GetCp();
  2947. }
  2948. break;
  2949. default:
  2950. PARSERCOVERAGE_DEFAULT();
  2951. if(pstate->sDest != destFieldInstruction && // Values outside token
  2952. (DWORD)(_token - tokenMin) > // range are treated
  2953. (DWORD)(tokenMax - tokenMin)) // as Unicode chars
  2954. {
  2955. // Currently we don't allow TABs in tables
  2956. if(_token == TAB && pstate->fInTable)
  2957. _token = TEXT(' ');
  2958. // 1.0 mode doesn't use Unicode bullets nor smart quotes
  2959. if (_ped->Get10Mode() && IN_RANGE(LQUOTE, _token, RDBLQUOTE))
  2960. {
  2961. if (_token == LQUOTE || _token == RQUOTE)
  2962. _token = L'\'';
  2963. else if (_token == LDBLQUOTE || _token == RDBLQUOTE)
  2964. _token = L'\"';
  2965. }
  2966. HandleChar(_token);
  2967. }
  2968. #if defined(DEBUG)
  2969. else
  2970. {
  2971. if(GetProfileIntA("RICHEDIT DEBUG", "RTFCOVERAGE", 0))
  2972. {
  2973. CHAR *pszKeyword = PszKeywordFromToken(_token);
  2974. CHAR szBuf[256];
  2975. sprintf(szBuf, "CRTFRead::HandleToken(): Token not processed - token = %d, %s%s%s",
  2976. _token,
  2977. "keyword = ",
  2978. pszKeyword ? "\\" : "<unknown>",
  2979. pszKeyword ? pszKeyword : "");
  2980. AssertSz(0, szBuf);
  2981. }
  2982. }
  2983. #endif
  2984. }
  2985. done:
  2986. TRACEERRSZSC("HandleToken()", - _ecParseError);
  2987. return _ecParseError;
  2988. }
  2989. /*
  2990. * CRTFRead::ReadRtf()
  2991. *
  2992. * @mfunc
  2993. * The range _prg is replaced by RTF data resulting from parsing the
  2994. * input stream _pes. The CRTFRead object assumes that the range is
  2995. * already degenerate (caller has to delete the range contents, if
  2996. * any, before calling this routine). Currently any info not used
  2997. * or supported by RICHEDIT is thrown away.
  2998. *
  2999. * @rdesc
  3000. * Number of chars inserted into text. 0 means none were inserted
  3001. * OR an error occurred.
  3002. */
  3003. LONG CRTFRead::ReadRtf()
  3004. {
  3005. TRACEBEGIN(TRCSUBSYSRTFR, TRCSCOPEINTERN, "CRTFRead::ReadRtf");
  3006. CTxtRange * prg = _prg;
  3007. STATE * pstate;
  3008. LONG cpFirstInPara;
  3009. _cpFirst = prg->GetCp();
  3010. if(!InitLex())
  3011. goto Quit;
  3012. TESTPARSERCOVERAGE();
  3013. AssertSz(!prg->GetCch(),
  3014. "CRTFRead::ReadRtf: range must be deleted");
  3015. if(!(_dwFlags & SFF_SELECTION))
  3016. {
  3017. // SFF_SELECTION is set if any kind of paste is being done, i.e.,
  3018. // not just that using the selection. If it isn't set, data is
  3019. // being streamed in and we allow this to reset the doc params
  3020. _ped->InitDocInfo();
  3021. }
  3022. prg->SetIgnoreFormatUpdate(TRUE);
  3023. _szUnicode = (TCHAR *)PvAlloc(cachTextMax * sizeof(TCHAR), GMEM_ZEROINIT);
  3024. if(!_szUnicode) // Allocate space for Unicode conversions
  3025. {
  3026. _ped->GetCallMgr()->SetOutOfMemory();
  3027. _ecParseError = ecNoMemory;
  3028. goto CleanUp;
  3029. }
  3030. _cchUnicode = cachTextMax;
  3031. // Initialize per-read variables
  3032. _nCodePage = (_dwFlags & SF_USECODEPAGE)
  3033. ? (_dwFlags >> 16) : INVALID_CODEPAGE;
  3034. // Populate _PF with initial paragraph formatting properties
  3035. _PF = *prg->GetPF();
  3036. _dwMaskPF = PFM_ALLRTF; // Setup initial MaskPF
  3037. _PF._iTabs = -1; // In case it's not -1
  3038. _fInTable = _PF.InTable();
  3039. // V-GUYB: PWord Converter requires loss notification.
  3040. #ifdef REPORT_LOSSAGE
  3041. if(!(_dwFlags & SFF_SELECTION)) // SFF_SELECTION is set if any
  3042. { // kind of paste is being done
  3043. ((LOST_COOKIE*)(_pes->dwCookie))->bLoss = FALSE;
  3044. }
  3045. #endif // REPORT_LOSSAGE
  3046. // Valid RTF files start with "{\rtf", "{urtf", or "{\pwd"
  3047. GetChar(); // Fill input buffer
  3048. UngetChar(); // Put char back
  3049. if(!IsRTF((char *)_pchRTFCurrent)) // Is it RTF?
  3050. { // No
  3051. if (_ped->Get10Mode())
  3052. _fNoRTFtoken = TRUE;
  3053. else
  3054. {
  3055. _ecParseError = ecUnexpectedToken; // Signal bad file
  3056. goto CleanUp;
  3057. }
  3058. }
  3059. // If initial cp follows EOP, use it for _cpThisPara. Else
  3060. // search for start of para containing the initial cp.
  3061. _cpThisPara = _cpFirst;
  3062. if(!prg->_rpTX.IsAfterEOP())
  3063. {
  3064. CTxtPtr tp(prg->_rpTX);
  3065. tp.FindEOP(tomBackward);
  3066. _cpThisPara = tp.GetCp();
  3067. }
  3068. cpFirstInPara = _cpThisPara; // Backup to start of para before
  3069. // parsing
  3070. while ( TokenGetToken() != tokenEOF && // Process tokens
  3071. _token != tokenError &&
  3072. !HandleToken() &&
  3073. _pstateStackTop )
  3074. ;
  3075. if(_iCell)
  3076. {
  3077. if(_ecParseError == ecTextMax) // Truncated table. Delete incomplete
  3078. { // row to keep Word happy
  3079. CTxtPtr tp(prg->_rpTX);
  3080. prg->Set(prg->GetCp(), -tp.FindEOP(tomBackward));
  3081. prg->Delete(NULL, SELRR_IGNORE);
  3082. }
  3083. else
  3084. AssertSz(FALSE, "CRTFRead::ReadRTF: Inserted cells but no row end");
  3085. }
  3086. _cCell = _iCell = 0;
  3087. prg->SetIgnoreFormatUpdate(FALSE); // Enable range _iFormat updates
  3088. prg->Update_iFormat(-1); // Update _iFormat to CF
  3089. // at current active end
  3090. if(!(_dwFlags & SFF_SELECTION)) // RTF applies to document:
  3091. { // update CDocInfo
  3092. // Apply char and para formatting of
  3093. // final text run to final CR
  3094. if(prg->GetCp() == _ped->GetAdjustedTextLength())
  3095. {
  3096. // REVIEW: we need to think about what para properties should
  3097. // be transferred here. E.g., borders were being transferred
  3098. // incorrectly
  3099. _dwMaskPF &= ~(PFM_BORDER | PFM_SHADING);
  3100. Apply_PF();
  3101. prg->ExtendFormattingCRLF();
  3102. }
  3103. // Update the per-document information from the RTF read
  3104. CDocInfo *pDocInfo = _ped->GetDocInfo();
  3105. if(!pDocInfo)
  3106. {
  3107. _ecParseError = ecNoMemory;
  3108. goto CleanUp;
  3109. }
  3110. if (ecNoError == _ecParseError) // If range end EOP wasn't
  3111. { // deleted and new text
  3112. prg->DeleteTerminatingEOP(NULL); // ends with an EOP, delete that EOP
  3113. }
  3114. pDocInfo->wCpg = (WORD)(_nCodePage == INVALID_CODEPAGE ?
  3115. tomInvalidCpg : _nCodePage);
  3116. if (pDocInfo->wCpg == CP_UTF8)
  3117. pDocInfo->wCpg = 1252;
  3118. _ped->SetDefaultLCID(_sDefaultLanguage == INVALID_LANGUAGE ?
  3119. tomInvalidLCID :
  3120. MAKELCID(_sDefaultLanguage, SORT_DEFAULT));
  3121. _ped->SetDefaultLCIDFE(_sDefaultLanguageFE == INVALID_LANGUAGE ?
  3122. tomInvalidLCID :
  3123. MAKELCID(_sDefaultLanguageFE, SORT_DEFAULT));
  3124. _ped->SetDefaultTabStop(TWIPS_TO_FPPTS(_sDefaultTabWidth));
  3125. _ped->SetDocumentType(_bDocType);
  3126. }
  3127. if(_ped->IsComplexScript() && prg->GetCp() > cpFirstInPara)
  3128. {
  3129. Assert(!prg->GetCch());
  3130. LONG cpSave = prg->GetCp();
  3131. LONG cpLastInPara = cpSave;
  3132. if(!prg->_rpTX.IsAtEOP())
  3133. {
  3134. CTxtPtr tp(prg->_rpTX);
  3135. tp.FindEOP(tomForward);
  3136. cpLastInPara = tp.GetCp();
  3137. prg->Advance(cpLastInPara - cpSave);
  3138. }
  3139. // Itemize from the start of paragraph to be inserted till the end of
  3140. // paragraph inserting. We need to cover all affected paragraphs because
  3141. // paragraphs we're playing could possibly in conflict direction. Think
  3142. // about the case that the range covers one LTR para and one RTL para, then
  3143. // the inserting text covers one RTL and one LTR. Both paragraphs' direction
  3144. // could have been changed after this insertion.
  3145. prg->ItemizeReplaceRange(cpLastInPara - cpFirstInPara, 0, NULL);
  3146. if (cpLastInPara != cpSave)
  3147. prg->SetCp(cpSave);
  3148. }
  3149. CleanUp:
  3150. FreeRtfObject();
  3151. pstate = _pstateStackTop;
  3152. if(pstate) // Illegal RTF file. Release
  3153. { // unreleased format indices
  3154. if(ecNoError == _ecParseError) // It's only an overflow if no
  3155. _ecParseError = ecStackOverflow; // other error has occurred
  3156. while(pstate->pstatePrev)
  3157. {
  3158. pstate = pstate->pstatePrev;
  3159. ReleaseFormats(pstate->iCF, -1);
  3160. }
  3161. }
  3162. pstate = _pstateLast;
  3163. if(pstate)
  3164. {
  3165. while(pstate->pstatePrev) // Free all but first STATE
  3166. {
  3167. pstate->DeletePF();
  3168. pstate = pstate->pstatePrev;
  3169. FreePv(pstate->pstateNext);
  3170. }
  3171. pstate->DeletePF();
  3172. }
  3173. Assert(_PF._iTabs == -1);
  3174. FreePv(pstate); // Free first STATE
  3175. FreePv(_szUnicode);
  3176. FreePv(_szHyperlinkFldinst);
  3177. FreePv(_szHyperlinkFldrslt);
  3178. Quit:
  3179. DeinitLex();
  3180. if(_pcpObPos)
  3181. {
  3182. if((_ped->GetEventMask() & ENM_OBJECTPOSITIONS) && _cobPos > 0)
  3183. {
  3184. OBJECTPOSITIONS obpos;
  3185. obpos.cObjectCount = _cobPos;
  3186. obpos.pcpPositions = _pcpObPos;
  3187. if (_ped->Get10Mode())
  3188. {
  3189. int i;
  3190. LONG *pcpPositions = _pcpObPos;
  3191. for (i = 0; i < _cobPos; i++, pcpPositions++)
  3192. *pcpPositions = _ped->GetAcpFromCp(*pcpPositions);
  3193. }
  3194. _ped->TxNotify(EN_OBJECTPOSITIONS, &obpos);
  3195. }
  3196. FreePv(_pcpObPos);
  3197. _pcpObPos = NULL;
  3198. }
  3199. #ifdef MACPORT
  3200. #if defined(ERROR_HANDLE_EOF) && ERROR_HANDLE_EOF != 38L
  3201. #error "ERROR_HANDLE_EOF value incorrect"
  3202. #endif
  3203. // transcribed from winerror.h
  3204. #define ERROR_HANDLE_EOF 38L
  3205. #endif
  3206. // FUTURE(BradO): We should devise a direct mapping from our error codes
  3207. // to Win32 error codes. In particular our clients are
  3208. // not expecting the error code produced by:
  3209. // _pes->dwError = (DWORD) -(LONG) _ecParseError;
  3210. if(_ecParseError)
  3211. {
  3212. AssertSz(_ecParseError >= 0,
  3213. "Parse error is negative");
  3214. if(_ecParseError == ecTextMax)
  3215. {
  3216. _ped->GetCallMgr()->SetMaxText();
  3217. _pes->dwError = (DWORD)STG_E_MEDIUMFULL;
  3218. }
  3219. if(_ecParseError == ecUnexpectedEOF)
  3220. _pes->dwError = (DWORD)HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
  3221. if(!_pes->dwError && _ecParseError != ecTruncateAtCRLF)
  3222. _pes->dwError = (DWORD) -(LONG) _ecParseError;
  3223. #if defined(DEBUG)
  3224. TRACEERRSZSC("CchParse_", _pes->dwError);
  3225. if(ecNoError < _ecParseError && _ecParseError < ecLastError)
  3226. Tracef(TRCSEVERR, "Parse error: %s", rgszParseError[_ecParseError]);
  3227. #endif
  3228. }
  3229. return prg->GetCp() - _cpFirst;
  3230. }
  3231. /*
  3232. * CRTFRead::CpgInfoFromFaceName()
  3233. *
  3234. * @mfunc
  3235. * This routine fills in the TEXTFONT::bCharSet and TEXTFONT::nCodePage
  3236. * members of the TEXTFONT structure by querying the system for the
  3237. * metrics of the font described by TEXTFONT::szName.
  3238. *
  3239. * @rdesc
  3240. * A flag indicating whether the charset and codepage were successfully
  3241. * determined.
  3242. */
  3243. BOOL CRTFRead::CpgInfoFromFaceName(
  3244. TEXTFONT *ptf)
  3245. {
  3246. // FUTURE(BradO): This code is a condensed version of a more sophisticated
  3247. // algorithm we use in font.cpp to second-guess the font-mapper.
  3248. // We should factor out the code from font.cpp for use here as well.
  3249. // Indicates that we've tried to obtain the cpg info from the system,
  3250. // so that after a failure we don't re-call this routine.
  3251. ptf->fCpgFromSystem = TRUE;
  3252. if(ptf->fNameIsDBCS)
  3253. {
  3254. // If fNameIsDBCS, we have high-ANSI characters in the facename, and
  3255. // no codepage with which to interpret them. The facename is gibberish,
  3256. // so don't waste time calling the system to match it.
  3257. return FALSE;
  3258. }
  3259. HDC hdc = _ped->TxGetDC();
  3260. if(!hdc)
  3261. return FALSE;
  3262. LOGFONT lf = {0};
  3263. TEXTMETRIC tm;
  3264. wcscpy(lf.lfFaceName, ptf->szName);
  3265. lf.lfCharSet = GetCharSet(GetSystemDefaultCodePage());
  3266. if(!GetTextMetrics(hdc, lf, tm) || tm.tmCharSet != lf.lfCharSet)
  3267. {
  3268. lf.lfCharSet = DEFAULT_CHARSET; // Doesn't match default sys
  3269. GetTextMetrics(hdc, lf, tm); // charset, so see what
  3270. } // DEFAULT_CHARSET gives
  3271. _ped->TxReleaseDC(hdc);
  3272. if(tm.tmCharSet != DEFAULT_CHARSET) // Got something, so use it
  3273. {
  3274. ptf->bCharSet = tm.tmCharSet;
  3275. ptf->sCodePage = (SHORT)GetCodePage(tm.tmCharSet);
  3276. return TRUE;
  3277. }
  3278. return FALSE;
  3279. }
  3280. #if defined(DEBUG)
  3281. /*
  3282. * CRTFRead::TestParserCoverage()
  3283. *
  3284. * @mfunc
  3285. * A debug routine used to test the coverage of HandleToken. The routine
  3286. * puts the routine into a debug mode and then determines:
  3287. *
  3288. * 1. Dead tokens - (T & !S & !P)
  3289. * Here, token:
  3290. * a) is defined in tom.h (T)
  3291. * b) does not have a corresponding keyword (not scanned) (!S)
  3292. * c) is not processed by HandleToken (!P)
  3293. * 2. Tokens that are parsed but not scanned - (T & !S & P)
  3294. * Here, token:
  3295. * a) is defined in tom.h (T)
  3296. * b) does not have a corresponding keyword (not scanned) (!S}
  3297. * c) is processed by HandleToken (P)
  3298. * 3. Tokens that are scanned but not parsed - (T & S & !P)
  3299. * Here, token:
  3300. * a) is defined in tom.h (T)
  3301. * b) does have a corresponding keyword (is scanned) (S)
  3302. * c) is not processed by HandleToken (!P)
  3303. */
  3304. void CRTFRead::TestParserCoverage()
  3305. {
  3306. int i;
  3307. char *rgpszKeyword[tokenMax - tokenMin];
  3308. BOOL rgfParsed[tokenMax - tokenMin];
  3309. char szBuf[256];
  3310. // Put HandleToken in debug mode
  3311. _fTestingParserCoverage = TRUE;
  3312. // Gather info about tokens/keywords
  3313. for(i = 0; i < tokenMax - tokenMin; i++)
  3314. {
  3315. _token = (TOKEN)(i + tokenMin);
  3316. rgpszKeyword[i] = PszKeywordFromToken(_token);
  3317. rgfParsed[i] = HandleToken() == ecNoError;
  3318. }
  3319. // Reset HandleToken to non-debug mode
  3320. _fTestingParserCoverage = FALSE;
  3321. // Should coverage check include those we know will fail test, but
  3322. // which we've examined and know why they fail?
  3323. BOOL fExcuseCheckedToks = TRUE;
  3324. if(GetProfileIntA("RICHEDIT DEBUG", "RTFCOVERAGESTRICT", 0))
  3325. fExcuseCheckedToks = FALSE;
  3326. // (T & !S & !P) (1. above)
  3327. for(i = 0; i < tokenMax - tokenMin; i++)
  3328. {
  3329. if(rgpszKeyword[i] || rgfParsed[i])
  3330. continue;
  3331. TOKEN tok = (TOKEN)(i + tokenMin);
  3332. // token does not correspond to a keyword, but still may be scanned
  3333. // check list of individual symbols which are scanned
  3334. if(FTokIsSymbol(tok))
  3335. continue;
  3336. // check list of tokens which have been checked and fail
  3337. // the sanity check for some known reason (see FTokFailsCoverageTest def'n)
  3338. if(fExcuseCheckedToks && FTokFailsCoverageTest(tok))
  3339. continue;
  3340. sprintf(szBuf, "CRTFRead::TestParserCoverage(): Token neither scanned nor parsed - token = %d", tok);
  3341. AssertSz(0, szBuf);
  3342. }
  3343. // (T & !S & P) (2. above)
  3344. for(i = 0; i < tokenMax - tokenMin; i++)
  3345. {
  3346. if(rgpszKeyword[i] || !rgfParsed[i])
  3347. continue;
  3348. TOKEN tok = (TOKEN)(i + tokenMin);
  3349. // token does not correspond to a keyword, but still may be scanned
  3350. // check list of individual symbols which are scanned
  3351. if(FTokIsSymbol(tok))
  3352. continue;
  3353. // check list of tokens which have been checked and fail
  3354. // the sanity check for some known reason (see FTokFailsCoverageTest def'n)
  3355. if(fExcuseCheckedToks && FTokFailsCoverageTest(tok))
  3356. continue;
  3357. sprintf(szBuf, "CRTFRead::TestParserCoverage(): Token parsed but not scanned - token = %d", tok);
  3358. AssertSz(0, szBuf);
  3359. }
  3360. // (T & S & !P) (3. above)
  3361. for(i = 0; i < tokenMax - tokenMin; i++)
  3362. {
  3363. if(!rgpszKeyword[i] || rgfParsed[i])
  3364. continue;
  3365. TOKEN tok = (TOKEN)(i + tokenMin);
  3366. // check list of tokens which have been checked and fail
  3367. // the sanity check for some known reason (see FTokFailsCoverageTest def'n)
  3368. if(fExcuseCheckedToks && FTokFailsCoverageTest(tok))
  3369. continue;
  3370. sprintf(szBuf, "CRTFRead::TestParserCoverage(): Token scanned but not parsed - token = %d, tag = \\%s", tok, rgpszKeyword[i]);
  3371. AssertSz(0, szBuf);
  3372. }
  3373. }
  3374. /*
  3375. * CRTFRead::PszKeywordFromToken()
  3376. *
  3377. * @mfunc
  3378. * Searches the array of keywords and returns the keyword
  3379. * string corresponding to the token supplied
  3380. *
  3381. * @rdesc
  3382. * returns a pointer to the keyword string if one exists
  3383. * and NULL otherwise
  3384. */
  3385. CHAR *CRTFRead::PszKeywordFromToken(TOKEN token)
  3386. {
  3387. for(int i = 0; i < cKeywords; i++)
  3388. {
  3389. if(rgKeyword[i].token == token)
  3390. return rgKeyword[i].szKeyword;
  3391. }
  3392. return NULL;
  3393. }
  3394. /*
  3395. * CRTFRead::FTokIsSymbol(TOKEN tok)
  3396. *
  3397. * @mfunc
  3398. * Returns a BOOL indicating whether the token, tok, corresponds to an RTF symbol
  3399. * (that is, one of a list of single characters that are scanned in the
  3400. * RTF reader)
  3401. *
  3402. * @rdesc
  3403. * BOOL - indicates whether the token corresponds to an RTF symbol
  3404. *
  3405. */
  3406. BOOL CRTFRead::FTokIsSymbol(TOKEN tok)
  3407. {
  3408. const BYTE *pbSymbol = NULL;
  3409. extern const BYTE szSymbolKeywords[];
  3410. extern const TOKEN tokenSymbol[];
  3411. // check list of individual symbols which are scanned
  3412. for(pbSymbol = szSymbolKeywords; *pbSymbol; pbSymbol++)
  3413. {
  3414. if(tokenSymbol[pbSymbol - szSymbolKeywords] == tok)
  3415. return TRUE;
  3416. }
  3417. return FALSE;
  3418. }
  3419. /*
  3420. * CRTFRead::FTokFailsCoverageTest(TOKEN tok)
  3421. *
  3422. * @mfunc
  3423. * Returns a BOOL indicating whether the token, tok, is known to fail the
  3424. * RTF parser coverage test. These tokens are those that have been checked
  3425. * and either:
  3426. * 1) have been implemented correctly, but just elude the coverage test
  3427. * 2) have yet to be implemented, and have been recognized as such
  3428. *
  3429. * @rdesc
  3430. * BOOL - indicates whether the token has been checked and fails the
  3431. * the parser coverage test for some known reason
  3432. *
  3433. */
  3434. BOOL CRTFRead::FTokFailsCoverageTest(TOKEN tok)
  3435. {
  3436. switch(tok)
  3437. {
  3438. // (T & !S & !P) (1. in TestParserCoverage)
  3439. // these really aren't tokens per se, but signal ending conditions for the parse
  3440. case tokenError:
  3441. case tokenEOF:
  3442. // (T & !S & P) (2. in TestParserCoverage)
  3443. // emitted by scanner, but don't correspond to recognized RTF keyword
  3444. case tokenUnknownKeyword:
  3445. case tokenText:
  3446. case tokenASCIIText:
  3447. // recognized directly (before the scanner is called)
  3448. case tokenStartGroup:
  3449. case tokenEndGroup:
  3450. // recognized using context information (before the scanner is called)
  3451. case tokenObjectDataValue:
  3452. case tokenPictureDataValue:
  3453. // (T & S & !P) (3. in TestParserCoverage)
  3454. // None
  3455. return TRUE;
  3456. }
  3457. return FALSE;
  3458. }
  3459. #endif // DEBUG
  3460. // Including a source file, but we only want to compile this code for debug purposes
  3461. // TODO: Implement RTF tag logging for the Mac
  3462. #if defined(DEBUG) && !defined(MACPORT)
  3463. #include "rtflog.cpp"
  3464. #endif