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

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