Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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