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

4517 lines
123 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. if (ch == SEPARATOR)
  602. {
  603. rg.Move(2, TRUE);
  604. rg.ReplaceRange(0, NULL, NULL, SELRR_IGNORE, NULL, RR_NO_LP_CHECK);
  605. }
  606. else //No field result
  607. {
  608. cchInst = cchResult;
  609. cchResult = 0;
  610. }
  611. BOOL fBackSlash = FALSE;
  612. chNext = 0;
  613. while((ch = rg.CRchTxtPtr::GetChar()) != STARTFIELD || chNext != 'F')
  614. {
  615. if(ch == BSLASH)
  616. fBackSlash = TRUE; // Need backslash pass
  617. if(!rg.Move(-1, FALSE))
  618. return; // Nothing to fix up
  619. cchInst++;
  620. chNext = ch;
  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(_cCell < MAX_TABLE_CELLS) // Save cell right boundaries
  1082. {
  1083. if(!_cCell)
  1084. { // Save border info
  1085. _wBorders = _PF._wBorders;
  1086. _wBorderSpace = _PF._wBorderSpace;
  1087. _wBorderWidth = _PF._wBorderWidth;
  1088. _xCellPrev = _xRowOffset;
  1089. }
  1090. CELLPARMS *pCellParms = (CELLPARMS *)&_rgxCell[0];
  1091. // Store cell widths instead of right boundaries relative to \trleftN
  1092. LONG i = iParam - _xCellPrev;
  1093. i = max(i, 0);
  1094. i = min(i, 1440*22);
  1095. pCellParms[_cCell].uCell = i + (_bCellFlags << 24);
  1096. pCellParms[_cCell].dxBrdrWidths = _dwCellBrdrWdths;
  1097. pCellParms[_cCell].dwColors = _dwCellColors;
  1098. pCellParms[_cCell].bShading = (BYTE)min(200, _dwShading);
  1099. _dwCellColors = 0; // Default autocolor for next cell
  1100. _dwShading = 0;
  1101. _xCellPrev = iParam;
  1102. _cCell++;
  1103. _dwCellBrdrWdths = 0;
  1104. _bCellFlags = 0;
  1105. }
  1106. }
  1107. /*
  1108. * CRTFRead::HandleChar(ch)
  1109. *
  1110. * @mfunc
  1111. * Handle single Unicode character <p ch>
  1112. *
  1113. * @rdesc
  1114. * EC The error code
  1115. */
  1116. EC CRTFRead::HandleChar(
  1117. WCHAR ch) //@parm char token to be handled
  1118. {
  1119. TRACEBEGIN(TRCSUBSYSRTFR, TRCSCOPEINTERN, "CRTFRead::HandleChar");
  1120. if(!_ped->_pdp->IsMultiLine() && IsASCIIEOP(ch))
  1121. _ecParseError = ecTruncateAtCRLF;
  1122. else
  1123. {
  1124. AssertNr(ch <= 0x7F || ch > 0xFF || FTokIsSymbol(ch));
  1125. _dwMaskCF2 |= CFM2_RUNISDBCS;
  1126. _CF._dwEffects &= ~CFE_RUNISDBCS;
  1127. AddText(&ch, 1, CharGetsNumbering(ch));
  1128. }
  1129. TRACEERRSZSC("HandleChar()", - _ecParseError);
  1130. return _ecParseError;
  1131. }
  1132. /*
  1133. * CRTFRead::HandleEndOfPara()
  1134. *
  1135. * @mfunc
  1136. * Insert EOP and apply current paraformat
  1137. *
  1138. * @rdesc
  1139. * EC the error code
  1140. */
  1141. EC CRTFRead::HandleEndOfPara()
  1142. {
  1143. TRACEBEGIN(TRCSUBSYSRTFR, TRCSCOPEINTERN, "CRTFRead::HandleEndOfPara");
  1144. if(!_ped->_pdp->IsMultiLine()) // No EOPs permitted in single-
  1145. { // line controls
  1146. Apply_PF(); // Apply any paragraph formatting
  1147. _ecParseError = ecTruncateAtCRLF; // Cause RTF reader to finish up
  1148. return ecTruncateAtCRLF;
  1149. }
  1150. Apply_CF(); // Apply _CF and save iCF, since
  1151. LONG iFormat = _prg->Get_iCF(); // it gets changed if processing
  1152. // CFE2_RUNISDBCS chars
  1153. EC ec;
  1154. if(IN_RANGE(tokenCell, _token, tokenNestCell) || _token == tokenRow)
  1155. ec = HandleChar((unsigned)CELL);
  1156. else
  1157. ec = _ped->fUseCRLF() // If RichEdit 1.0 compatibility
  1158. ? HandleText(szaCRLF, ALL_ASCII) // mode, use CRLF; else CR or VT
  1159. : HandleChar((unsigned)(_token == tokenLineBreak ? VT :
  1160. _token == tokenPage ? FF : CR));
  1161. if(ec == ecNoError)
  1162. {
  1163. Apply_PF();
  1164. _cpThisPara = _prg->GetCp(); // New para starts after CRLF
  1165. }
  1166. _prg->Set_iCF(iFormat); // Restore iFormat if changed
  1167. ReleaseFormats(iFormat, -1); // Release iFormat (AddRef'd by
  1168. // Get_iCF())
  1169. return _ecParseError;
  1170. }
  1171. /*
  1172. * CRTFRead::HandleText(szText, iAllASCII)
  1173. *
  1174. * @mfunc
  1175. * Handle the string of Ansi characters <p szText>
  1176. *
  1177. * @rdesc
  1178. * EC The error code
  1179. */
  1180. EC CRTFRead::HandleText(
  1181. BYTE * szText, //@parm string to be handled
  1182. int iAllASCII, //@parm enum indicating if string is all ASCII chars
  1183. LONG cchText) //@parm size of szText in bytes
  1184. {
  1185. TRACEBEGIN(TRCSUBSYSRTFR, TRCSCOPEINTERN, "CRTFRead::HandleText");
  1186. LONG cch;
  1187. BOOL fStateChng = FALSE;
  1188. WCHAR * pch;
  1189. STATE * pstate = _pstateStackTop;
  1190. TEXTFONT * ptf = pstate->ptf;
  1191. struct TEXTFONTSAVE : TEXTFONT
  1192. {
  1193. TEXTFONTSAVE(TEXTFONT *ptf)
  1194. {
  1195. if (ptf)
  1196. {
  1197. iCharRep = ptf->iCharRep;
  1198. sCodePage = ptf->sCodePage;
  1199. fCpgFromSystem = ptf->fCpgFromSystem;
  1200. }
  1201. }
  1202. };
  1203. BOOL fAdjustPtf = FALSE;
  1204. if(IN_RANGE(DEFFONT_LTRCH, pstate->iDefFont, DEFFONT_RTLCH))
  1205. {
  1206. // CharSet resolution based on directional control words
  1207. BOOL frtlch = pstate->iDefFont == DEFFONT_RTLCH;
  1208. if(_CF._iCharRep == DEFAULT_INDEX)
  1209. {
  1210. _CF._iCharRep = (BYTE)(frtlch ? _iCharRepBiDi : ANSI_INDEX);
  1211. _dwMaskCF |= CFM_CHARSET;
  1212. fAdjustPtf = TRUE;
  1213. }
  1214. else
  1215. {
  1216. BOOL fBiDiCharRep = IsRTLCharRep(_CF._iCharRep);
  1217. // If direction token doesn't correspond to current charset
  1218. if(fBiDiCharRep ^ frtlch)
  1219. {
  1220. _dwMaskCF |= CFM_CHARSET;
  1221. fAdjustPtf = TRUE;
  1222. if(!fBiDiCharRep) // Wasn't BiDi, but is now
  1223. SelectCurrentFont(_sDefaultBiDiFont);
  1224. _CF._iCharRep = (BYTE)(frtlch ? _iCharRepBiDi : ANSI_INDEX);
  1225. }
  1226. else if (fBiDiCharRep && ptf && !W32->IsBiDiCodePage(ptf->sCodePage))
  1227. fAdjustPtf = TRUE;
  1228. }
  1229. }
  1230. else if(_ped->IsBiDi() && _CF._iCharRep == DEFAULT_INDEX)
  1231. {
  1232. _CF._iCharRep = ANSI_INDEX;
  1233. _dwMaskCF |= CFM_CHARSET;
  1234. fAdjustPtf = TRUE;
  1235. }
  1236. if (fAdjustPtf && ptf)
  1237. {
  1238. ptf->sCodePage = (SHORT)CodePageFromCharRep(_CF._iCharRep);
  1239. pstate->SetCodePage(ptf->sCodePage);
  1240. }
  1241. TEXTFONTSAVE tfSave(ptf);
  1242. // TODO: what if szText cuts off in middle of DBCS?
  1243. if(!*szText)
  1244. goto CleanUp;
  1245. if (cchText != -1 && _cchUnicode < cchText)
  1246. {
  1247. // Re-allocate a bigger buffer
  1248. _szUnicode = (WCHAR *)PvReAlloc(_szUnicode, (cchText + 1) * sizeof(WCHAR));
  1249. if(!_szUnicode) // Re-allocate space for Unicode conversions
  1250. {
  1251. _ped->GetCallMgr()->SetOutOfMemory();
  1252. _ecParseError = ecNoMemory;
  1253. goto CleanUp;
  1254. }
  1255. _cchUnicode = cchText + 1;
  1256. }
  1257. if(iAllASCII == ALL_ASCII || pstate->nCodePage == CP_SYMBOL)
  1258. {
  1259. // Don't use MBTWC() in cases where text contains
  1260. // only ASCII chars (which don't require conversion)
  1261. for(cch = 0, pch = _szUnicode; *szText; cch++)
  1262. {
  1263. Assert(*szText <= 0x7F || pstate->nCodePage == CP_SYMBOL);
  1264. *pch++ = (WCHAR)*szText++;
  1265. }
  1266. *pch = 0;
  1267. _dwMaskCF2 |= CFM2_RUNISDBCS;
  1268. _CF._dwEffects &= ~CFE_RUNISDBCS;
  1269. // Fall through to AddText at end of HandleText()
  1270. }
  1271. else
  1272. {
  1273. BOOL fMissingCodePage;
  1274. // Run of text contains bytes > 0x7F.
  1275. // Ensure that we have the correct codepage with which to interpret
  1276. // these (possibly DBCS) bytes.
  1277. if(ptf && ptf->sCodePage == INVALID_CODEPAGE && !ptf->fCpgFromSystem)
  1278. {
  1279. if(_dwFlags & SF_USECODEPAGE)
  1280. {
  1281. _CF._iCharRep = CharRepFromCodePage(_nCodePage);
  1282. _dwMaskCF |= CFM_CHARSET;
  1283. }
  1284. // Determine codepage from the font name
  1285. else if(CpgInfoFromFaceName(pstate->ptf))
  1286. {
  1287. fStateChng = TRUE;
  1288. SelectCurrentFont(pstate->ptf->sHandle);
  1289. Assert(ptf->sCodePage != INVALID_CODEPAGE && ptf->fCpgFromSystem);
  1290. }
  1291. else
  1292. {
  1293. // Here, we were not able to determine a cpg/charset value
  1294. // from the font name. We have two choices: (1) either choose
  1295. // some fallback value like 1252/0 or (2) rely on the
  1296. // document-level cpg value.
  1297. //
  1298. // I think choosing the document-level cpg value will give
  1299. // us the best results. In FE cases, it will likely err
  1300. // on the side of tagging too many runs as CFE2_RUNISDBCS, but
  1301. // that is safer than using a western cpg and potentially missing
  1302. // runs which should be CFE2_RUNISDBCS.
  1303. }
  1304. }
  1305. Assert(!IsUTF8 || pstate->nCodePage == CP_UTF8);
  1306. if (pstate->nCodePage == INVALID_CODEPAGE && ptf)
  1307. pstate->nCodePage = ptf->sCodePage;
  1308. cch = MBTWC(pstate->nCodePage, 0,
  1309. (char *)szText, -1,
  1310. _szUnicode, _cchUnicode, &fMissingCodePage);
  1311. AssertSz(cch > 0, "CRTFRead::HandleText(): MBTWC implementation changed"
  1312. " such that it returned a value <= 0");
  1313. if(!fMissingCodePage || !W32->IsFECodePage(pstate->nCodePage))
  1314. {
  1315. // Use result of MBTWC if:
  1316. // (1) we converted some chars and did the conversion with the codepage
  1317. // provided.
  1318. // (2) we converted some chars but couldn't use the codepage provided,
  1319. // but the codepage is invalid. Since the codepage is invalid,
  1320. // we can't do anything more sophisticated with the text before
  1321. // adding to the backing store
  1322. cch--; // don't want char count to including terminating NULL
  1323. _dwMaskCF2 |= CFM2_RUNISDBCS;
  1324. _CF._dwEffects &= ~CFE_RUNISDBCS;
  1325. if(pstate->nCodePage == INVALID_CODEPAGE)
  1326. _CF._dwEffects |= CFE_RUNISDBCS;
  1327. // fall through to AddText at end of HandleText()
  1328. }
  1329. else
  1330. {
  1331. // Conversion to Unicode failed. Break up the string of
  1332. // text into runs of ASCII and non-ASCII characters.
  1333. // FUTURE(BradO): Here, I am saving dwMask and restoring it before
  1334. // each AddText. I'm not sure this is neccessary. When I have
  1335. // the time, I should revisit this save/restoring and
  1336. // determine that it is indeed neccessary.
  1337. BOOL fPrevIsASCII = ((*szText <= 0x7F) ? TRUE : FALSE);
  1338. BOOL fCurrentIsASCII = FALSE;
  1339. BOOL fLastChunk = FALSE;
  1340. DWORD dwMaskSave = _dwMaskCF;
  1341. #if defined(DEBUG) || defined(_RELEASE_ASSERTS_)
  1342. CCharFormat CFSave = _CF;
  1343. #endif
  1344. pch = _szUnicode;
  1345. cch = 0;
  1346. // (!*szText && *pch) is the case where we do the AddText for the
  1347. // last chunk of text
  1348. while(*szText || fLastChunk)
  1349. {
  1350. // fCurrentIsASCII assumes that no byte <= 0x7F is a
  1351. // DBCS lead-byte
  1352. if(fLastChunk ||
  1353. (fPrevIsASCII != (fCurrentIsASCII = (*szText <= 0x7F))))
  1354. {
  1355. _dwMaskCF = dwMaskSave;
  1356. #if defined(DEBUG) || defined(_RELEASE_ASSERTS_)
  1357. _CF = CFSave;
  1358. #endif
  1359. *pch = 0;
  1360. _dwMaskCF2 |= CFM2_RUNISDBCS;
  1361. _CF._dwEffects |= CFE_RUNISDBCS;
  1362. if(fPrevIsASCII)
  1363. _CF._dwEffects &= ~CFE_RUNISDBCS;
  1364. Assert(cch);
  1365. pch = _szUnicode;
  1366. AddText(pch, cch, TRUE);
  1367. cch = 0;
  1368. fPrevIsASCII = fCurrentIsASCII;
  1369. // My assumption in saving _dwMaskCF is that the remainder
  1370. // of the _CF is unchanged by AddText. This assert verifies
  1371. // this assumption.
  1372. AssertSz(!CompareMemory(&CFSave._iCharRep, &_CF._iCharRep,
  1373. sizeof(CCharFormat) - sizeof(DWORD)) &&
  1374. !((CFSave._dwEffects ^ _CF._dwEffects) & ~CFE_RUNISDBCS),
  1375. "CRTFRead::HandleText(): AddText has been changed "
  1376. "and now alters the _CF structure.");
  1377. if(fLastChunk) // Last chunk of text was AddText'd
  1378. break;
  1379. }
  1380. // Not the last chunk of text.
  1381. Assert(*szText);
  1382. // Advance szText pointer
  1383. if (!fCurrentIsASCII && *(szText + 1) &&
  1384. GetTrailBytesCount(*szText, pstate->nCodePage))
  1385. {
  1386. // Current byte is a lead-byte of a DBCS character
  1387. *pch++ = *szText++;
  1388. ++cch;
  1389. }
  1390. *pch++ = *szText++;
  1391. ++cch;
  1392. // Must do an AddText for the last chunk of text
  1393. if(!*szText || cch >= _cchUnicode - 1)
  1394. fLastChunk = TRUE;
  1395. }
  1396. goto CleanUp;
  1397. }
  1398. }
  1399. if(cch > 0)
  1400. {
  1401. if(!_cCell || _iCell < _cCell)
  1402. AddText(_szUnicode, cch, TRUE);
  1403. if(fStateChng && ptf)
  1404. {
  1405. ptf->iCharRep = tfSave.iCharRep;
  1406. ptf->sCodePage = tfSave.sCodePage;
  1407. ptf->fCpgFromSystem = tfSave.fCpgFromSystem;
  1408. SelectCurrentFont(ptf->sHandle);
  1409. }
  1410. }
  1411. CleanUp:
  1412. TRACEERRSZSC("HandleText()", - _ecParseError);
  1413. return _ecParseError;
  1414. }
  1415. /*
  1416. * CRTFRead::HandleUN(pstate)
  1417. *
  1418. * @mfunc
  1419. * Handle run of Unicode characters given by \uN control words
  1420. */
  1421. void CRTFRead::HandleUN(
  1422. STATE *pstate)
  1423. {
  1424. char ach = 0;
  1425. LONG cch = 0;
  1426. DWORD ch; // Treat as unsigned quantity
  1427. WCHAR * pch = _szUnicode;
  1428. _dwMaskCF2 |= CFM2_RUNISDBCS;
  1429. _CF._dwEffects &= ~CFE_RUNISDBCS;
  1430. do
  1431. {
  1432. if(_iParam < 0)
  1433. _iParam = (WORD)_iParam;
  1434. ch = _iParam;
  1435. if(pstate->ptf && pstate->ptf->iCharRep == SYMBOL_INDEX)
  1436. {
  1437. if(IN_RANGE(0xF000, ch, 0xF0FF))// Compensate for converters
  1438. ch -= 0xF000; // that write symbol codes
  1439. // up high
  1440. else if(ch > 255) // Whoops, RTF is using con-
  1441. { // verted value for symbol:
  1442. char ach; // convert back
  1443. WCHAR wch = ch; // Avoid endian problems
  1444. WCTMB(1252, 0, &wch, 1, &ach, 1, NULL, NULL, NULL);
  1445. ch = (BYTE)ach; // Review: use CP_ACP??
  1446. }
  1447. }
  1448. if(IN_RANGE(0x10000, ch, 0x10FFFF)) // Higher-plane char:
  1449. { // Store as Unicode surrogate
  1450. ch -= 0x10000; // pair
  1451. *pch++ = 0xD800 + (ch >> 10);
  1452. ch = 0xDC00 + (ch & 0x3FF);
  1453. cch++;
  1454. }
  1455. if(!IN_RANGE(STARTFIELD, ch, NOTACHAR) && // Don't insert 0xFFF9 - 0xFFFF
  1456. (!IN_RANGE(0xDC00, ch, 0xDFFF) || // or lone trail surrogate
  1457. IN_RANGE(0xD800, cch ? *(pch - 1) : _prg->GetPrevChar(), 0xDBFF)))
  1458. {
  1459. *pch++ = ch;
  1460. cch++;
  1461. }
  1462. for(LONG i = pstate->cbSkipForUnicodeMax; i--; )
  1463. GetCharEx(); // Eat the \uc N chars
  1464. ach = GetChar(); // Get next char
  1465. while(IsASCIIEOP(ach))
  1466. {
  1467. ach = GetChar();
  1468. if (_ecParseError != ecNoError)
  1469. return;
  1470. }
  1471. if(ach != BSLASH) // Not followed by \, so
  1472. { // not by \uN either
  1473. UngetChar(); // Put back BSLASH
  1474. break;
  1475. }
  1476. ach = GetChar();
  1477. if((ach | ' ') != 'u')
  1478. {
  1479. UngetChar(2); // Not \u; put back \x
  1480. break;
  1481. }
  1482. GetParam(GetChar()); // \u so try for _iParam = N
  1483. if(!_fParam) // No \uN; put back \u
  1484. {
  1485. UngetChar(2);
  1486. break;
  1487. }
  1488. } while(cch < _cchUnicode - 2 && _iParam);
  1489. AddText(_szUnicode, cch, TRUE, TRUE);
  1490. }
  1491. /*
  1492. * CRTFRead::HandleNumber()
  1493. *
  1494. * @mfunc
  1495. * Insert the number _iParam as text. Useful as a workaround for
  1496. * Word RTF that leaves out the blank between \ltrch and a number.
  1497. *
  1498. * @rdesc
  1499. * EC The error code
  1500. */
  1501. EC CRTFRead::HandleNumber()
  1502. {
  1503. if(!_fParam) // Nothing to do
  1504. return _ecParseError;
  1505. LONG n = _iParam;
  1506. BYTE *pch = _szText;
  1507. if(n < 0)
  1508. {
  1509. n = -n;
  1510. *pch++ = '-';
  1511. }
  1512. for(LONG d = 1; d < n; d *= 10) // d = smallest power of 10 > n
  1513. ;
  1514. if(d > n)
  1515. d /= 10; // d = largest power of 10 < n
  1516. while(d)
  1517. {
  1518. *pch++ = (WORD)(n/d + '0'); // Store digit
  1519. n = n % d; // Setup remainder
  1520. d /= 10;
  1521. }
  1522. *pch = 0;
  1523. _token = tokenASCIIText;
  1524. HandleTextToken(_pstateStackTop);
  1525. return _ecParseError;
  1526. }
  1527. /*
  1528. * CRTFRead::HandleTextToken(pstate)
  1529. *
  1530. * @mfunc
  1531. * Handle tokenText
  1532. *
  1533. * @rdesc
  1534. * EC The error code
  1535. */
  1536. EC CRTFRead::HandleTextToken(
  1537. STATE *pstate)
  1538. {
  1539. COLORREF *pclrf;
  1540. switch (pstate->sDest)
  1541. {
  1542. case destColorTable:
  1543. pclrf = _colors.Add(1, NULL);
  1544. if(!pclrf)
  1545. goto OutOfRAM;
  1546. *pclrf = _fGetColorYet ?
  1547. RGB(pstate->bRed, pstate->bGreen, pstate->bBlue) : tomAutoColor;
  1548. // Prepare for next color table entry
  1549. pstate->bRed =
  1550. pstate->bGreen =
  1551. pstate->bBlue = 0;
  1552. _fGetColorYet = FALSE; // in case more "empty" color
  1553. break;
  1554. case destFontTable:
  1555. if(!pstate->fRealFontName)
  1556. {
  1557. ReadFontName(pstate, _token == tokenASCIIText
  1558. ? ALL_ASCII : CONTAINS_NONASCII);
  1559. }
  1560. break;
  1561. case destRealFontName:
  1562. {
  1563. STATE * const pstatePrev = pstate->pstatePrev;
  1564. if(pstatePrev && pstatePrev->sDest == destFontTable)
  1565. {
  1566. // Mark previous state so that tagged font name will be ignored
  1567. // AROO: Do this before calling ReadFontName so that
  1568. // AROO: it doesn't try to match font name
  1569. pstatePrev->fRealFontName = TRUE;
  1570. ReadFontName(pstatePrev,
  1571. _token == tokenASCIIText ? ALL_ASCII : CONTAINS_NONASCII);
  1572. }
  1573. break;
  1574. }
  1575. case destFieldInstruction:
  1576. HandleFieldInstruction();
  1577. break;
  1578. case destObjectClass:
  1579. if(StrAlloc(&_prtfObject->szClass, _szText))
  1580. goto OutOfRAM;
  1581. break;
  1582. case destObjectName:
  1583. if(StrAlloc(&_prtfObject->szName, _szText))
  1584. goto OutOfRAM;
  1585. break;
  1586. case destStyleSheet:
  1587. // _szText has style name, e.g., "heading 1"
  1588. if(W32->ASCIICompareI(_szText, (unsigned char *)"heading", 7))
  1589. {
  1590. DWORD dwT = (unsigned)(_szText[8] - '0');
  1591. if(dwT < NSTYLES)
  1592. _rgStyles[dwT] = (BYTE)_Style;
  1593. }
  1594. break;
  1595. case destShapeName:
  1596. if(pstate->fBackground)
  1597. {
  1598. TOKEN token = TokenFindKeyword(_szText, rgShapeKeyword, cShapeKeywords);
  1599. _bShapeNameIndex = (token != tokenUnknownKeyword) ? (BYTE)token : 0;
  1600. }
  1601. break;
  1602. case destShapeValue:
  1603. if(_bShapeNameIndex)
  1604. {
  1605. CDocInfo *pDocInfo = _ped->GetDocInfoNC();
  1606. BYTE *pch = _szText;
  1607. BOOL fNegative = FALSE;
  1608. if(*pch == '-')
  1609. {
  1610. fNegative = TRUE;
  1611. pch++;
  1612. }
  1613. for(_iParam = 0; IsDigit(*pch); pch++)
  1614. _iParam = _iParam*10 + *pch - '0';
  1615. if(fNegative)
  1616. _iParam = -_iParam;
  1617. switch(_bShapeNameIndex)
  1618. {
  1619. case shapeFillBackColor:
  1620. case shapeFillColor:
  1621. {
  1622. if(_iParam > 0xFFFFFF)
  1623. _iParam = 0;
  1624. if(_bShapeNameIndex == shapeFillColor)
  1625. pDocInfo->_crColor = (COLORREF)_iParam;
  1626. else
  1627. pDocInfo->_crBackColor = (COLORREF)_iParam;
  1628. if(pDocInfo->_nFillType == -1)
  1629. pDocInfo->_nFillType = 0;
  1630. break;
  1631. }
  1632. case shapeFillType:
  1633. // DEBUGGG: more general _nFillType commented out for now
  1634. // pDocInfo->_nFillType = _iParam;
  1635. if(pDocInfo->_nFillType)
  1636. CheckNotifyLowFiRTF(TRUE);
  1637. pDocInfo->_crColor = RGB(255, 255, 255);
  1638. pDocInfo->_crBackColor = RGB(255, 255, 255);
  1639. break;
  1640. case shapeFillAngle:
  1641. pDocInfo->_sFillAngle = HIWORD(_iParam);
  1642. break;
  1643. case shapeFillFocus:
  1644. pDocInfo->_bFillFocus = _iParam;
  1645. break;
  1646. }
  1647. }
  1648. break;
  1649. case destDocumentArea:
  1650. case destFollowingPunct:
  1651. case destLeadingPunct:
  1652. break;
  1653. // This has been changed now. We will store the Punct strings as
  1654. // raw text strings. So, we don't have to convert them.
  1655. // This code is keep here just in case we want to change back.
  1656. #if 0
  1657. case destDocumentArea:
  1658. if (_tokenLast != tokenFollowingPunct &&
  1659. _tokenLast != tokenLeadingPunct)
  1660. {
  1661. break;
  1662. } // Else fall thru to
  1663. // destFollowingPunct
  1664. case destFollowingPunct:
  1665. case destLeadingPunct:
  1666. // TODO(BradO): Consider some kind of merging heuristic when
  1667. // we paste FE RTF (for lead and follow chars, that is).
  1668. if(!(_dwFlags & SFF_SELECTION))
  1669. {
  1670. int cwch = MBTWC(INVALID_CODEPAGE, 0,
  1671. (char *)_szText, -1,
  1672. NULL, 0,
  1673. NULL);
  1674. Assert(cwch);
  1675. WCHAR *pwchBuf = (WCHAR *)PvAlloc(cwch * sizeof(WCHAR), GMEM_ZEROINIT);
  1676. if(!pwchBuf)
  1677. goto OutOfRAM;
  1678. SideAssert(MBTWC(INVALID_CODEPAGE, 0,
  1679. (char *)_szText, -1,
  1680. pwchBuf, cwch,
  1681. NULL) > 0);
  1682. if(pstate->sDest == destFollowingPunct)
  1683. _ped->SetFollowingPunct(pwchBuf);
  1684. else
  1685. {
  1686. Assert(pstate->sDest == destLeadingPunct);
  1687. _ped->SetLeadingPunct(pwchBuf);
  1688. }
  1689. FreePv(pwchBuf);
  1690. }
  1691. break;
  1692. #endif
  1693. default:
  1694. if(!IsLowMergedCell())
  1695. HandleText(_szText, _token == tokenASCIIText ? ALL_ASCII : CONTAINS_NONASCII);
  1696. }
  1697. return _ecParseError;
  1698. OutOfRAM:
  1699. _ped->GetCallMgr()->SetOutOfMemory();
  1700. _ecParseError = ecNoMemory;
  1701. return _ecParseError;
  1702. }
  1703. /*
  1704. * CRTFRead::AddText(pch, cch, fNumber, fUN)
  1705. *
  1706. * @mfunc
  1707. * Add <p cch> chars of the string <p pch> to the range _prg
  1708. *
  1709. * @rdesc
  1710. * error code placed in _ecParseError
  1711. */
  1712. EC CRTFRead::AddText(
  1713. WCHAR * pch, //@parm Text to add
  1714. LONG cch, //@parm Count of chars to add
  1715. BOOL fNumber, //@parm Indicates whether or not to prepend numbering
  1716. BOOL fUN) //@parm TRUE means caller is \uN handler
  1717. {
  1718. TRACEBEGIN(TRCSUBSYSRTFR, TRCSCOPEINTERN, "CRTFRead::AddText");
  1719. LONG cchAdded;
  1720. LONG cchT;
  1721. STATE * const pstate = _pstateStackTop;
  1722. WCHAR * szDest;
  1723. LONG cchMove;
  1724. // AROO: No saving state before this point (other than pstate)
  1725. // AROO: This would screw recursion below
  1726. //AssertSz(pstate, "CRTFRead::AddText: no state");
  1727. if((DWORD)cch > _cchMax)
  1728. {
  1729. cch = (LONG)_cchMax;
  1730. _ecParseError = ecTextMax;
  1731. if(*pch == STARTFIELD)
  1732. return ecTextMax; // Don't enter partial startfield
  1733. }
  1734. if(!cch)
  1735. return _ecParseError;
  1736. if(_fStartRow)
  1737. DelimitRow(szRowStart);
  1738. // FUTURE(BradO): This strategy for \pntext is prone to bugs, I believe.
  1739. // The recursive call to AddText to add the \pntext will trounce the
  1740. // accumulated _CF diffs associated with the text for which AddText is
  1741. // called. I believe we should save and restore _CF before and after
  1742. // the recursive call to AddText below. Also, it isn't sufficient to
  1743. // accumulate bits of \pntext as below, since each bit might be formatted
  1744. // with different _CF properties. Instead, we should accumulate a mini-doc
  1745. // complete with multiple text, char and para runs (or some stripped down
  1746. // version of this strategy).
  1747. if(pstate && pstate->sDest == destParaNumText && pch != szRowStart)
  1748. {
  1749. szDest = _szNumText + _cchUsedNumText;
  1750. cch = min(cch, cchMaxNumText - 1 - _cchUsedNumText);
  1751. if(cch > 0)
  1752. {
  1753. MoveMemory((BYTE *)szDest, (BYTE *)pch, cch*2);
  1754. szDest[cch] = TEXT('\0'); // HandleText() takes sz
  1755. _cchUsedNumText += cch;
  1756. }
  1757. return ecNoError;
  1758. }
  1759. if(pstate && _cchUsedNumText && fNumber) // Some \pntext available
  1760. {
  1761. // Bug 3496 - The fNumber flag is an ugly hack to work around RTF
  1762. // commonly written by Word. Often, to separate a numbered list
  1763. // by page breaks, Word will write:
  1764. // <NUMBERING INFO> \page <PARAGRAPH TEXT>
  1765. // The paragraph numbering should precede the paragraph text rather
  1766. // than the page break. The fNumber flag is set to FALSE when the
  1767. // the text being added should not be prepended with the para-numbering,
  1768. // as is the case with \page (mapped to FF).
  1769. cchT = _cchUsedNumText;
  1770. _cchUsedNumText = 0; // Prevent infinite recursion
  1771. if(!pstate->fBullet)
  1772. {
  1773. // If there are any _CF diffs to be injected, they will be trounced
  1774. // by this recursive call (see FUTURE comment above).
  1775. // Since we didn't save _CF data from calls to AddText with
  1776. // pstate->sDest == destParaNumText, we have no way of setting up
  1777. // CFE2_RUNISDBCS and CFM2_RUNISDBCS (see FUTURE comment above).
  1778. AddText(_szNumText, cchT, FALSE);
  1779. }
  1780. else if(_PF.IsListNumbered() && _szNumText[cchT - 1] == TAB)
  1781. {
  1782. AssertSz(cchT >= 1, "Invalid numbered text count");
  1783. if (cchT > 1)
  1784. {
  1785. WCHAR ch = _szNumText[cchT - 2];
  1786. _wNumberingStyle = (_wNumberingStyle & ~0x300)
  1787. | (ch == '.' ? PFNS_PERIOD :
  1788. ch != ')' ? PFNS_PLAIN :
  1789. _szNumText[0] == '(' ? PFNS_PARENS : PFNS_PAREN);
  1790. }
  1791. else
  1792. {
  1793. // There is only a tab so we will assume they meant to
  1794. // skip numbering.
  1795. _wNumberingStyle = PFNS_NONUMBER;
  1796. }
  1797. }
  1798. }
  1799. Apply_CF(); // Apply formatting changes in _CF
  1800. // CTxtRange::ReplaceRange will change the character formatting
  1801. // and possibly adjust the _rpCF forward if the current char
  1802. // formatting includes protection. The changes affected by
  1803. // CTxtRange::ReplaceRange are necessary only for non-streaming
  1804. // input, so we save state before and restore it after the call
  1805. // to CTxtRange::ReplaceRange
  1806. LONG iFormatSave = _prg->Get_iCF(); // Save state
  1807. QWORD qwFlags = GetCharFlags(pch, cch);
  1808. if(fUN && // \uN generated string
  1809. (!pstate->ptf || pstate->ptf->sCodePage == INVALID_CODEPAGE || qwFlags & FOTHER ||
  1810. (qwFlags & GetFontSignatureFromFace(_ped->GetCharFormat(iFormatSave)->_iFont)) != qwFlags &&
  1811. (!(qwFlags & (FARABIC | FHEBREW)) || _fNon0CharSet)))
  1812. {
  1813. // No charset info for \uN or current charset doesn't support char
  1814. cchAdded = _prg->CleanseAndReplaceRange(cch, pch, FALSE, NULL, pch);
  1815. }
  1816. else
  1817. {
  1818. cchAdded = _prg->ReplaceRange(cch, pch, NULL, SELRR_IGNORE, &cchMove,
  1819. RR_NO_LP_CHECK);
  1820. for(cchT = cch - 1; cchT; cchT--)
  1821. qwFlags |= GetCharFlags(++pch, cchT);// Note if ComplexScript
  1822. _ped->OrCharFlags(qwFlags);
  1823. }
  1824. _fBody = TRUE;
  1825. _prg->Set_iCF(iFormatSave); // Restore state
  1826. ReleaseFormats(iFormatSave, -1);
  1827. Assert(!_prg->GetCch());
  1828. if(cchAdded != cch)
  1829. {
  1830. Tracef(TRCSEVERR, "AddText(): Only added %d out of %d", cchAdded, cch);
  1831. _ecParseError = ecGeneralFailure;
  1832. if(cchAdded <= 0)
  1833. return _ecParseError;
  1834. }
  1835. _cchMax -= cchAdded;
  1836. return _ecParseError;
  1837. }
  1838. /*
  1839. * CRTFRead::Apply_CF()
  1840. *
  1841. * @mfunc
  1842. * Apply character formatting changes collected in _CF
  1843. */
  1844. void CRTFRead::Apply_CF()
  1845. {
  1846. // If any CF changes, update range's _iFormat
  1847. if(_dwMaskCF || _dwMaskCF2)
  1848. {
  1849. AssertSz(_prg->GetCch() == 0,
  1850. "CRTFRead::Apply_CF: nondegenerate range");
  1851. _prg->SetCharFormat(&_CF, 0, NULL, _dwMaskCF, _dwMaskCF2);
  1852. _dwMaskCF = 0;
  1853. _dwMaskCF2 = 0;
  1854. }
  1855. }
  1856. /*
  1857. * CRTFRead::Apply_PF()
  1858. *
  1859. * @mfunc
  1860. * Apply paragraph format given by _PF
  1861. *
  1862. * @rdesc
  1863. * if table row delimiter with nonzero cell count, PF::_iTabs; else -1
  1864. */
  1865. SHORT CRTFRead::Apply_PF()
  1866. {
  1867. LONG cp = _prg->GetCp();
  1868. DWORD dwMask = _dwMaskPF;
  1869. DWORD dwMask2 = _dwMaskPF2;
  1870. SHORT iTabs = -1;
  1871. CParaFormat *pPF = &_PF;
  1872. if(_pstateStackTop)
  1873. {
  1874. Assert(_pstateStackTop->pPF);
  1875. // Add PF diffs to *_pstateStackTop->pPF
  1876. if(!_pstateStackTop->AddPF(_PF, _bDocType, _dwMaskPF, _dwMaskPF2))
  1877. {
  1878. _ped->GetCallMgr()->SetOutOfMemory();
  1879. _ecParseError = ecNoMemory;
  1880. return -1;
  1881. }
  1882. _dwMaskPF = _dwMaskPF2 = 0; // _PF contains delta's from *_pstateStackTop->pPF
  1883. pPF = _pstateStackTop->pPF;
  1884. dwMask = _pstateStackTop->dwMaskPF;
  1885. Assert(dwMask == PFM_ALLRTF);
  1886. if(pPF->_wNumbering)
  1887. {
  1888. pPF->_wNumberingTab = _pstateStackTop->sIndentNumbering;
  1889. pPF->_wNumberingStyle = _wNumberingStyle;
  1890. }
  1891. if(_bTableLevelIP + _bTableLevel)
  1892. {
  1893. pPF->_wEffects |= PFE_TABLE;
  1894. dwMask |= PFM_TABLE;
  1895. pPF->_bTableLevel = min(_bTableLevel + _bTableLevelIP, MAXTABLENEST);
  1896. }
  1897. }
  1898. if(dwMask & PFM_TABSTOPS)
  1899. {
  1900. LONG cTab = _cTab;
  1901. BOOL fIsTableRowDelimiter = pPF->IsTableRowDelimiter();
  1902. const LONG *prgxTabs = &_rgxCell[0];
  1903. if(fIsTableRowDelimiter)
  1904. {
  1905. dwMask2 = PFM2_ALLOWTRDCHANGE;
  1906. if(!_cCell)
  1907. {
  1908. if(_iTabsTable >= 0) // No cells defined here;
  1909. { // Use previous table defs
  1910. Assert(_bTableLevel == 1 && !_fNo_iTabsTable);
  1911. CTabs *pTabs = GetTabsCache()->Elem(_iTabsTable);
  1912. _cCell = pTabs->_cTab/(CELL_EXTRA + 1);
  1913. prgxTabs = pTabs->_prgxTabs;
  1914. }
  1915. else if(_prg->_rpTX.IsAfterTRD(ENDFIELD) && _iCell)
  1916. {
  1917. LONG x = 2000; // Bad RTF: no \cellx's. Def 'em
  1918. for(LONG i = 1; i <= _iCell; i++)
  1919. {
  1920. HandleCellx(x);
  1921. x += 2000;
  1922. }
  1923. }
  1924. }
  1925. cTab = _cCell;
  1926. }
  1927. // Caching a tabs array AddRefs the corresponding cached tabs entry.
  1928. // Be absolutely sure to release the entry before exiting the routine
  1929. // that caches it (see GetTabsCache()->Release at end of this function).
  1930. pPF->_bTabCount = cTab;
  1931. if(fIsTableRowDelimiter)
  1932. cTab *= CELL_EXTRA + 1;
  1933. pPF->_iTabs = GetTabsCache()->Cache(prgxTabs, cTab);
  1934. if(fIsTableRowDelimiter && _bTableLevel == 1)
  1935. {
  1936. iTabs = pPF->_iTabs;
  1937. if(!_fNo_iTabsTable)
  1938. _iTabsTable = pPF->_iTabs;
  1939. }
  1940. AssertSz(!cTab || pPF->_iTabs >= 0,
  1941. "CRTFRead::Apply_PF: illegal pPF->_iTabs");
  1942. }
  1943. LONG fCell = (_prg->GetPrevChar() == CELL);
  1944. LONG fIsAfterTRD = _prg->_rpTX.IsAfterTRD(0);
  1945. if(fCell || fIsAfterTRD) // Deal with table delimiters
  1946. { // in hidden text and with
  1947. _prg->_rpCF.AdjustBackward(); // custom colors
  1948. if(_prg->IsHidden())
  1949. { // Turn off hidden text
  1950. CCharFormat CF;
  1951. CF._dwEffects = 0;
  1952. _prg->Set(cp, fCell ? 1 : 2);
  1953. _prg->SetCharFormat(&CF, 0, NULL, CFM_HIDDEN, 0);
  1954. CheckNotifyLowFiRTF(TRUE);
  1955. _CF._dwEffects |= CFE_HIDDEN; // Restore CFE_HIDDEN
  1956. _dwMaskCF |= CFM_HIDDEN;
  1957. }
  1958. _prg->_rpCF.AdjustForward();
  1959. if(fIsAfterTRD && _crCellCustom1)
  1960. {
  1961. pPF->_crCustom1 = _crCellCustom1;
  1962. dwMask |= PFM_SHADING;
  1963. if(_crCellCustom2)
  1964. {
  1965. pPF->_crCustom2 = _crCellCustom2;
  1966. dwMask |= PFM_NUMBERINGSTART | PFM_NUMBERINGSTYLE;
  1967. }
  1968. }
  1969. }
  1970. if(dwMask)
  1971. {
  1972. _prg->Set(cp, cp - _cpThisPara); // Select back to _cpThisPara
  1973. _prg->SetParaFormat(pPF, NULL, dwMask, dwMask2);
  1974. }
  1975. _prg->Set(cp, 0); // Restore _prg to an IP
  1976. GetTabsCache()->Release(pPF->_iTabs);
  1977. pPF->_iTabs = -1;
  1978. return iTabs;
  1979. }
  1980. /*
  1981. * CRTFRead::SetBorderParm(&Parm, Value)
  1982. *
  1983. * @mfunc
  1984. * Set the border pen width in half points for the current border
  1985. * (_bBorder)
  1986. */
  1987. void CRTFRead::SetBorderParm(
  1988. WORD& Parm,
  1989. LONG Value)
  1990. {
  1991. Assert(_bBorder <= 3);
  1992. Value = min(Value, 15);
  1993. Value = max(Value, 0);
  1994. Parm &= ~(0xF << 4*_bBorder);
  1995. Parm |= Value << 4*_bBorder;
  1996. _dwMaskPF |= PFM_BORDER;
  1997. }
  1998. /*
  1999. * CRTFRead::HandleToken()
  2000. *
  2001. * @mfunc
  2002. * Grand switch board that handles all tokens. Switches on _token
  2003. *
  2004. * @rdesc
  2005. * EC The error code
  2006. *
  2007. * @comm
  2008. * Token values are chosen contiguously (see tokens.h and tokens.c) to
  2009. * encourage the compiler to use a jump table. The lite-RTF keywords
  2010. * come first, so that an optimized OLE-free version works well. Some
  2011. * groups of keyword tokens are ordered so as to simplify the code, e.g,
  2012. * those for font family names, CF effects, and paragraph alignment.
  2013. */
  2014. EC CRTFRead::HandleToken()
  2015. {
  2016. TRACEBEGIN(TRCSUBSYSRTFR, TRCSCOPEINTERN, "CRTFRead::HandleToken");
  2017. DWORD dwT; // Temporary DWORD
  2018. LONG dy, i;
  2019. LONG iParam = _iParam;
  2020. const CCharFormat * pCF;
  2021. STATE * pstate = _pstateStackTop;
  2022. TEXTFONT * ptf;
  2023. WORD wT; // Temporary WORD
  2024. if(!pstate && _token != tokenStartGroup ||
  2025. IN_RANGE(tokenPicFirst, _token, tokenObjLast) && !_prtfObject)
  2026. {
  2027. abort: _ecParseError = ecAbort;
  2028. return ecAbort;
  2029. }
  2030. switch (_token)
  2031. {
  2032. case tokenURtf: // \urtf N - Preferred RE format
  2033. PARSERCOVERAGE_CASE(); // Currently we ignore the N
  2034. _dwFlags &= 0xFFFF; // Kill possible codepage
  2035. _dwFlags |= SF_USECODEPAGE | (CP_UTF8 << 16); // Save bit for Asserts
  2036. pstate->SetCodePage(CP_UTF8);
  2037. goto rtf;
  2038. case tokenPocketWord: // \pwd N - Pocket Word
  2039. _dwFlags |= SFF_PWD;
  2040. case tokenRtf: // \rtf N - Backward compatible
  2041. PARSERCOVERAGE_CASE();
  2042. rtf: if(pstate->pstatePrev)
  2043. goto abort; // Can't handle nested rtf
  2044. pstate->sDest = destRTF;
  2045. Assert(pstate->nCodePage == INVALID_CODEPAGE ||
  2046. pstate->nCodePage == (int)(_dwFlags >> 16) &&
  2047. (_dwFlags & SF_USECODEPAGE));
  2048. if(!_fonts.Count() && !_fonts.Add(1, NULL)) // If can't add a font,
  2049. goto OutOfRAM; // report the bad news
  2050. _sDefaultFont = 0; // Set up valid default font
  2051. ptf = _fonts.Elem(0);
  2052. pstate->ptf = ptf; // Get char set, pitch, family
  2053. pCF = _prg->GetCF(); // from current range font
  2054. ptf->iCharRep = pCF->_iCharRep; // These are guaranteed OK
  2055. ptf->bPitchAndFamily = pCF->_bPitchAndFamily;
  2056. ptf->sCodePage = (SHORT)CodePageFromCharRep(pCF->_iCharRep);
  2057. wcscpy(ptf->szName, GetFontName(pCF->_iFont));
  2058. ptf->fNameIsDBCS = (pCF->_dwEffects & CFE_FACENAMEISDBCS) != 0;
  2059. pstate->cbSkipForUnicodeMax = iUnicodeCChDefault;
  2060. break;
  2061. case tokenViewKind: // \viewkind N
  2062. if(!(_dwFlags & SFF_SELECTION) && IsUTF8)// RTF applies to document:
  2063. _ped->SetViewKind(iParam); // For now, only do for \urtf
  2064. break; // (need to work some more
  2065. // on OutlineView)
  2066. case tokenViewScale: // \viewscale N
  2067. if(_dwFlags & SFF_PERSISTVIEWSCALE &&
  2068. !(_dwFlags & SFF_SELECTION)) // RTF applies to document:
  2069. _ped->SetViewScale(iParam);
  2070. break;
  2071. case tokenCharacterDefault: // \plain
  2072. PARSERCOVERAGE_CASE();
  2073. SetPlain(pstate);
  2074. break;
  2075. case tokenCharSetAnsi: // \ansi
  2076. PARSERCOVERAGE_CASE();
  2077. _iCharRep = ANSI_INDEX;
  2078. break;
  2079. case tokenMac: // \mac
  2080. _fMac = TRUE;
  2081. break;
  2082. case tokenDefaultLanguage: // \deflang
  2083. PARSERCOVERAGE_CASE();
  2084. _sDefaultLanguage = (SHORT)iParam;
  2085. break;
  2086. case tokenDefaultLanguageFE: // \deflangfe
  2087. PARSERCOVERAGE_CASE();
  2088. _sDefaultLanguageFE = (SHORT)iParam;
  2089. break;
  2090. case tokenDefaultTabWidth: // \deftab
  2091. PARSERCOVERAGE_CASE();
  2092. _sDefaultTabWidth = (SHORT)iParam;
  2093. break;
  2094. //--------------------------- Font Control Words -------------------------------
  2095. case tokenDefaultFont: // \deff N
  2096. PARSERCOVERAGE_CASE();
  2097. if(iParam >= 0)
  2098. {
  2099. if(!_fonts.Count() && !_fonts.Add(1, NULL)) // If can't add a font,
  2100. goto OutOfRAM; // report the bad news
  2101. _fonts.Elem(0)->sHandle = _sDefaultFont = (SHORT)iParam;
  2102. }
  2103. TRACEERRSZSC("tokenDefaultFont: Negative value", iParam);
  2104. break;
  2105. case tokenDefaultBiDiFont: // \adeff N
  2106. PARSERCOVERAGE_CASE();
  2107. if(iParam >=0 && _fonts.Count() == 1)
  2108. {
  2109. if(!_fonts.Add(1, NULL))
  2110. goto OutOfRAM;
  2111. _fonts.Elem(1)->sHandle = _sDefaultBiDiFont = (SHORT)iParam;
  2112. }
  2113. TRACEERRSZSC("tokenDefaultBiDiFont: Negative value", iParam);
  2114. break;
  2115. case tokenFontTable: // \fonttbl
  2116. PARSERCOVERAGE_CASE();
  2117. pstate->sDest = destFontTable;
  2118. pstate->ptf = NULL;
  2119. break;
  2120. case tokenFontFamilyBidi: // \fbidi
  2121. case tokenFontFamilyTechnical: // \ftech
  2122. case tokenFontFamilyDecorative: // \fdecor
  2123. case tokenFontFamilyScript: // \fscript
  2124. case tokenFontFamilyModern: // \fmodern
  2125. case tokenFontFamilySwiss: // \fswiss
  2126. case tokenFontFamilyRoman: // \froman
  2127. case tokenFontFamilyDefault: // \fnil
  2128. PARSERCOVERAGE_CASE();
  2129. AssertSz(tokenFontFamilyRoman - tokenFontFamilyDefault == 1,
  2130. "CRTFRead::HandleToken: invalid token definition");
  2131. if(pstate->ptf)
  2132. {
  2133. pstate->ptf->bPitchAndFamily
  2134. = (BYTE)((_token - tokenFontFamilyDefault) << 4
  2135. | (pstate->ptf->bPitchAndFamily & 0xF));
  2136. // Setup SYMBOL_CHARSET charset for \ftech if there isn't any charset info
  2137. if(tokenFontFamilyTechnical == _token && pstate->ptf->iCharRep == DEFAULT_INDEX)
  2138. pstate->ptf->iCharRep = SYMBOL_INDEX;
  2139. }
  2140. break;
  2141. case tokenPitch: // \fprq
  2142. PARSERCOVERAGE_CASE();
  2143. if(pstate->ptf)
  2144. pstate->ptf->bPitchAndFamily
  2145. = (BYTE)(iParam | (pstate->ptf->bPitchAndFamily & 0xF0));
  2146. break;
  2147. case tokenAnsiCodePage: // \ansicpg
  2148. PARSERCOVERAGE_CASE();
  2149. #if !defined(NOFULLDEBUG) && defined(DEBUG)
  2150. if(_fSeenFontTable && _nCodePage == INVALID_CODEPAGE)
  2151. TRACEWARNSZ("CRTFRead::HandleToken(): Found an \ansicpgN tag after "
  2152. "the font table. Should have code to fix-up "
  2153. "converted font names and document text.");
  2154. #endif
  2155. if(!(_dwFlags & SF_USECODEPAGE))
  2156. {
  2157. _nCodePage = iParam;
  2158. pstate->SetCodePage(iParam);
  2159. }
  2160. Assert(!IsUTF8 || pstate->nCodePage == CP_UTF8);
  2161. break;
  2162. case tokenCodePage: // \cpg
  2163. PARSERCOVERAGE_CASE();
  2164. pstate->SetCodePage(iParam);
  2165. if(pstate->sDest == destFontTable && pstate->ptf)
  2166. {
  2167. pstate->ptf->sCodePage = (SHORT)iParam;
  2168. pstate->ptf->iCharRep = CharRepFromCodePage(iParam);
  2169. // If a document-level code page has not been specified,
  2170. // grab this from the first font table entry containing a
  2171. // \fcharsetN or \cpgN
  2172. if(_nCodePage == INVALID_CODEPAGE)
  2173. _nCodePage = iParam;
  2174. }
  2175. break;
  2176. case tokenCharSet: // \fcharset N
  2177. PARSERCOVERAGE_CASE();
  2178. if(pstate->ptf)
  2179. {
  2180. pstate->ptf->iCharRep = CharRepFromCharSet((BYTE)iParam);
  2181. pstate->ptf->sCodePage = (SHORT)CodePageFromCharRep(pstate->ptf->iCharRep);
  2182. pstate->SetCodePage(pstate->ptf->sCodePage);
  2183. // If a document-level code page has not been specified,
  2184. // grab this from the first font table entry containing a
  2185. // \fcharsetN or \cpgN
  2186. if (pstate->nCodePage != CP_SYMBOL &&
  2187. _nCodePage == INVALID_CODEPAGE)
  2188. {
  2189. _nCodePage = pstate->nCodePage;
  2190. }
  2191. if(IsRTLCharSet(iParam))
  2192. {
  2193. if(_sDefaultBiDiFont == -1)
  2194. _sDefaultBiDiFont = pstate->ptf->sHandle;
  2195. if(!IsRTLCharRep(_iCharRepBiDi))
  2196. _iCharRepBiDi = pstate->ptf->iCharRep;
  2197. }
  2198. _fCharSet = TRUE;
  2199. if(iParam)
  2200. _fNon0CharSet = TRUE; // Not HTML converter
  2201. }
  2202. break;
  2203. case tokenRealFontName: // \fname
  2204. PARSERCOVERAGE_CASE();
  2205. pstate->sDest = destRealFontName;
  2206. break;
  2207. case tokenAssocFontSelect: // \af N
  2208. PARSERCOVERAGE_CASE();
  2209. pstate->rgDefFont[pstate->iDefFont].sHandle = iParam;
  2210. iParam = 0; // Fall thru to \afs N to 0 sSize
  2211. case tokenAssocFontSize: // \afs N
  2212. PARSERCOVERAGE_CASE();
  2213. pstate->rgDefFont[pstate->iDefFont].sSize = iParam;
  2214. break;
  2215. case tokenFontSelect: // \f N
  2216. PARSERCOVERAGE_CASE();
  2217. if(iParam == -1) // Can't handle this bizarre choice
  2218. goto skip_group;
  2219. if(pstate->sDest == destFontTable) // Building font table
  2220. {
  2221. if(iParam == _sDefaultFont)
  2222. {
  2223. _fReadDefFont = TRUE;
  2224. ptf = _fonts.Elem(0);
  2225. }
  2226. else if(iParam == _sDefaultBiDiFont)
  2227. ptf = _fonts.Elem(1);
  2228. else if(!(ptf =_fonts.Add(1,NULL))) // Make room in font table for
  2229. { // font to be parsed
  2230. OutOfRAM:
  2231. _ped->GetCallMgr()->SetOutOfMemory();
  2232. _ecParseError = ecNoMemory;
  2233. break;
  2234. }
  2235. pstate->ptf = ptf;
  2236. ptf->sHandle = (SHORT)iParam; // Save handle
  2237. ptf->szName[0] = '\0'; // Start with null string
  2238. ptf->bPitchAndFamily = 0;
  2239. ptf->fNameIsDBCS = FALSE;
  2240. ptf->sCodePage = INVALID_CODEPAGE;
  2241. ptf->fCpgFromSystem = FALSE;
  2242. ptf->iCharRep = DEFAULT_INDEX;
  2243. }
  2244. else if(_fonts.Count() && pstate->sDest != destStyleSheet) // Font switch in text
  2245. {
  2246. SHORT idx = DEFFONT_LTRCH;
  2247. SelectCurrentFont(iParam);
  2248. if(IsRTLCharRep(pstate->ptf->iCharRep))
  2249. {
  2250. _iCharRepBiDi = pstate->ptf->iCharRep;
  2251. idx = DEFFONT_RTLCH;
  2252. if(pstate->iDefFont == DEFFONT_LTRCH)
  2253. pstate->iDefFont = DEFFONT_RTLCH;
  2254. }
  2255. pstate->rgDefFont[idx].sHandle = iParam;
  2256. pstate->rgDefFont[idx].sSize = 0;
  2257. }
  2258. break;
  2259. case tokenDBChars: // \dbch
  2260. case tokenHIChars: // \hich
  2261. case tokenLOChars: // \loch
  2262. case tokenRToLChars: // \rtlch
  2263. case tokenLToRChars: // \ltrch
  2264. pstate->iDefFont = _token - tokenLToRChars + DEFFONT_LTRCH;
  2265. if(!IN_RANGE(DEFFONT_LTRCH, pstate->iDefFont, DEFFONT_RTLCH))
  2266. break;
  2267. i = pstate->rgDefFont[pstate->iDefFont].sHandle;
  2268. if(i == -1)
  2269. break;
  2270. SelectCurrentFont(i);
  2271. HandleNumber(); // Fix Word \ltrchN bug
  2272. iParam = pstate->rgDefFont[pstate->iDefFont].sSize;
  2273. if(!iParam)
  2274. break; // No \afs N value specified
  2275. // Fall thru to \fs N
  2276. case tokenFontSize: // \fs N
  2277. PARSERCOVERAGE_CASE();
  2278. pstate->rgDefFont[pstate->iDefFont].sSize = iParam;
  2279. _CF._yHeight = PointsToFontHeight(iParam); // Convert font size in
  2280. _dwMaskCF |= CFM_SIZE; // half points to logical
  2281. break; // units
  2282. // NOTE: \*\fontemb and \*\fontfile are discarded. The font mapper will
  2283. // have to do the best it can given font name, family, and pitch.
  2284. // Embedded fonts are particularly nasty because legal use should
  2285. // only support read-only which parser cannot enforce.
  2286. case tokenLanguage: // \lang N
  2287. PARSERCOVERAGE_CASE();
  2288. _CF._lcid = MAKELCID(iParam, SORT_DEFAULT);
  2289. _dwMaskCF |= CFM_LCID;
  2290. if(W32->IsBiDiLcid(_CF._lcid))
  2291. {
  2292. _iCharRepBiDi = CharRepFromLID(iParam);
  2293. if(pstate->iDefFont == DEFFONT_LTRCH) // Workaround Word 10 bug
  2294. pstate->iDefFont = DEFFONT_RTLCH;
  2295. }
  2296. break;
  2297. //-------------------------- Color Control Words ------------------------------
  2298. case tokenColorTable: // \colortbl
  2299. PARSERCOVERAGE_CASE();
  2300. pstate->sDest = destColorTable;
  2301. _fGetColorYet = FALSE;
  2302. break;
  2303. case tokenColorRed: // \red
  2304. PARSERCOVERAGE_CASE();
  2305. pstate->bRed = (BYTE)iParam;
  2306. _fGetColorYet = TRUE;
  2307. break;
  2308. case tokenColorGreen: // \green
  2309. PARSERCOVERAGE_CASE();
  2310. pstate->bGreen = (BYTE)iParam;
  2311. _fGetColorYet = TRUE;
  2312. break;
  2313. case tokenColorBlue: // \blue
  2314. PARSERCOVERAGE_CASE();
  2315. pstate->bBlue = (BYTE)iParam;
  2316. _fGetColorYet = TRUE;
  2317. break;
  2318. case tokenColorForeground: // \cf
  2319. PARSERCOVERAGE_CASE();
  2320. _CF._crTextColor = GetColor(CFM_COLOR);
  2321. break;
  2322. case tokenColorBackground: // \highlight
  2323. PARSERCOVERAGE_CASE();
  2324. _CF._crBackColor = GetColor(CFM_BACKCOLOR);
  2325. break;
  2326. case tokenExpand: // \expndtw N
  2327. PARSERCOVERAGE_CASE();
  2328. _CF._sSpacing = (SHORT) iParam;
  2329. _dwMaskCF |= CFM_SPACING;
  2330. break;
  2331. case tokenCharStyle: // \cs N
  2332. PARSERCOVERAGE_CASE();
  2333. /* FUTURE (alexgo): we may want to support character styles
  2334. in some future version.
  2335. _CF._sStyle = (SHORT)iParam;
  2336. _dwMaskCF |= CFM_STYLE; */
  2337. if(pstate->sDest == destStyleSheet)
  2338. goto skip_group;
  2339. break;
  2340. case tokenAnimText: // \animtext N
  2341. PARSERCOVERAGE_CASE();
  2342. _CF._bAnimation = (BYTE)iParam;
  2343. _dwMaskCF |= CFM_ANIMATION;
  2344. CheckNotifyLowFiRTF(TRUE);
  2345. break;
  2346. case tokenKerning: // \kerning N
  2347. PARSERCOVERAGE_CASE();
  2348. _CF._wKerning = (WORD)(10 * iParam); // Convert to twips
  2349. _dwMaskCF |= CFM_KERNING;
  2350. break;
  2351. case tokenHorzInVert: // \horzvert N
  2352. PARSERCOVERAGE_CASE();
  2353. CheckNotifyLowFiRTF(TRUE);
  2354. break;
  2355. case tokenFollowingPunct: // \*\fchars
  2356. PARSERCOVERAGE_CASE();
  2357. pstate->sDest = destFollowingPunct;
  2358. {
  2359. char *pwchBuf=NULL;
  2360. if (ReadRawText((_dwFlags & SFF_SELECTION) ? NULL : &pwchBuf) && pwchBuf)
  2361. {
  2362. if (_ped->SetFollowingPunct(pwchBuf) != NOERROR) // Store this buffer inside doc
  2363. FreePv(pwchBuf);
  2364. }
  2365. else if (pwchBuf)
  2366. FreePv(pwchBuf);
  2367. }
  2368. break;
  2369. case tokenLeadingPunct: // \*\lchars
  2370. PARSERCOVERAGE_CASE();
  2371. pstate->sDest = destLeadingPunct;
  2372. {
  2373. char *pwchBuf=NULL;
  2374. if (ReadRawText((_dwFlags & SFF_SELECTION) ? NULL : &pwchBuf) && pwchBuf)
  2375. {
  2376. if (_ped->SetLeadingPunct(pwchBuf) != NOERROR) // Store this buffer inside doc
  2377. FreePv(pwchBuf);
  2378. }
  2379. else if (pwchBuf)
  2380. FreePv(pwchBuf);
  2381. }
  2382. break;
  2383. case tokenDocumentArea: // \info
  2384. PARSERCOVERAGE_CASE();
  2385. pstate->sDest = destDocumentArea;
  2386. break;
  2387. case tokenVerticalRender: // \vertdoc
  2388. PARSERCOVERAGE_CASE();
  2389. TRACEINFOSZ("Vertical" );
  2390. if (!(_dwFlags & SFF_SELECTION))
  2391. HandleSTextFlow(1);
  2392. break;
  2393. case tokenSTextFlow: // \stextflow N
  2394. PARSERCOVERAGE_CASE();
  2395. TRACEINFOSZ("STextFlow" );
  2396. if (!(_dwFlags & SFF_SELECTION) && !_ped->Get10Mode())
  2397. HandleSTextFlow(iParam);
  2398. break;
  2399. #ifdef FE
  2400. USHORT usPunct; // Used for FE word breaking
  2401. case tokenNoOverflow: // \nooverflow
  2402. PARSERCOVERAGE_CASE();
  2403. TRACEINFOSZ("No Overflow");
  2404. usPunct = ~WBF_OVERFLOW;
  2405. goto setBrkOp;
  2406. case tokenNoWordBreak: // \nocwrap
  2407. PARSERCOVERAGE_CASE();
  2408. TRACEINFOSZ("No Word Break" );
  2409. usPunct = ~WBF_WORDBREAK;
  2410. goto setBrkOp;
  2411. case tokenNoWordWrap: // \nowwrap
  2412. PARSERCOVERAGE_CASE();
  2413. TRACEINFOSZ("No Word Word Wrap" );
  2414. usPunct = ~WBF_WORDWRAP;
  2415. setBrkOp:
  2416. if(!(_dwFlags & fRTFFE))
  2417. {
  2418. usPunct &= UsVGetBreakOption(_ped->lpPunctObj);
  2419. UsVSetBreakOption(_ped->lpPunctObj, usPunct);
  2420. }
  2421. break;
  2422. case tokenHorizontalRender: // \horzdoc
  2423. PARSERCOVERAGE_CASE();
  2424. TRACEINFOSZ("Horizontal" );
  2425. if(pstate->sDest == destDocumentArea && !(_dwFlags & fRTFFE))
  2426. _ped->fModeDefer = FALSE;
  2427. break;
  2428. #endif
  2429. //-------------------- Character Format Control Words -----------------------------
  2430. case tokenUnderlineThickLongDash: // \ulthldash [18]
  2431. case tokenUnderlineThickDotted: // \ulthd [17]
  2432. case tokenUnderlineThickDashDotDot: // \ulthdashdd [16]
  2433. case tokenUnderlineThickDashDot: // \ulthdashd [15]
  2434. case tokenUnderlineThickDash: // \ulthdash [14]
  2435. case tokenUnderlineLongDash: // \ulldash [13]
  2436. case tokenUnderlineHeavyWave: // \ulhwave [12]
  2437. case tokenUnderlineDoubleWave: // \ululdbwave [11]
  2438. case tokenUnderlineHairline: // \ulhair [10]
  2439. case tokenUnderlineThick: // \ulth [9]
  2440. case tokenUnderlineDouble: // \uldb [3]
  2441. case tokenUnderlineWord: // \ulw [2]
  2442. // CheckNotifyLowFiRTF();
  2443. case tokenUnderlineWave: // \ulwave [8]
  2444. case tokenUnderlineDashDotDotted: // \uldashdd [7]
  2445. case tokenUnderlineDashDotted: // \uldashd [6]
  2446. case tokenUnderlineDash: // \uldash [5]
  2447. case tokenUnderlineDotted: // \uld [4]
  2448. PARSERCOVERAGE_CASE();
  2449. _CF._bUnderlineType = (BYTE)(_token - tokenUnderlineWord + 2);
  2450. _token = tokenUnderline; // CRenderer::RenderUnderline()
  2451. goto under; // reveals which of these are
  2452. // rendered specially
  2453. case tokenUnderline: // \ul [Effect 4]
  2454. PARSERCOVERAGE_CASE(); // (see handleCF)
  2455. _CF._bUnderlineType = CFU_UNDERLINE;
  2456. under: _dwMaskCF |= CFM_UNDERLINETYPE;
  2457. goto handleCF;
  2458. case tokenDeleted: // \deleted
  2459. PARSERCOVERAGE_CASE();
  2460. _dwMaskCF2 = CFM2_DELETED;
  2461. dwT = CFE_DELETED;
  2462. goto hndlCF;
  2463. // These effects are turned on if their control word parameter is missing
  2464. // or nonzero. They are turned off if the parameter is zero. This
  2465. // behavior is usually identified by an asterisk (*) in the RTF spec.
  2466. // The code uses fact that CFE_xxx = CFM_xxx
  2467. case tokenImprint: // \impr [1000]
  2468. case tokenEmboss: // \embo [800]
  2469. case tokenShadow: // \shad [400]
  2470. case tokenOutline: // \outl [200]
  2471. case tokenSmallCaps: // \scaps [40]
  2472. CheckNotifyLowFiRTF();
  2473. handleCF:
  2474. case tokenRevised: // \revised [4000]
  2475. case tokenDisabled: // \disabled [2000]
  2476. case tokenHiddenText: // \v [100]
  2477. case tokenCaps: // \caps [80]
  2478. case tokenLink: // \link [20]
  2479. case tokenProtect: // \protect [10]
  2480. case tokenStrikeOut: // \strike [8]
  2481. case tokenItalic: // \i [2]
  2482. case tokenBold: // \b [1]
  2483. PARSERCOVERAGE_CASE();
  2484. dwT = 1 << (_token - tokenBold); // Generate effect mask
  2485. _dwMaskCF |= dwT;
  2486. hndlCF: _CF._dwEffects &= ~dwT; // Default attribute off
  2487. if(!_fParam || _iParam) // Effect is on
  2488. _CF._dwEffects |= dwT; // In either case, the effect
  2489. break; // is defined
  2490. case tokenStopUnderline: // \ulnone
  2491. PARSERCOVERAGE_CASE();
  2492. _CF._dwEffects &= ~CFE_UNDERLINE; // Kill all underlining
  2493. _dwMaskCF |= CFM_UNDERLINE;
  2494. break;
  2495. case tokenRevAuthor: // \revauth N
  2496. PARSERCOVERAGE_CASE();
  2497. /* FUTURE: (alexgo) this doesn't work well now since we don't support
  2498. revision tables. We may want to support this better in the future.
  2499. So what we do now is the 1.0 technique of using a color for the
  2500. author */
  2501. if(iParam > 0)
  2502. {
  2503. _CF._dwEffects &= ~CFE_AUTOCOLOR;
  2504. _dwMaskCF |= CFM_COLOR;
  2505. _CF._crTextColor = rgcrRevisions[(iParam - 1) & REVMASK];
  2506. }
  2507. break;
  2508. case tokenUp: // \up
  2509. PARSERCOVERAGE_CASE();
  2510. dy = 10;
  2511. goto StoreOffset;
  2512. case tokenDown: // \down
  2513. PARSERCOVERAGE_CASE();
  2514. dy = -10;
  2515. StoreOffset:
  2516. if(!_fParam)
  2517. iParam = dyDefaultSuperscript;
  2518. _CF._yOffset = iParam * dy; // Half points->twips
  2519. _dwMaskCF |= CFM_OFFSET;
  2520. break;
  2521. case tokenSuperscript: // \super
  2522. PARSERCOVERAGE_CASE();
  2523. dwT = CFE_SUPERSCRIPT;
  2524. goto SetSubSuperScript;
  2525. case tokenSubscript: // \sub
  2526. PARSERCOVERAGE_CASE();
  2527. dwT = CFE_SUBSCRIPT;
  2528. goto SetSubSuperScript;
  2529. case tokenNoSuperSub: // \nosupersub
  2530. PARSERCOVERAGE_CASE();
  2531. dwT = 0;
  2532. SetSubSuperScript:
  2533. _dwMaskCF |= (CFE_SUPERSCRIPT | CFE_SUBSCRIPT);
  2534. _CF._dwEffects &= ~(CFE_SUPERSCRIPT | CFE_SUBSCRIPT);
  2535. _CF._dwEffects |= dwT;
  2536. break;
  2537. //--------------------- Paragraph Control Words -----------------------------
  2538. case tokenStyleSheet: // \stylesheet
  2539. PARSERCOVERAGE_CASE();
  2540. pstate->sDest = destStyleSheet;
  2541. _Style = 0; // Default normal style
  2542. break;
  2543. case tokenTabBar: // \tb
  2544. PARSERCOVERAGE_CASE();
  2545. _bTabType = PFT_BAR; // Fall thru to \tx
  2546. case tokenTabPosition: // \tx
  2547. PARSERCOVERAGE_CASE();
  2548. if(_cTab < MAX_TAB_STOPS && (unsigned)iParam < 0x1000000)
  2549. {
  2550. _rgxCell[_cTab++] = GetTabPos(iParam)
  2551. + (_bTabType << 24) + (_bTabLeader << 28);
  2552. _dwMaskPF |= PFM_TABSTOPS;
  2553. }
  2554. _cCell = 0; // Invalidate _rgxCell array
  2555. break; // for table purposes
  2556. case tokenDecimalTab: // \tqdec
  2557. case tokenFlushRightTab: // \tqr
  2558. case tokenCenterTab: // \tqc
  2559. PARSERCOVERAGE_CASE();
  2560. _bTabType = (BYTE)(_token - tokenCenterTab + PFT_CENTER);
  2561. break;
  2562. case tokenTabLeaderEqual: // \tleq
  2563. case tokenTabLeaderThick: // \tlth
  2564. case tokenTabLeaderUnderline: // \tlul
  2565. case tokenTabLeaderHyphen: // \tlhyph
  2566. CheckNotifyLowFiRTF();
  2567. case tokenTabLeaderDots: // \tldot
  2568. PARSERCOVERAGE_CASE();
  2569. _bTabLeader = (BYTE)(_token - tokenTabLeaderDots + PFTL_DOTS);
  2570. break;
  2571. // The following need to be kept in sync with PFE_xxx
  2572. case tokenRToLPara: // \rtlpar
  2573. _ped->OrCharFlags(FRTL);
  2574. case tokenCollapsed: // \collapsed
  2575. case tokenSideBySide: // \sbys
  2576. case tokenHyphPar: // \hyphpar
  2577. case tokenNoWidCtlPar: // \nowidctlpar
  2578. case tokenNoLineNumber: // \noline
  2579. case tokenPageBreakBefore: // \pagebb
  2580. case tokenKeepNext: // \keepn
  2581. case tokenKeep: // \keep
  2582. PARSERCOVERAGE_CASE();
  2583. wT = (WORD)(1 << (_token - tokenRToLPara));
  2584. _PF._wEffects |= wT;
  2585. _dwMaskPF |= (wT << 16);
  2586. break;
  2587. case tokenLToRPara: // \ltrpar
  2588. PARSERCOVERAGE_CASE();
  2589. _PF._wEffects &= ~PFE_RTLPARA;
  2590. _dwMaskPF |= PFM_RTLPARA;
  2591. break;
  2592. case tokenLineSpacing: // \sl N
  2593. PARSERCOVERAGE_CASE();
  2594. _PF._dyLineSpacing = abs(iParam);
  2595. _PF._bLineSpacingRule // Handle nonmultiple rules
  2596. = (BYTE)(!iParam || iParam == 1000
  2597. ? 0 : (iParam > 0) ? tomLineSpaceAtLeast
  2598. : tomLineSpaceExactly); // \slmult can change (has to
  2599. _dwMaskPF |= PFM_LINESPACING; // follow if it appears)
  2600. break;
  2601. case tokenDropCapLines: // \dropcapliN
  2602. if(_PF._bLineSpacingRule == tomLineSpaceExactly) // Don't chop off
  2603. _PF._bLineSpacingRule = tomLineSpaceAtLeast; // drop cap
  2604. _fBody = TRUE;
  2605. break;
  2606. case tokenLineSpacingRule: // \slmult N
  2607. PARSERCOVERAGE_CASE();
  2608. if(iParam)
  2609. { // It's multiple line spacing
  2610. _PF._bLineSpacingRule = tomLineSpaceMultiple;
  2611. _PF._dyLineSpacing /= 12; // RE line spacing multiple is
  2612. _dwMaskPF |= PFM_LINESPACING; // given in 20ths of a line,
  2613. } // while RTF uses 240ths
  2614. break;
  2615. case tokenSpaceBefore: // \sb N
  2616. PARSERCOVERAGE_CASE();
  2617. _PF._dySpaceBefore = iParam;
  2618. _dwMaskPF |= PFM_SPACEBEFORE;
  2619. break;
  2620. case tokenSpaceAfter: // \sa N
  2621. PARSERCOVERAGE_CASE();
  2622. _PF._dySpaceAfter = iParam;
  2623. _dwMaskPF |= PFM_SPACEAFTER;
  2624. break;
  2625. case tokenStyle: // \s N
  2626. PARSERCOVERAGE_CASE();
  2627. _Style = iParam; // Save it in case in StyleSheet
  2628. if(pstate->sDest != destStyleSheet)
  2629. { // Select possible heading level
  2630. _PF._sStyle = STYLE_NORMAL; // Default Normal style
  2631. _PF._bOutlineLevel |= 1;
  2632. for(i = 0; i < NSTYLES && iParam != _rgStyles[i]; i++)
  2633. ; // Check for heading style
  2634. if(i < NSTYLES) // Found one
  2635. {
  2636. _PF._sStyle = (SHORT)(-i - 1); // Store desired heading level
  2637. _PF._bOutlineLevel = (BYTE)(2*(i-1));// Update outline level for
  2638. } // nonheading styles
  2639. _dwMaskPF |= PFM_ALLRTF;
  2640. }
  2641. break;
  2642. case tokenIndentFirst: // \fi N
  2643. PARSERCOVERAGE_CASE();
  2644. _PF._dxStartIndent += _PF._dxOffset // Cancel current offset
  2645. + iParam; // and add in new one
  2646. _PF._dxOffset = -iParam; // Offset for all but 1st line
  2647. // = -RTF_FirstLineIndent
  2648. _dwMaskPF |= (PFM_STARTINDENT | PFM_OFFSET);
  2649. break;
  2650. case tokenIndentLeft: // \li N
  2651. case tokenIndentRight: // \ri N
  2652. PARSERCOVERAGE_CASE();
  2653. // AymanA: For RtL para indents has to be flipped.
  2654. Assert(PFE_RTLPARA == 0x0001);
  2655. if((_token == tokenIndentLeft) ^ (_PF.IsRtlPara()))
  2656. {
  2657. _PF._dxStartIndent = iParam - _PF._dxOffset;
  2658. _dwMaskPF |= PFM_STARTINDENT;
  2659. }
  2660. else
  2661. {
  2662. _PF._dxRightIndent = iParam;
  2663. _dwMaskPF |= PFM_RIGHTINDENT;
  2664. }
  2665. break;
  2666. case tokenAlignLeft: // \ql
  2667. case tokenAlignRight: // \qr
  2668. case tokenAlignCenter: // \qc
  2669. case tokenAlignJustify: // \qj
  2670. PARSERCOVERAGE_CASE();
  2671. _PF._bAlignment = (WORD)(_token - tokenAlignLeft + PFA_LEFT);
  2672. _dwMaskPF |= PFM_ALIGNMENT;
  2673. break;
  2674. case tokenBorderOutside: // \brdrbar
  2675. case tokenBorderBetween: // \brdrbtw
  2676. case tokenBorderShadow: // \brdrsh
  2677. PARSERCOVERAGE_CASE();
  2678. _PF._dwBorderColor |= 1 << (_token - tokenBorderShadow + 20);
  2679. _dwBorderColors = _PF._dwBorderColor;
  2680. break;
  2681. // Paragraph and cell border segments
  2682. case tokenBox: // \box
  2683. PARSERCOVERAGE_CASE();
  2684. CheckNotifyLowFiRTF();
  2685. _PF._wEffects |= PFE_BOX;
  2686. _dwMaskPF |= PFM_BOX;
  2687. _bBorder = 0; // Store parms as if for
  2688. break; // \brdrt
  2689. case tokenBorderBottom: // \brdrb
  2690. case tokenBorderRight: // \brdrr
  2691. case tokenBorderTop: // \brdrt
  2692. if((rgKeyword[_iKeyword].szKeyword[0] | 0x20) != 't')
  2693. CheckNotifyLowFiRTF();
  2694. case tokenBorderLeft: // \brdrl
  2695. case tokenCellBorderBottom: // \clbrdrb
  2696. case tokenCellBorderRight: // \clbrdrr
  2697. case tokenCellBorderTop: // \clbrdrt
  2698. case tokenCellBorderLeft: // \clbrdrl
  2699. PARSERCOVERAGE_CASE();
  2700. _bBorder = (BYTE)(_token - tokenBorderLeft);
  2701. break;
  2702. // Paragraph border styles
  2703. case tokenBorderTriple: // \brdrtriple
  2704. case tokenBorderDoubleThick: // \brdrth
  2705. case tokenBorderSingleThick: // \brdrs
  2706. case tokenBorderHairline: // \brdrhair
  2707. case tokenBorderDot: // \brdrdot
  2708. case tokenBorderDouble: // \brdrdb
  2709. case tokenBorderDashSmall: // \brdrdashsm
  2710. case tokenBorderDash: // \brdrdash
  2711. PARSERCOVERAGE_CASE();
  2712. if(_bBorder < 4) // Only for paragraphs
  2713. SetBorderParm(_PF._wBorders, _token - tokenBorderDash);
  2714. break;
  2715. case tokenBorderColor: // \brdrcf
  2716. PARSERCOVERAGE_CASE();
  2717. if(_bBorder < 4) // Only for paragraphs
  2718. {
  2719. iParam = GetStandardColorIndex();
  2720. _PF._dwBorderColor &= ~(0x1F << (5*_bBorder));
  2721. _PF._dwBorderColor |= iParam << (5*_bBorder);
  2722. _dwBorderColors = _PF._dwBorderColor;
  2723. }
  2724. else // Cell borders
  2725. _dwCellColors |= GetCellColorIndex() << (5*(_bBorder - 4));
  2726. break;
  2727. case tokenBorderWidth: // \brdrw
  2728. PARSERCOVERAGE_CASE(); // Store width in half pts
  2729. // iParam is in twips
  2730. if(_bBorder < 4) // Paragraphs
  2731. {
  2732. iParam = TwipsToQuarterPoints(iParam);
  2733. SetBorderParm(_PF._wBorderWidth, iParam);
  2734. }
  2735. else // Table cells
  2736. {
  2737. iParam = CheckTwips(iParam);
  2738. _dwCellBrdrWdths |= iParam << 8*(_bBorder - 4);
  2739. }
  2740. break;
  2741. case tokenBorderSpace: // \brsp
  2742. PARSERCOVERAGE_CASE(); // Space is in pts
  2743. if(_bBorder < 4) // Only for paragraphs
  2744. SetBorderParm(_PF._wBorderSpace, iParam/20);// iParam is in twips
  2745. break;
  2746. // Paragraph shading
  2747. case tokenBckgrndVert: // \bgvert
  2748. case tokenBckgrndHoriz: // \bghoriz
  2749. case tokenBckgrndFwdDiag: // \bgfdiag
  2750. case tokenBckgrndDrkVert: // \bgdkvert
  2751. case tokenBckgrndDrkHoriz: // \bgdkhoriz
  2752. case tokenBckgrndDrkFwdDiag: // \bgdkfdiag
  2753. case tokenBckgrndDrkDiagCross: // \bgdkdcross
  2754. case tokenBckgrndDrkCross: // \bgdkcross
  2755. case tokenBckgrndDrkBckDiag: // \bgdkbdiag
  2756. case tokenBckgrndDiagCross: // \bgdcross
  2757. case tokenBckgrndCross: // \bgcross
  2758. case tokenBckgrndBckDiag: // \bgbdiag
  2759. PARSERCOVERAGE_CASE();
  2760. _PF._wShadingStyle = (WORD)((_PF._wShadingStyle & 0xFFC0)
  2761. | (_token - tokenBckgrndBckDiag + 1));
  2762. _dwMaskPF |= PFM_SHADING;
  2763. break;
  2764. case tokenColorBckgrndPat: // \cbpat
  2765. PARSERCOVERAGE_CASE();
  2766. iParam = GetStandardColorIndex();
  2767. _PF._wShadingStyle = (WORD)((_PF._wShadingStyle & 0x07FF) | (iParam << 11));
  2768. _dwMaskPF |= PFM_SHADING;
  2769. break;
  2770. case tokenColorForgrndPat: // \cfpat
  2771. PARSERCOVERAGE_CASE();
  2772. iParam = GetStandardColorIndex();
  2773. _PF._wShadingStyle = (WORD)((_PF._wShadingStyle & 0xF83F) | (iParam << 6));
  2774. _dwMaskPF |= PFM_SHADING;
  2775. break;
  2776. case tokenShading: // \shading
  2777. PARSERCOVERAGE_CASE();
  2778. _PF._wShadingWeight = (WORD)iParam;
  2779. _dwMaskPF |= PFM_SHADING;
  2780. break;
  2781. // Paragraph numbering
  2782. case tokenParaNum: // \pn
  2783. PARSERCOVERAGE_CASE();
  2784. pstate->sDest = destParaNumbering;
  2785. pstate->fBullet = FALSE;
  2786. _PF._wNumberingStart = 1;
  2787. _dwMaskPF |= PFM_NUMBERINGSTART;
  2788. break;
  2789. case tokenParaNumIndent: // \pnindent N
  2790. PARSERCOVERAGE_CASE();
  2791. if(pstate->sDest == destParaNumbering)
  2792. pstate->sIndentNumbering = (SHORT)iParam;
  2793. break;
  2794. case tokenParaNumStart: // \pnstart N
  2795. PARSERCOVERAGE_CASE();
  2796. if(pstate->sDest == destParaNumbering)
  2797. {
  2798. _PF._wNumberingStart = (WORD)iParam;
  2799. _dwMaskPF |= PFM_NUMBERINGSTART;
  2800. }
  2801. break;
  2802. case tokenParaNumCont: // \pnlvlcont
  2803. PARSERCOVERAGE_CASE();
  2804. _prg->_rpPF.AdjustBackward(); // Maintain numbering mode
  2805. _PF._wNumbering = _prg->GetPF()->_wNumbering;
  2806. _prg->_rpPF.AdjustForward();
  2807. _wNumberingStyle = PFNS_NONUMBER; // Signal no number
  2808. _dwMaskPF |= PFM_NUMBERING; // Note: can be new para with
  2809. pstate->fBullet = TRUE; // its own indents
  2810. break;
  2811. case tokenParaNumBody: // \pnlvlbody
  2812. PARSERCOVERAGE_CASE();
  2813. _wNumberingStyle = PFNS_PAREN;
  2814. _token = tokenParaNumDecimal; // Default to decimal
  2815. goto setnum;
  2816. case tokenParaNumBullet: // \pnlvlblt
  2817. _wNumberingStyle = 0; // Reset numbering styles
  2818. goto setnum;
  2819. case tokenParaNumDecimal: // \pndec
  2820. case tokenParaNumLCLetter: // \pnlcltr
  2821. case tokenParaNumUCLetter: // \pnucltr
  2822. case tokenParaNumLCRoman: // \pnlcrm
  2823. case tokenParaNumUCRoman: // \pnucrm
  2824. PARSERCOVERAGE_CASE();
  2825. if(_PF._wNumbering == PFN_BULLET && pstate->fBullet)
  2826. break; // Ignore above for bullets
  2827. setnum: if(pstate->sDest == destParaNumbering)
  2828. {
  2829. _PF._wNumbering = (WORD)(PFN_BULLET + _token - tokenParaNumBullet);
  2830. _dwMaskPF |= PFM_NUMBERING;
  2831. pstate->fBullet = TRUE; // We do bullets, so don't
  2832. } // output the \pntext group
  2833. break;
  2834. case tokenParaNumText: // \pntext
  2835. PARSERCOVERAGE_CASE();
  2836. // Throw away previously read paragraph numbering and use
  2837. // the most recently read to apply to next run of text.
  2838. _cchUsedNumText = 0;
  2839. pstate->sDest = destParaNumText;
  2840. break;
  2841. case tokenParaNumAlignCenter: // \pnqc
  2842. case tokenParaNumAlignRight: // \pnqr
  2843. PARSERCOVERAGE_CASE();
  2844. _wNumberingStyle = (_wNumberingStyle & ~3) | _token - tokenParaNumAlignCenter + 1;
  2845. break;
  2846. case tokenPictureQuickDraw: // \macpict
  2847. case tokenPictureOS2Metafile: // \pmmetafile
  2848. CheckNotifyLowFiRTF(TRUE);
  2849. case tokenParaNumAfter: // \pntxta
  2850. case tokenParaNumBefore: // \pntxtb
  2851. PARSERCOVERAGE_CASE();
  2852. skip_group:
  2853. if(!SkipToEndOfGroup())
  2854. {
  2855. // During \fonttbl processing, we may hit unknown destinations,
  2856. // e.g., \panose, that cause the HandleEndGroup to select the
  2857. // default font, which may not be defined yet. So, we change
  2858. // sDest to avoid this problem.
  2859. if(pstate->sDest == destFontTable || pstate->sDest == destStyleSheet)
  2860. pstate->sDest = destNULL;
  2861. HandleEndGroup();
  2862. }
  2863. break;
  2864. // Tables
  2865. case tokenInTable: // \intbl
  2866. PARSERCOVERAGE_CASE();
  2867. if(pstate->sDest != destRTF && pstate->sDest != destFieldResult &&
  2868. pstate->sDest != destParaNumText)
  2869. {
  2870. _ecParseError = ecUnexpectedToken;
  2871. break;
  2872. }
  2873. if(!_iCell && !_bTableLevel)
  2874. DelimitRow(szRowStart); // Start row
  2875. break;
  2876. case tokenNestCell: // \nestcell
  2877. case tokenCell: // \cell
  2878. PARSERCOVERAGE_CASE();
  2879. HandleCell();
  2880. break;
  2881. case tokenRowHeight: // \trrh N
  2882. PARSERCOVERAGE_CASE();
  2883. _dyRow = iParam;
  2884. break;
  2885. case tokenCellHalfGap: // \trgaph N
  2886. PARSERCOVERAGE_CASE(); // Save half space between
  2887. if((unsigned)iParam > 255) // Illegal value: use default
  2888. iParam = 108;
  2889. _dxCell = iParam; // cells to add to tabs
  2890. break; // Roundtrip value at end of
  2891. // tab array
  2892. case tokenCellX: // \cellx N
  2893. PARSERCOVERAGE_CASE();
  2894. HandleCellx(iParam);
  2895. break;
  2896. case tokenRowDefault: // \trowd
  2897. PARSERCOVERAGE_CASE();
  2898. if(_ped->fUsePassword() || pstate->sDest == destParaNumText)
  2899. {
  2900. _ecParseError = ecUnexpectedToken;
  2901. break;
  2902. }
  2903. // Insert a newline if we are inserting a table behind characters in the
  2904. // same line. This follows the Word9 model.
  2905. if (_cpFirst == _prg->GetCp() && _cpThisPara != _cpFirst)
  2906. {
  2907. EC ec = _ped->fUseCRLF() // If RichEdit 1.0 compatibility
  2908. ? HandleText(szaCRLF, ALL_ASCII)// mode, use CRLF; else CR
  2909. : HandleChar((unsigned)(CR));
  2910. if(ec == ecNoError)
  2911. _cpThisPara = _prg->GetCp(); // New para starts after CRLF
  2912. }
  2913. _cCell = 0; // No cell right boundaries
  2914. _dxCell = 0; // or half gap defined yet
  2915. _xRowOffset = 0;
  2916. _dwCellBrdrWdths = 0;
  2917. _dyRow = 0; // No row height yet
  2918. _wBorderWidth = 0; // No borders yet
  2919. _dwBorderColors = 0; // No colors yet
  2920. _dwCellColors = 0; // No colors yet
  2921. _dwShading = 0; // No shading yet
  2922. _bAlignment = PFA_LEFT;
  2923. _iTabsTable = -1; // No cell widths yet
  2924. _bCellFlags = 0; // No cell vert merge
  2925. _crCellCustom1 = 0;
  2926. _crCellCustom2 = 0;
  2927. _fRTLRow = FALSE;
  2928. break;
  2929. case tokenRowLeft: // \trleft N
  2930. PARSERCOVERAGE_CASE();
  2931. _xRowOffset = iParam;
  2932. break;
  2933. case tokenRowAlignCenter: // \trqc
  2934. case tokenRowAlignRight: // \trqr
  2935. PARSERCOVERAGE_CASE();
  2936. _bAlignment = (WORD)(_token - tokenRowAlignRight + PFA_RIGHT);
  2937. break;
  2938. case tokenRToLRow: // \rtlrow
  2939. _fRTLRow = TRUE;
  2940. break;
  2941. case tokenNestRow: // \nestrow
  2942. _fNo_iTabsTable = TRUE;
  2943. goto row;
  2944. case tokenRow: // \row
  2945. PARSERCOVERAGE_CASE();
  2946. _iTabsLevel1 = -1;
  2947. row:
  2948. if(!_bTableLevel) // Ignore \row and \nestrow if not in table
  2949. break;
  2950. while(_iCell < _cCell) // If not enuf cells, add
  2951. HandleCell(); // them since Word crashes
  2952. DelimitRow(szRowEnd);
  2953. if(_fNo_iTabsTable && !_bTableLevel) // New nested table format
  2954. InitializeTableRowParms(); // used so reset _cCell
  2955. break; // (new values will be given)
  2956. case tokenCellBackColor: // \clcbpat N
  2957. _dwCellColors |= GetCellColorIndex() << 4*5;
  2958. break;
  2959. case tokenCellForeColor: // \clcfpat N
  2960. _dwCellColors |= GetCellColorIndex() << 5*5;
  2961. break;
  2962. case tokenCellShading: // \clshdng N
  2963. _dwShading = iParam/50; // Store in .5 per cents
  2964. break; // (N is in .01 per cent)
  2965. case tokenCellAlignBottom: // \clvertalb
  2966. case tokenCellAlignCenter: // \clvertalc
  2967. PARSERCOVERAGE_CASE();
  2968. _bCellFlags |= _token - tokenCellAlignCenter + 1;
  2969. break;
  2970. case tokenCellMergeDown: // \clvmgf
  2971. _bCellFlags |= fTopCell >> 24;
  2972. break;
  2973. case tokenCellMergeUp: // \clvmrg
  2974. _bCellFlags |= fLowCell >> 24;
  2975. break;
  2976. case tokenCellTopBotRLVert: // \cltxtbrlv
  2977. PARSERCOVERAGE_CASE();
  2978. _bCellFlags |= fVerticalCell >> 24;
  2979. break;
  2980. case tokenCellLRTB: // \cltxlrtb
  2981. break; // This is the default
  2982. // so don't fire LowFiRTF
  2983. case tokenTableLevel: // \itap N
  2984. PARSERCOVERAGE_CASE(); // Set table level
  2985. if(pstate->fShape) // Bogus shape RTF
  2986. break;
  2987. AssertSz(iParam >= _bTableLevel,
  2988. "CRTFRead::HandleToken: illegal itap N");
  2989. if(iParam)
  2990. {
  2991. if(pstate->sDest != destRTF && pstate->sDest != destFieldResult || iParam > 127)
  2992. goto abort;
  2993. _iTabsTable = -1; // Previous cell widths invalid
  2994. _cCell = 0;
  2995. while(iParam > _bTableLevel)
  2996. DelimitRow(szRowStart); // Insert enuf table row headers
  2997. }
  2998. _fNo_iTabsTable = TRUE;
  2999. break;
  3000. case tokenNestTableProps: // \nesttableprops
  3001. break; // Control word is recognized
  3002. case tokenNoNestTables: // \nonesttables
  3003. goto skip_group; // Ignore info for nesttable
  3004. // unaware readers
  3005. case tokenPage: // \page
  3006. // FUTURE: we want to be smarter about handling FF. But for
  3007. // now we ignore it for bulletted and number paragraphs
  3008. // and RE 1.0 mode.
  3009. if (_PF._wNumbering != 0 || _ped->Get10Mode())
  3010. break;
  3011. // Intentional fall thru to EOP
  3012. case tokenEndParagraph: // \par
  3013. case tokenLineBreak: // \line
  3014. PARSERCOVERAGE_CASE();
  3015. HandleEndOfPara();
  3016. break;
  3017. case tokenParagraphDefault: // \pard
  3018. PARSERCOVERAGE_CASE();
  3019. if(pstate->sDest != destParaNumText) // Ignore if \pn destination
  3020. Pard(pstate);
  3021. break;
  3022. case tokenEndSection: // \sect
  3023. CheckNotifyLowFiRTF(); // Fall thru to \sectd
  3024. case tokenSectionDefault: // \sectd
  3025. PARSERCOVERAGE_CASE();
  3026. Pard(pstate);
  3027. break;
  3028. case tokenBackground: // \background
  3029. if(_dwFlags & SFF_SELECTION) // If pasting a selection,
  3030. goto skip_group; // skip background
  3031. pstate->fBackground = TRUE; // Enable background. NB:
  3032. break; // InitBackground() already called
  3033. //----------------------- Field and Group Control Words --------------------------------
  3034. case tokenField: // \field
  3035. PARSERCOVERAGE_CASE();
  3036. if (pstate->sDest == destDocumentArea ||
  3037. pstate->sDest == destLeadingPunct ||
  3038. pstate->sDest == destFollowingPunct)
  3039. {
  3040. // We're not equipped to handle symbols in these destinations, and
  3041. // we don't want the fields added accidentally to document text.
  3042. goto skip_group;
  3043. }
  3044. pstate->sDest = destField;
  3045. break;
  3046. case tokenFieldResult: // \fldrslt
  3047. PARSERCOVERAGE_CASE();
  3048. if(_fSymbolField)
  3049. goto skip_group;
  3050. pstate->sDest = destFieldResult;
  3051. AddText(pchSeparateField, 2, FALSE);
  3052. break;
  3053. case tokenFieldInstruction: // \fldinst
  3054. PARSERCOVERAGE_CASE();
  3055. if(AddText(pchStartField, 2, FALSE) == ecNoError)
  3056. pstate->sDest = destFieldInstruction;
  3057. break;
  3058. case tokenStartGroup: // Save current state by
  3059. PARSERCOVERAGE_CASE(); // pushing it onto stack
  3060. HandleStartGroup();
  3061. if (_fNoRTFtoken)
  3062. {
  3063. // Hack Alert !!!!! For 1.0 compatibility to allow no \rtf token.
  3064. _fNoRTFtoken = FALSE;
  3065. pstate = _pstateStackTop;
  3066. goto rtf;
  3067. }
  3068. break;
  3069. case tokenEndGroup:
  3070. PARSERCOVERAGE_CASE();
  3071. HandleFieldEndGroup(); // Special end group handling for \field
  3072. HandleEndGroup(); // Restore save state by
  3073. break; // popping stack
  3074. case tokenOptionalDestination: // (see case tokenUnknown)
  3075. PARSERCOVERAGE_CASE();
  3076. break;
  3077. case tokenNullDestination: // Found a destination whose group
  3078. PARSERCOVERAGE_CASE(); // should be skipped
  3079. // tokenNullDestination triggers a loss notification here for...
  3080. // Footer related tokens - "footer", "footerf", "footerl", "footerr",
  3081. // "footnote", "ftncn", "ftnsep", "ftnsepc"
  3082. // Header related tokens - "header", "headerf", "headerl", "headerr"
  3083. // Table of contents - "tc"
  3084. // Index entries - "xe"
  3085. CheckNotifyLowFiRTF();
  3086. // V-GUYB: PWord Converter requires loss notification.
  3087. #ifdef REPORT_LOSSAGE
  3088. if(!(_dwFlags & SFF_SELECTION)) // SFF_SELECTION is set if any kind of paste is being done.
  3089. {
  3090. ((LOST_COOKIE*)(_pes->dwCookie))->bLoss = TRUE;
  3091. }
  3092. #endif // REPORT_LOSSAGE
  3093. goto skip_group;
  3094. case tokenUnknownKeyword:
  3095. PARSERCOVERAGE_CASE();
  3096. if(_tokenLast == tokenOptionalDestination)
  3097. goto skip_group;
  3098. break;
  3099. //-------------------------- Text Control Words --------------------------------
  3100. case tokenUnicode: // \u N
  3101. PARSERCOVERAGE_CASE();
  3102. HandleUN(pstate);
  3103. break;
  3104. case tokenUnicodeCharByteCount: // \uc N
  3105. PARSERCOVERAGE_CASE();
  3106. if(IN_RANGE(1, iParam, 2))
  3107. pstate->cbSkipForUnicodeMax = iParam;
  3108. break;
  3109. case tokenText: // Lexer concludes tokenText
  3110. case tokenASCIIText:
  3111. PARSERCOVERAGE_CASE();
  3112. HandleTextToken(pstate);
  3113. break;
  3114. // \ltrmark, \rtlmark, \zwj, and \zwnj are translated directly into
  3115. // their Unicode values. \ltrmark and \rtlmark cause no further
  3116. // processing here because we assume that the current font has the
  3117. // CharSet needed to identify the direction.
  3118. case tokenLToRDocument: // \ltrdoc
  3119. PARSERCOVERAGE_CASE();
  3120. _bDocType = DT_LTRDOC;
  3121. break;
  3122. case tokenRToLDocument: // \rtldoc
  3123. PARSERCOVERAGE_CASE();
  3124. _bDocType = DT_RTLDOC;
  3125. _ped->OrCharFlags(FRTL);
  3126. break;
  3127. //--------------------------Shape Control Words---------------------------------
  3128. case tokenShape: // \shp
  3129. if(!pstate->fBackground)
  3130. CheckNotifyLowFiRTF(TRUE);
  3131. pstate->fShape = TRUE;
  3132. _dwFlagsShape = 0;
  3133. break;
  3134. case tokenShapeName: // \sn name
  3135. pstate->sDest = destShapeName;
  3136. break;
  3137. case tokenShapeValue: // \sv value
  3138. pstate->sDest = destShapeValue;
  3139. break;
  3140. case tokenShapeWrap: // \shpwr N
  3141. if(iParam == 2)
  3142. _dwFlagsShape |= REO_WRAPTEXTAROUND;
  3143. break;
  3144. case tokenPositionRight: // \posxr
  3145. _dwFlagsShape |= REO_ALIGNTORIGHT;
  3146. break;
  3147. //------------------------- Object Control Words --------------------------------
  3148. case tokenObject: // \object
  3149. PARSERCOVERAGE_CASE();
  3150. // V-GUYB: PWord Converter requires loss notification.
  3151. #ifdef REPORT_LOSSAGE
  3152. if(!(_dwFlags & SFF_SELECTION)) // SFF_SELECTION is set if any kind of paste is being done.
  3153. {
  3154. ((LOST_COOKIE*)(_pes->dwCookie))->bLoss = TRUE;
  3155. }
  3156. #endif // REPORT_LOSSAGE
  3157. // Assume that the object failed to load until proven otherwise
  3158. // by RTFRead::ObjectReadFromEditStream
  3159. // This works for both:
  3160. // - an empty \objdata tag
  3161. // - a non-existent \objdata tag
  3162. _fFailedPrevObj = TRUE;
  3163. case tokenPicture: // \pict
  3164. PARSERCOVERAGE_CASE();
  3165. pstate->sDest = (SHORT)(_token == tokenPicture ? destPicture : destObject);
  3166. FreeRtfObject();
  3167. _prtfObject = (RTFOBJECT *) PvAlloc(sizeof(RTFOBJECT), GMEM_ZEROINIT);
  3168. if(!_prtfObject)
  3169. goto OutOfRAM;
  3170. _prtfObject->xScale = _prtfObject->yScale = 100;
  3171. _prtfObject->cBitsPerPixel = 1;
  3172. _prtfObject->cColorPlanes = 1;
  3173. _prtfObject->szClass = NULL;
  3174. _prtfObject->szName = NULL;
  3175. _prtfObject->sType = -1;
  3176. break;
  3177. case tokenObjectEBookImage:
  3178. // Added by VikramM for E-Book
  3179. //
  3180. _prtfObject->sType = ROT_EBookImage;
  3181. break;
  3182. case tokenObjectEmbedded: // \objemb
  3183. case tokenObjectLink: // \objlink
  3184. case tokenObjectAutoLink: // \objautlink
  3185. PARSERCOVERAGE_CASE();
  3186. _prtfObject->sType = (SHORT)(_token - tokenObjectEmbedded + ROT_Embedded);
  3187. break;
  3188. case tokenObjectMacSubscriber: // \objsub
  3189. case tokenObjectMacPublisher: // \objpub
  3190. case tokenObjectMacICEmbedder:
  3191. PARSERCOVERAGE_CASE();
  3192. _prtfObject->sType = ROT_MacEdition;
  3193. break;
  3194. case tokenWidth: // \picw N or \objw N
  3195. PARSERCOVERAGE_CASE();
  3196. _prtfObject->xExt = iParam;
  3197. break;
  3198. case tokenHeight: // \pic N or \objh N
  3199. PARSERCOVERAGE_CASE();
  3200. _prtfObject->yExt = iParam;
  3201. break;
  3202. case tokenObjectSetSize: // \objsetsize
  3203. PARSERCOVERAGE_CASE();
  3204. _prtfObject->fSetSize = TRUE;
  3205. break;
  3206. case tokenScaleX: // \picscalex N or \objscalex N
  3207. PARSERCOVERAGE_CASE();
  3208. _prtfObject->xScale = iParam;
  3209. break;
  3210. case tokenScaleY: // \picscaley N or \objscaley N
  3211. PARSERCOVERAGE_CASE();
  3212. _prtfObject->yScale = iParam;
  3213. break;
  3214. case tokenCropLeft: // \piccropl or \objcropl
  3215. case tokenCropTop: // \piccropt or \objcropt
  3216. case tokenCropRight: // \piccropr or \objcropr
  3217. case tokenCropBottom: // \piccropb or \objcropb
  3218. PARSERCOVERAGE_CASE();
  3219. *((LONG *)&_prtfObject->rectCrop
  3220. + (_token - tokenCropLeft)) = iParam;
  3221. break;
  3222. case tokenObjectClass: // \objclass
  3223. PARSERCOVERAGE_CASE();
  3224. pstate->sDest = destObjectClass;
  3225. break;
  3226. case tokenObjectName: // \objname
  3227. PARSERCOVERAGE_CASE();
  3228. pstate->sDest = destObjectName;
  3229. break;
  3230. case tokenObjectResult: // \result
  3231. PARSERCOVERAGE_CASE();
  3232. if(_fMac || // If it's Mac stuff, we don't
  3233. _prtfObject->sType==ROT_MacEdition ||// understand the data, or if
  3234. _fFailedPrevObj || _fNeedPres) // we need an obj presentation,
  3235. {
  3236. pstate->sDest = destRTF; // use the object results
  3237. break;
  3238. }
  3239. goto skip_group;
  3240. case tokenObjectData: // \objdata
  3241. PARSERCOVERAGE_CASE();
  3242. pstate->sDest = destObjectData;
  3243. if(_prtfObject->sType==ROT_MacEdition) // It's Mac stuff so just
  3244. goto skip_group; // throw away the data
  3245. break;
  3246. case tokenPictureWindowsMetafile: // \wmetafile
  3247. #ifdef NOMETAFILES
  3248. goto skip_group;
  3249. #endif NOMETAFILES
  3250. case tokenPngBlip: // \pngblip
  3251. case tokenJpegBlip: // \jpegblip
  3252. case tokenPictureWindowsDIB: // \dibitmap N
  3253. case tokenPictureWindowsBitmap: // \wbitmap N
  3254. PARSERCOVERAGE_CASE();
  3255. _prtfObject->sType = (SHORT)(_token - tokenPictureWindowsBitmap + ROT_Bitmap);
  3256. _prtfObject->sPictureType = (SHORT)iParam;
  3257. break;
  3258. case tokenBitmapBitsPerPixel: // \wbmbitspixel N
  3259. PARSERCOVERAGE_CASE();
  3260. _prtfObject->cBitsPerPixel = (SHORT)iParam;
  3261. break;
  3262. case tokenBitmapNumPlanes: // \wbmplanes N
  3263. PARSERCOVERAGE_CASE();
  3264. _prtfObject->cColorPlanes = (SHORT)iParam;
  3265. break;
  3266. case tokenBitmapWidthBytes: // \wbmwidthbytes N
  3267. PARSERCOVERAGE_CASE();
  3268. _prtfObject->cBytesPerLine = (SHORT)iParam;
  3269. break;
  3270. case tokenDesiredWidth: // \picwgoal N
  3271. PARSERCOVERAGE_CASE();
  3272. _prtfObject->xExtGoal = (SHORT)iParam;
  3273. break;
  3274. case tokenDesiredHeight: // \pichgoal N
  3275. PARSERCOVERAGE_CASE();
  3276. _prtfObject->yExtGoal = (SHORT)iParam;
  3277. break;
  3278. case tokenBinaryData: // \bin N
  3279. PARSERCOVERAGE_CASE();
  3280. // Update OleGet function
  3281. RTFReadOLEStream.lpstbl->Get =
  3282. (DWORD (CALLBACK* )(LPOLESTREAM, void FAR*, DWORD))
  3283. RTFGetBinaryDataFromStream;
  3284. _cbBinLeft = iParam; // Set data length
  3285. switch (pstate->sDest)
  3286. {
  3287. case destObjectData:
  3288. _fFailedPrevObj = !ObjectReadFromEditStream();
  3289. break;
  3290. case destPicture:
  3291. StaticObjectReadFromEditStream(iParam);
  3292. break;
  3293. default:
  3294. AssertSz(FALSE, "Binary data hit but don't know where to put it");
  3295. }
  3296. // Restore OleGet function
  3297. RTFReadOLEStream.lpstbl->Get =
  3298. (DWORD (CALLBACK* )(LPOLESTREAM, void FAR*, DWORD))
  3299. RTFGetFromStream;
  3300. break;
  3301. case tokenObjectDataValue:
  3302. PARSERCOVERAGE_CASE();
  3303. if(_prtfObject->sType != ROT_EBookImage) // Added by VikramM for E-Book
  3304. {
  3305. // Normal processing
  3306. _fFailedPrevObj = !ObjectReadFromEditStream();
  3307. }
  3308. else
  3309. {
  3310. // Do the Ebook Image callback here and set the _prtfObject size here
  3311. // Don't need to read the image data at this point, we just want to
  3312. // do a callback at a later point to have the E-Book shell render the image
  3313. _fFailedPrevObj = !ObjectReadEBookImageInfoFromEditStream();
  3314. }
  3315. goto EndOfObjectStream;
  3316. case tokenPictureDataValue:
  3317. PARSERCOVERAGE_CASE();
  3318. StaticObjectReadFromEditStream();
  3319. EndOfObjectStream:
  3320. if(!SkipToEndOfGroup())
  3321. HandleEndGroup();
  3322. break;
  3323. case tokenObjectPlaceholder:
  3324. PARSERCOVERAGE_CASE();
  3325. if(_ped->GetEventMask() & ENM_OBJECTPOSITIONS)
  3326. {
  3327. if(!_pcpObPos)
  3328. {
  3329. _pcpObPos = (LONG *)PvAlloc(sizeof(ULONG) * cobPosInitial, GMEM_ZEROINIT);
  3330. if(!_pcpObPos)
  3331. {
  3332. _ecParseError = ecNoMemory;
  3333. break;
  3334. }
  3335. _cobPosFree = cobPosInitial;
  3336. _cobPos = 0;
  3337. }
  3338. if(_cobPosFree-- <= 0)
  3339. {
  3340. const int cobPosNew = _cobPos + cobPosChunk;
  3341. LPVOID pv;
  3342. pv = PvReAlloc(_pcpObPos, sizeof(ULONG) * cobPosNew);
  3343. if(!pv)
  3344. {
  3345. _ecParseError = ecNoMemory;
  3346. break;
  3347. }
  3348. _pcpObPos = (LONG *)pv;
  3349. _cobPosFree = cobPosChunk - 1;
  3350. }
  3351. _pcpObPos[_cobPos++] = _prg->GetCp();
  3352. }
  3353. break;
  3354. default:
  3355. PARSERCOVERAGE_DEFAULT();
  3356. if(pstate->sDest != destFieldInstruction && // Values outside token
  3357. (DWORD)(_token - tokenMin) > // range are treated
  3358. (DWORD)(tokenMax - tokenMin)) // as Unicode chars
  3359. {
  3360. // 1.0 mode doesn't use Unicode bullets nor smart quotes
  3361. if (_ped->Get10Mode() && IN_RANGE(LQUOTE, _token, RDBLQUOTE))
  3362. {
  3363. if (_token == LQUOTE || _token == RQUOTE)
  3364. _token = L'\'';
  3365. else if (_token == LDBLQUOTE || _token == RDBLQUOTE)
  3366. _token = L'\"';
  3367. }
  3368. if(!IsLowMergedCell())
  3369. HandleChar(_token);
  3370. }
  3371. #if defined(DEBUG) && !defined(NOFULLDEBUG)
  3372. else
  3373. {
  3374. if(GetProfileIntA("RICHEDIT DEBUG", "RTFCOVERAGE", 0))
  3375. {
  3376. CHAR *pszKeyword = PszKeywordFromToken(_token);
  3377. CHAR szBuf[256];
  3378. sprintf(szBuf, "CRTFRead::HandleToken(): Token not processed - token = %d, %s%s%s",
  3379. _token,
  3380. "keyword = ",
  3381. pszKeyword ? "\\" : "<unknown>",
  3382. pszKeyword ? pszKeyword : "");
  3383. AssertSz(0, szBuf);
  3384. }
  3385. }
  3386. #endif
  3387. }
  3388. TRACEERRSZSC("HandleToken()", - _ecParseError);
  3389. return _ecParseError;
  3390. }
  3391. /*
  3392. * CRTFRead::IsLowMergedCell()
  3393. *
  3394. * @mfunc
  3395. * Return TRUE iff _prg is currently in a low merged table cell. Note
  3396. * that RichEdit can't insert any text into a low merged cell, but
  3397. * Word's RTF sometimes attempts to, e.g., {\listtext...} ignored
  3398. * by Word can be (erroneously) emitted for insertion into these cells.
  3399. * Hence we discard such insertions.
  3400. *
  3401. * @rdesc
  3402. * Return TRUE iff _prg is currently in a low merged table cell
  3403. */
  3404. BOOL CRTFRead::IsLowMergedCell()
  3405. {
  3406. if(!_bTableLevel)
  3407. return FALSE;
  3408. CELLPARMS *pCellParms = (CELLPARMS *)&_rgxCell[0];
  3409. return IsLowCell(pCellParms[_iCell].uCell);
  3410. }
  3411. /*
  3412. * CRTFRead::Pard(pstate)
  3413. *
  3414. * @mfunc
  3415. * Reset paragraph and pstate properties to default values
  3416. */
  3417. void CRTFRead::Pard(
  3418. STATE *pstate)
  3419. {
  3420. if(IN_RANGE(destColorTable, pstate->sDest, destPicture))
  3421. {
  3422. _ecParseError = ecAbort;
  3423. return;
  3424. }
  3425. BYTE bT = _PF._bOutlineLevel; // Save outline level
  3426. _PF.InitDefault(_bDocType == DT_RTLDOC ? PFE_RTLPARA : 0);
  3427. // Reset para formatting
  3428. pstate->fBullet = FALSE;
  3429. pstate->sIndentNumbering = 0;
  3430. _cTab = 0; // No tabs defined
  3431. _bTabLeader = 0;
  3432. _bTabType = 0;
  3433. _bBorder = 0;
  3434. _fStartRow = FALSE;
  3435. _PF._bOutlineLevel = (BYTE)(bT | 1);
  3436. _dwMaskPF = PFM_ALLRTF;
  3437. _dwMaskPF2 = PFM2_TABLEROWSHIFTED;
  3438. }
  3439. /*
  3440. * CRTFRead::DelimitRow(szRowDelimiter)
  3441. *
  3442. * @mfunc
  3443. * Insert start-of-row or end-of-row paragraph with current table
  3444. * properties
  3445. */
  3446. void CRTFRead::DelimitRow(
  3447. WCHAR *szRowDelimiter) //@parm Delimit text to insert
  3448. {
  3449. if(!_ped->_pdp->IsMultiLine()) // No tables in single line
  3450. { // controls
  3451. _ecParseError = ecTruncateAtCRLF;
  3452. return;
  3453. }
  3454. LONG nTableIndex = _bTableLevel;
  3455. if(szRowDelimiter == szRowEnd)
  3456. {
  3457. if(!_iCell) // Bad RTF: \row with no \cell,
  3458. HandleCell(); // so fake one
  3459. nTableIndex--;
  3460. }
  3461. if(nTableIndex + _bTableLevelIP >= MAXTABLENEST)
  3462. {
  3463. if(szRowDelimiter == szRowEnd) // Maintain _bTableLevel
  3464. _bTableLevel--;
  3465. else
  3466. _bTableLevel++;
  3467. _token = tokenEndParagraph;
  3468. HandleEndOfPara();
  3469. return;
  3470. }
  3471. if(szRowDelimiter == szRowStart && _prg->GetCp() && !_prg->_rpTX.IsAfterEOP())
  3472. {
  3473. _token = tokenEndParagraph;
  3474. HandleEndOfPara();
  3475. }
  3476. Assert(_pstateStackTop && _pstateStackTop->pPF);
  3477. // Add _PF diffs to *_pstateStackTop->pPF
  3478. if(!_pstateStackTop->AddPF(_PF, _bDocType, _dwMaskPF, _dwMaskPF2))
  3479. {
  3480. _ped->GetCallMgr()->SetOutOfMemory();
  3481. _ecParseError = ecNoMemory;
  3482. return;
  3483. }
  3484. DWORD dwMaskPF = _pstateStackTop->dwMaskPF; // Save PF for restoration
  3485. DWORD dwMaskPF2 = _pstateStackTop->dwMaskPF2; // Save PF for restoration
  3486. SHORT iTabs = -1;
  3487. CParaFormat PF = *_pstateStackTop->pPF; // on return
  3488. _PF.InitDefault(_fRTLRow ? PFE_RTLPARA : 0);
  3489. _fStartRow = FALSE;
  3490. _dwMaskPF = PFM_ALLRTF;
  3491. _dwMaskPF2 = 0;
  3492. if(_wBorderWidth) // Store any border info
  3493. {
  3494. _PF._dwBorderColor = _dwBorderColors;
  3495. _PF._wBorders = _wBorders;
  3496. _PF._wBorderSpace = _wBorderSpace;
  3497. _PF._wBorderWidth = _wBorderWidth;
  3498. _dwMaskPF |= PFM_BORDER;
  3499. }
  3500. _PF._bAlignment = _bAlignment; // Row alignment (no cell align)
  3501. _PF._dxStartIndent = _xRowOffset; // \trleft N
  3502. _PF._dxOffset = max(_dxCell, 10); // \trgaph N
  3503. _PF._dyLineSpacing = _dyRow; // \trrh N
  3504. _PF._wEffects |= PFE_TABLE | PFE_TABLEROWDELIMITER;
  3505. BOOL fHidden = _ped->GetCharFormat(_prg->Get_iFormat())->_dwEffects & CFE_HIDDEN;
  3506. _prg->_rpCF.AdjustBackward();
  3507. if(_prg->IsHidden())
  3508. {
  3509. CCharFormat CF;
  3510. CF._dwEffects = 0; // Don't hide EOP preceding TRD
  3511. _prg->BackupCRLF(CSC_NORMAL, TRUE);
  3512. _prg->SetCharFormat(&CF, 0, NULL, CFM_HIDDEN, 0);
  3513. CheckNotifyLowFiRTF(TRUE);
  3514. _prg->AdvanceCRLF(CSC_NORMAL, FALSE);
  3515. }
  3516. _prg->_rpCF.AdjustForward();
  3517. AssertSz(!_prg->GetCp() || IsEOP(_prg->GetPrevChar()),
  3518. "CRTFRead::DelimitRow: no EOP precedes TRD");
  3519. if(AddText(szRowDelimiter, 2, FALSE) != ecNoError)
  3520. goto cleanup;
  3521. if(!_bTableLevel && _PF._dxStartIndent < 50)// Move neg shifted table right
  3522. { // (handles common default Word table)
  3523. _PF._wEffects |= PFE_TABLEROWSHIFTED;
  3524. _dwMaskPF2 |= PFM2_TABLEROWSHIFTED;
  3525. _PF._dxStartIndent += _dxCell + 50; // 50 gives room for left border
  3526. }
  3527. if(szRowDelimiter == szRowStart)
  3528. _bTableLevel++;
  3529. _PF._bTableLevel = _bTableLevel + _bTableLevelIP;
  3530. iTabs = Apply_PF();
  3531. if(szRowDelimiter == szRowStart)
  3532. {
  3533. if(_bTableLevel == 1)
  3534. _iTabsLevel1 = iTabs;
  3535. _rgTableState[nTableIndex]._iCell = _iCell;
  3536. _rgTableState[nTableIndex]._cCell = _cCell;
  3537. if(_token == tokenTableLevel)
  3538. _cCell = 0;
  3539. _iCell = 0;
  3540. if(!_cCell) // Cache if need to recompute row PF
  3541. _dwRowResolveFlags |= 1 << _bTableLevel;
  3542. }
  3543. else
  3544. {
  3545. Assert(szRowDelimiter == szRowEnd);
  3546. DWORD dwMask = 1 << _bTableLevel;
  3547. if(_dwRowResolveFlags & dwMask)
  3548. { // Copy iPF over to corresponding
  3549. CPFRunPtr rpPF(*_prg); // row header
  3550. rpPF.ResolveRowStartPF();
  3551. _dwRowResolveFlags &= (dwMask - 1);
  3552. // Insert NOTACHARs for cells
  3553. LONG cp = _prg->GetCp(); // vert merged with cells above
  3554. LONG j = _cCell - 1;
  3555. CELLPARMS *pCellParms = (CELLPARMS *)&_rgxCell[0];
  3556. WCHAR szNOTACHAR[1] = {NOTACHAR};
  3557. _prg->Move(-2, FALSE); // Move before row-end delimiter
  3558. for(LONG i = _cCell; i--;) // and CELL mark
  3559. {
  3560. if(IsLowCell(pCellParms[i].uCell))
  3561. {
  3562. if(i != j)
  3563. _prg->Move(tomCell, i - j, NULL);
  3564. if(_prg->GetPrevChar() == CELL)
  3565. _prg->Move(-1, FALSE); // Backspace over CELL mark
  3566. Assert(_prg->_rpTX.GetChar() == CELL);
  3567. if(_prg->_rpTX.GetPrevChar() == NOTACHAR)
  3568. _prg->Move(-1, FALSE);
  3569. else
  3570. {
  3571. _prg->ReplaceRange(1, szNOTACHAR, NULL, SELRR_IGNORE, NULL, 0);
  3572. _prg->Move(-2, FALSE); // Backspace over NOTACHAR CELL combo
  3573. cp++;
  3574. }
  3575. j = i - 1;
  3576. }
  3577. }
  3578. _prg->SetCp(cp, FALSE); // Reposition rg after end-row delim
  3579. Assert(_prg->_rpTX.IsAfterTRD(ENDFIELD));
  3580. }
  3581. _bTableLevel--; // End of current row
  3582. _iCell = _rgTableState[nTableIndex]._iCell;
  3583. _cCell = _rgTableState[nTableIndex]._cCell;
  3584. if(!_bTableLevel)
  3585. _fStartRow = TRUE; // Tell AddText to start new row
  3586. } // unless \pard terminates it
  3587. _cpThisPara = _prg->GetCp(); // New para starts after CRLF
  3588. cleanup:
  3589. _PF = PF;
  3590. _dwMaskPF = dwMaskPF;
  3591. _dwMaskPF2 = dwMaskPF2;
  3592. if(fHidden) // Restore hidden property
  3593. {
  3594. _CF._dwEffects |= CFE_HIDDEN;
  3595. _dwMaskCF |= CFM_HIDDEN;
  3596. }
  3597. Assert(!(_PF._wEffects & PFE_TABLEROWDELIMITER));
  3598. }
  3599. /*
  3600. * CRTFRead::InitializeTableRowParms()
  3601. *
  3602. * @mfunc
  3603. * Initialize table parms to no table state
  3604. */
  3605. void CRTFRead::InitializeTableRowParms()
  3606. {
  3607. // Initialize table parms
  3608. _cCell = 0; // No table cells yet
  3609. _iCell = 0;
  3610. _fStartRow = FALSE;
  3611. _wBorderWidth = 0;
  3612. _bAlignment = PFA_LEFT;
  3613. _xRowOffset = 0;
  3614. _dxCell = 0;
  3615. _dyRow = 0;
  3616. _iTabsTable = -1;
  3617. }
  3618. /*
  3619. * CRTFRead::ReadRtf()
  3620. *
  3621. * @mfunc
  3622. * The range _prg is replaced by RTF data resulting from parsing the
  3623. * input stream _pes. The CRTFRead object assumes that the range is
  3624. * already degenerate (caller has to delete the range contents, if
  3625. * any, before calling this routine). Currently any info not used
  3626. * or supported by RICHEDIT is thrown away.
  3627. *
  3628. * @rdesc
  3629. * Number of chars inserted into text. 0 means none were inserted
  3630. * OR an error occurred.
  3631. */
  3632. LONG CRTFRead::ReadRtf()
  3633. {
  3634. TRACEBEGIN(TRCSUBSYSRTFR, TRCSCOPEINTERN, "CRTFRead::ReadRtf");
  3635. LONG cpFirst;
  3636. LONG cpFirstInPara;
  3637. CTxtRange * prg = _prg;
  3638. STATE * pstate;
  3639. cpFirst = _cpFirst = prg->GetCp();
  3640. if (!_cchMax)
  3641. {
  3642. // At text limit already, forget it.
  3643. _ecParseError = ecTextMax;
  3644. goto Quit;
  3645. }
  3646. if(!InitLex())
  3647. goto Quit;
  3648. TESTPARSERCOVERAGE();
  3649. AssertSz(!prg->GetCch(),
  3650. "CRTFRead::ReadRtf: range must be deleted");
  3651. if(!(_dwFlags & SFF_SELECTION))
  3652. {
  3653. // SFF_SELECTION is set if any kind of paste is being done, i.e.,
  3654. // not just that using the selection. If it isn't set, data is
  3655. // being streamed in and we allow this to reset the doc params
  3656. if(_ped->InitDocInfo() != NOERROR)
  3657. {
  3658. _ecParseError = ecNoMemory;
  3659. goto Quit;
  3660. }
  3661. }
  3662. prg->SetIgnoreFormatUpdate(TRUE);
  3663. _szUnicode = (WCHAR *)PvAlloc(cachTextMax * sizeof(WCHAR), GMEM_ZEROINIT);
  3664. if(!_szUnicode) // Allocate space for Unicode conversions
  3665. {
  3666. _ped->GetCallMgr()->SetOutOfMemory();
  3667. _ecParseError = ecNoMemory;
  3668. goto CleanUp;
  3669. }
  3670. _cchUnicode = cachTextMax;
  3671. // Initialize per-read variables
  3672. _nCodePage = (_dwFlags & SF_USECODEPAGE)
  3673. ? (_dwFlags >> 16) : INVALID_CODEPAGE;
  3674. // Populate _PF with initial paragraph formatting properties
  3675. _PF = *prg->GetPF();
  3676. _dwMaskPF = PFM_ALLRTF; // Setup initial MaskPF
  3677. _PF._iTabs = -1; // In case it's not -1
  3678. if(_PF.IsTableRowDelimiter()) // Do _not_ insert with this property!
  3679. {
  3680. if(prg->_rpTX.IsAtTRD(ENDFIELD))
  3681. {
  3682. prg->AdvanceCRLF(CSC_NORMAL, FALSE);// Bypass table row-end delimiter
  3683. cpFirst = prg->GetCp(); // Update value
  3684. _PF = *prg->GetPF(); // Might still be row-start delimiter
  3685. _PF._iTabs = -1;
  3686. Assert(!prg->_rpTX.IsAtTRD(ENDFIELD));
  3687. }
  3688. if(prg->_rpTX.IsAtTRD(STARTFIELD))
  3689. {
  3690. // REVIEW: this if can probably be omitted now since the caller calls
  3691. // DeleteWithTRDCheck()
  3692. _ecParseError = ecGeneralFailure;
  3693. goto CleanUp;
  3694. }
  3695. }
  3696. _bTableLevelIP = _PF._bTableLevel; // Save table level of insertion pt
  3697. AssertSz(_bTableLevelIP >= 0, "CRTFRead::ReadRtf: illegal table level");
  3698. // V-GUYB: PWord Converter requires loss notification.
  3699. #ifdef REPORT_LOSSAGE
  3700. if(!(_dwFlags & SFF_SELECTION)) // SFF_SELECTION is set if any
  3701. { // kind of paste is being done
  3702. ((LOST_COOKIE*)(_pes->dwCookie))->bLoss = FALSE;
  3703. }
  3704. #endif // REPORT_LOSSAGE
  3705. // Valid RTF files start with "{\rtf", "{urtf", or "{\pwd"
  3706. GetChar(); // Fill input buffer
  3707. UngetChar(); // Put char back
  3708. if(!IsRTF((char *)_pchRTFCurrent, _pchRTFEnd - _pchRTFCurrent)) // Is it RTF?
  3709. { // No
  3710. if (_ped->Get10Mode())
  3711. _fNoRTFtoken = TRUE;
  3712. else
  3713. {
  3714. _ecParseError = ecUnexpectedToken; // Signal bad file
  3715. goto CleanUp;
  3716. }
  3717. }
  3718. // If initial cp follows EOP, use it for _cpThisPara. Else
  3719. // search for start of para containing the initial cp.
  3720. _cpThisPara = prg->GetCp();
  3721. if(!prg->_rpTX.IsAfterEOP())
  3722. {
  3723. CTxtPtr tp(prg->_rpTX);
  3724. tp.FindEOP(tomBackward);
  3725. _cpThisPara = tp.GetCp();
  3726. }
  3727. cpFirstInPara = _cpThisPara; // Backup to start of para before
  3728. // parsing
  3729. while ( TokenGetToken() != tokenEOF && // Process tokens
  3730. _token != tokenError &&
  3731. !HandleToken() &&
  3732. _pstateStackTop )
  3733. ;
  3734. if(_ecParseError == ecAbort) // Really vile error: delete anything
  3735. { // that was inserted
  3736. prg->Set(prg->GetCp(), prg->GetCp() - cpFirst);
  3737. prg->ReplaceRange(0, NULL, NULL, SELRR_IGNORE, NULL,
  3738. RR_NO_LP_CHECK | RR_NO_TRD_CHECK | RR_NO_CHECK_TABLE_SEL);
  3739. goto CleanUp;
  3740. }
  3741. if(_bTableLevel) // Whoops! still in middle of table
  3742. {
  3743. LONG cpMin;
  3744. prg->_rpPF.AdjustBackward(); // Be sure to get preceding level
  3745. prg->FindRow(&cpMin, NULL, _bTableLevelIP + 1);// Find beginning of row and
  3746. if(cpMin == prg->GetCp()) // delete from there to here
  3747. { // No table formatting yet
  3748. LONG ch; // Just delete back to STARTFIELD
  3749. CTxtPtr tp(prg->_rpTX);
  3750. while((ch = tp.PrevChar()) && ch != STARTFIELD)
  3751. ;
  3752. cpMin = tp.GetCp();
  3753. }
  3754. cpMin = max(cpMin, _cpFirst);
  3755. prg->Set(prg->GetCp(), prg->GetCp() - cpMin);
  3756. prg->ReplaceRange(0, NULL, NULL, SELRR_IGNORE, NULL,
  3757. RR_NO_LP_CHECK | RR_NO_TRD_CHECK | RR_NO_CHECK_TABLE_SEL);
  3758. #ifdef DEBUG
  3759. prg->_rpTX.MoveGapToEndOfBlock();
  3760. #endif
  3761. }
  3762. _cCell = _iCell = 0;
  3763. prg->SetIgnoreFormatUpdate(FALSE); // Enable range _iFormat updates
  3764. prg->Update_iFormat(-1); // Update _iFormat to CF
  3765. // at current active end
  3766. if(!(_dwFlags & SFF_SELECTION)) // RTF applies to document:
  3767. { // update CDocInfo
  3768. // Apply char and para formatting of
  3769. // final text run to final CR
  3770. if (prg->GetCp() == _ped->GetAdjustedTextLength() &&
  3771. !(_dwMaskPF & (PFM_TABLEROWDELIMITER | PFM_TABLE)))
  3772. {
  3773. // REVIEW: we need to think about what para properties should
  3774. // be transferred here. E.g., borders were being transferred
  3775. // incorrectly
  3776. _dwMaskPF &= ~(PFM_BORDER | PFM_SHADING);
  3777. Apply_PF();
  3778. prg->ExtendFormattingCRLF();
  3779. }
  3780. // Update the per-document information from the RTF read
  3781. CDocInfo *pDocInfo = _ped->GetDocInfoNC();
  3782. if(!pDocInfo)
  3783. {
  3784. Assert(FALSE); // Should be allocated by
  3785. _ecParseError = ecNoMemory; // earlier call in this function
  3786. goto CleanUp;
  3787. }
  3788. if (ecNoError == _ecParseError) // If range end EOP wasn't
  3789. prg->DeleteTerminatingEOP(NULL); // deleted and new text
  3790. // ends with an EOP, delete that EOP
  3791. pDocInfo->_wCpg = (WORD)(_nCodePage == INVALID_CODEPAGE ?
  3792. tomInvalidCpg : _nCodePage);
  3793. if (pDocInfo->_wCpg == CP_UTF8)
  3794. pDocInfo->_wCpg = 1252;
  3795. _ped->SetDefaultLCID(_sDefaultLanguage == INVALID_LANGUAGE ?
  3796. tomInvalidLCID :
  3797. MAKELCID(_sDefaultLanguage, SORT_DEFAULT));
  3798. _ped->SetDefaultLCIDFE(_sDefaultLanguageFE == INVALID_LANGUAGE ?
  3799. tomInvalidLCID :
  3800. MAKELCID(_sDefaultLanguageFE, SORT_DEFAULT));
  3801. _ped->SetDefaultTabStop(TWIPS_TO_FPPTS(_sDefaultTabWidth));
  3802. _ped->SetDocumentType(_bDocType);
  3803. }
  3804. if(_ped->IsComplexScript() && prg->GetCp() > cpFirstInPara)
  3805. {
  3806. Assert(!prg->GetCch());
  3807. LONG cpSave = prg->GetCp();
  3808. LONG cpLastInPara = cpSave;
  3809. if(_ped->IsBiDi() && !prg->_rpTX.IsAtEOP())
  3810. {
  3811. CTxtPtr tp(prg->_rpTX);
  3812. tp.FindEOP(tomForward);
  3813. cpLastInPara = tp.GetCp();
  3814. prg->Move(cpLastInPara - cpSave, FALSE);
  3815. }
  3816. // Itemize from the start of paragraph to be inserted till the end of
  3817. // paragraph inserting. We need to cover all affected paragraphs because
  3818. // paragraphs we're playing could possibly in conflict direction. Think
  3819. // about the case that the range covers one LTR para and one RTL para, then
  3820. // the inserting text covers one RTL and one LTR. Both paragraphs' direction
  3821. // could have been changed after this insertion.
  3822. prg->ItemizeReplaceRange(cpLastInPara - cpFirstInPara, 0, NULL,
  3823. _ped->IsBiDi() && !_fNon0CharSet);
  3824. if (cpLastInPara != cpSave)
  3825. prg->SetCp(cpSave, FALSE);
  3826. }
  3827. CleanUp:
  3828. FreeRtfObject();
  3829. pstate = _pstateStackTop;
  3830. if(pstate) // Illegal RTF file. Release
  3831. { // unreleased format indices
  3832. if(ecNoError == _ecParseError) // It's only an overflow if no
  3833. _ecParseError = ecStackOverflow; // other error has occurred
  3834. if(_ecParseError != ecAbort)
  3835. HandleFieldEndGroup(); // Cleanup possible partial field
  3836. while(pstate->pstatePrev)
  3837. {
  3838. pstate = pstate->pstatePrev;
  3839. ReleaseFormats(pstate->iCF, -1);
  3840. }
  3841. }
  3842. pstate = _pstateLast;
  3843. if(pstate)
  3844. {
  3845. while(pstate->pstatePrev) // Free all but first STATE
  3846. {
  3847. pstate->DeletePF();
  3848. pstate = pstate->pstatePrev;
  3849. FreePv(pstate->pstateNext);
  3850. }
  3851. pstate->DeletePF();
  3852. }
  3853. Assert(_PF._iTabs == -1);
  3854. FreePv(pstate); // Free first STATE
  3855. FreePv(_szUnicode);
  3856. Quit:
  3857. DeinitLex();
  3858. if(_pcpObPos)
  3859. {
  3860. if((_ped->GetEventMask() & ENM_OBJECTPOSITIONS) && _cobPos > 0)
  3861. {
  3862. OBJECTPOSITIONS obpos;
  3863. obpos.cObjectCount = _cobPos;
  3864. obpos.pcpPositions = _pcpObPos;
  3865. if (_ped->Get10Mode())
  3866. {
  3867. LONG *pcpPositions = _pcpObPos;
  3868. for (LONG i = 0; i < _cobPos; i++, pcpPositions++)
  3869. *pcpPositions = _ped->GetAcpFromCp(*pcpPositions);
  3870. }
  3871. _ped->TxNotify(EN_OBJECTPOSITIONS, &obpos);
  3872. }
  3873. FreePv(_pcpObPos);
  3874. _pcpObPos = NULL;
  3875. }
  3876. // transcribed from winerror.h
  3877. #define ERROR_HANDLE_EOF 38L
  3878. // FUTURE(BradO): We should devise a direct mapping from our error codes
  3879. // to Win32 error codes. In particular our clients are
  3880. // not expecting the error code produced by:
  3881. // _pes->dwError = (DWORD) -(LONG) _ecParseError;
  3882. if(_ecParseError)
  3883. {
  3884. AssertSz(_ecParseError >= 0,
  3885. "Parse error is negative");
  3886. if(_ecParseError == ecTextMax)
  3887. {
  3888. _ped->GetCallMgr()->SetMaxText();
  3889. _pes->dwError = (DWORD)STG_E_MEDIUMFULL;
  3890. }
  3891. if(_ecParseError == ecUnexpectedEOF)
  3892. _pes->dwError = (DWORD)HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
  3893. if(!_pes->dwError && _ecParseError != ecTruncateAtCRLF)
  3894. _pes->dwError = (DWORD) -(LONG) _ecParseError;
  3895. #if defined(DEBUG)
  3896. TRACEERRSZSC("CchParse_", _pes->dwError);
  3897. if(ecNoError < _ecParseError && _ecParseError < ecLastError)
  3898. Tracef(TRCSEVERR, "Parse error: %s", rgszParseError[_ecParseError]);
  3899. #endif
  3900. }
  3901. if(cpFirst > _cpFirst && prg->GetCp() == cpFirst)
  3902. {
  3903. prg->SetCp(_cpFirst, FALSE); // Restore prg cp, since nothing inserted
  3904. return 0;
  3905. }
  3906. return prg->GetCp() - cpFirst;
  3907. }
  3908. /*
  3909. * CRTFRead::CpgInfoFromFaceName()
  3910. *
  3911. * @mfunc
  3912. * This routine fills in the TEXTFONT::bCharSet and TEXTFONT::nCodePage
  3913. * members of the TEXTFONT structure by querying the system for the
  3914. * metrics of the font described by TEXTFONT::szName.
  3915. *
  3916. * @rdesc
  3917. * A flag indicating whether the charset and codepage were successfully
  3918. * determined.
  3919. */
  3920. BOOL CRTFRead::CpgInfoFromFaceName(
  3921. TEXTFONT *ptf)
  3922. {
  3923. // FUTURE(BradO): This code is a condensed version of a more sophisticated
  3924. // algorithm we use in font.cpp to second-guess the font-mapper.
  3925. // We should factor out the code from font.cpp for use here as well.
  3926. // Indicates that we've tried to obtain the cpg info from the system,
  3927. // so that after a failure we don't re-call this routine.
  3928. ptf->fCpgFromSystem = TRUE;
  3929. if(ptf->fNameIsDBCS)
  3930. {
  3931. // If fNameIsDBCS, we have high-ANSI characters in the facename, and
  3932. // no codepage with which to interpret them. The facename is gibberish,
  3933. // so don't waste time calling the system to match it.
  3934. return FALSE;
  3935. }
  3936. HDC hdc = _ped->TxGetDC();
  3937. if(!hdc)
  3938. return FALSE;
  3939. LOGFONT lf = {0};
  3940. TEXTMETRIC tm;
  3941. wcscpy(lf.lfFaceName, ptf->szName);
  3942. lf.lfCharSet = CharSetFromCharRep(CharRepFromCodePage(GetSystemDefaultCodePage()));
  3943. if(!GetTextMetrics(hdc, lf, tm) || tm.tmCharSet != lf.lfCharSet)
  3944. {
  3945. lf.lfCharSet = DEFAULT_CHARSET; // Doesn't match default sys
  3946. GetTextMetrics(hdc, lf, tm); // charset, so see what
  3947. } // DEFAULT_CHARSET gives
  3948. _ped->TxReleaseDC(hdc);
  3949. if(tm.tmCharSet != DEFAULT_CHARSET) // Got something, so use it
  3950. {
  3951. ptf->iCharRep = CharRepFromCharSet(tm.tmCharSet);
  3952. ptf->sCodePage = (SHORT)CodePageFromCharRep(ptf->iCharRep);
  3953. return TRUE;
  3954. }
  3955. return FALSE;
  3956. }
  3957. // Including a source file, but we only want to compile this code for debug purposes
  3958. #if defined(DEBUG)
  3959. #include "rtflog.cpp"
  3960. #endif