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

919 lines
22 KiB

  1. // ==========================================================================================
  2. // RtfParser.cpp
  3. //
  4. // Impl RTF parser
  5. //
  6. // History:
  7. // first created
  8. // ==========================================================================================
  9. //#include <windows.h>
  10. #include "stdafx.h"
  11. #include <stdio.h>
  12. #include <assert.h>
  13. #include "rtfparser.h"
  14. #include "ConvEng.h"
  15. //extern BOOL MapFunc(PBYTE, UINT, PBYTE, UINT*);
  16. const char szRTFSignature[] = "{\\rtf";
  17. // Keyword descriptions
  18. SYM g_rgSymRtf[] = {
  19. // keyword kwd idx
  20. "*", kwdSpec, ipfnSkipDest,
  21. "'", kwdSpec, ipfnHex,
  22. "bin", kwdSpec, ipfnBin,
  23. "upr", kwdDest, idestSkip,
  24. "fonttbl", kwdDest, idestSkip,
  25. /*
  26. // we will search through following destinations
  27. "author", kwdDest, idestSkip,
  28. "buptim", kwdDest, idestSkip,
  29. "colortbl", kwdDest, idestSkip,
  30. "comment", kwdDest, idestSkip,
  31. "creatim", kwdDest, idestSkip,
  32. "doccomm", kwdDest, idestSkip,
  33. "fonttbl", kwdDest, idestSkip,
  34. "footer", kwdDest, idestSkip,
  35. "footerf", kwdDest, idestSkip,
  36. "footerl", kwdDest, idestSkip,
  37. "footerr", kwdDest, idestSkip,
  38. "footnote", kwdDest, idestSkip,
  39. "ftncn", kwdDest, idestSkip,
  40. "ftnsep", kwdDest, idestSkip,
  41. "ftnsepc", kwdDest, idestSkip,
  42. "header", kwdDest, idestSkip,
  43. "headerf", kwdDest, idestSkip,
  44. "headerl", kwdDest, idestSkip,
  45. "headerr", kwdDest, idestSkip,
  46. "info", kwdDest, idestSkip,
  47. "keywords", kwdDest, idestSkip,
  48. "operator", kwdDest, idestSkip,
  49. "pict", kwdDest, idestSkip,
  50. "printim", kwdDest, idestSkip,
  51. "private1", kwdDest, idestSkip,
  52. "revtim", kwdDest, idestSkip,
  53. "rxe", kwdDest, idestSkip,
  54. "stylesheet", kwdDest, idestSkip,
  55. "subject", kwdDest, idestSkip,
  56. "tc", kwdDest, idestSkip,
  57. "title", kwdDest, idestSkip,
  58. "txe", kwdDest, idestSkip,
  59. "xe", kwdDest, idestSkip,
  60. */
  61. };
  62. int g_iSymMax = sizeof(g_rgSymRtf) / sizeof(SYM);
  63. // ctor
  64. CRtfParser::CRtfParser( BYTE* pchInput, UINT cchInput,
  65. BYTE* pchOutput, UINT cchOutput)
  66. {
  67. m_fInit = FALSE;
  68. m_pchInput = pchInput;
  69. m_cchInput = cchInput;
  70. m_pchOutput = pchOutput;
  71. m_cchOutput = cchOutput;
  72. Reset();
  73. if (pchInput && pchOutput && cchInput && cchOutput) {
  74. m_fInit = TRUE;
  75. }
  76. }
  77. // Reset
  78. // clean internal status before start the parser
  79. void CRtfParser::Reset(void)
  80. {
  81. m_cGroup = 0;
  82. m_cbBin = 0;
  83. m_fSkipDestIfUnk = FALSE;
  84. m_ris = risNorm;
  85. m_rds = rdsNorm;
  86. m_psave = NULL;
  87. m_uCursor = 0;
  88. m_uOutPos = 0;
  89. m_bsStatus = bsDefault;
  90. m_uConvStart = 0;
  91. m_cchConvLen = 0;
  92. memset(&m_sKeyword,0, sizeof(SKeyword));
  93. }
  94. // check signature
  95. BOOL CRtfParser::fRTFFile()
  96. {
  97. if (m_fInit &&
  98. 0 == memcmp(m_pchInput, szRTFSignature, strlen(szRTFSignature)))
  99. {
  100. return TRUE;
  101. }
  102. return FALSE;
  103. }
  104. // Get major version
  105. int
  106. CRtfParser::GetVersion(PDWORD pdwVersion)
  107. {
  108. int ec;
  109. *pdwVersion = 1;
  110. // set keyword to get
  111. m_sKeyword.wStatus |= KW_ENABLE;
  112. strcpy(m_sKeyword.szKeyword, "rtf");
  113. ec = Do();
  114. if (ec == ecOK &&
  115. (m_sKeyword.wStatus & KW_FOUND) &&
  116. (m_sKeyword.wStatus & KW_PARAM))
  117. {
  118. *pdwVersion = (DWORD) atoi(m_sKeyword.szParameter);
  119. }
  120. Reset();
  121. return ec;
  122. }
  123. // GetCodepage
  124. int
  125. CRtfParser::GetCodepage(PDWORD pdwCodepage)
  126. {
  127. int ec;
  128. *pdwCodepage = 0;
  129. // set keyword to get
  130. m_sKeyword.wStatus |= KW_ENABLE;
  131. strcpy(m_sKeyword.szKeyword, "ansicpg");
  132. ec = Do();
  133. if (ec == ecOK &&
  134. (m_sKeyword.wStatus & KW_FOUND) &&
  135. (m_sKeyword.wStatus & KW_PARAM))
  136. {
  137. *pdwCodepage = atoi(m_sKeyword.szParameter);
  138. }
  139. Reset();
  140. return ec;
  141. }
  142. // do
  143. // main parser function
  144. int
  145. CRtfParser::Do()
  146. {
  147. int ec;
  148. int cNibble = 2;
  149. BYTE ch;
  150. BSTATUS bsStatus;
  151. while ((ec = GetByte(&ch)) == ecOK)
  152. {
  153. if (m_cGroup < 0)
  154. return ecStackUnderflow;
  155. // check if search specific keyword
  156. if (m_sKeyword.wStatus & KW_ENABLE) {
  157. if (m_sKeyword.wStatus & KW_FOUND) {
  158. ReleaseRtfState();
  159. break;
  160. }
  161. }
  162. // set buf status
  163. bsStatus = bsDefault;
  164. if (m_ris == risBin) // if we're parsing binary data, handle it directly
  165. {
  166. // fall through
  167. }
  168. else
  169. {
  170. switch (ch)
  171. {
  172. case '{':
  173. if ((ec = PushRtfState()) != ecOK)
  174. return ec;
  175. break;
  176. case '}':
  177. if ((ec = PopRtfState()) != ecOK)
  178. return ec;
  179. break;
  180. case '\\':
  181. if ((ec = ParseRtfKeyword()) != ecOK)
  182. return ec;
  183. continue; // all keyword is processed in ParseRtfKeyword
  184. case 0x0d:
  185. case 0x0a: // cr and lf are noise characters...
  186. break;
  187. default:
  188. if (m_ris == risNorm )
  189. {
  190. bsStatus = bsText;
  191. } else if (m_ris == risHex)
  192. {
  193. cNibble--;
  194. if (!cNibble) {
  195. cNibble = 2;
  196. m_ris = risNorm;
  197. }
  198. bsStatus = bsHex;
  199. } else {
  200. return ecAssertion;
  201. }
  202. break;
  203. } // switch
  204. } // else (ris != risBin)
  205. if ((ec = ParseChar(ch, bsStatus)) != ecOK)
  206. return ec;
  207. } // while
  208. if (m_cGroup < 0)
  209. return ecStackUnderflow;
  210. if (m_cGroup > 0)
  211. return ecUnmatchedBrace;
  212. return ecOK;
  213. }
  214. //
  215. // PushRtfState
  216. //
  217. // Save relevant info on a linked list of SAVE structures.
  218. //
  219. int
  220. CRtfParser::PushRtfState(void)
  221. {
  222. SAVE *psaveNew = new SAVE;
  223. if (!psaveNew)
  224. return ecStackOverflow;
  225. psaveNew -> pNext = m_psave;
  226. psaveNew -> rds = m_rds;
  227. psaveNew -> ris = m_ris;
  228. m_ris = risNorm;
  229. // do not save rds, rds status spread to sub destination until this destination
  230. // terminated
  231. m_psave = psaveNew;
  232. m_cGroup++;
  233. return ecOK;
  234. }
  235. //
  236. // PopRtfState
  237. //
  238. // If we're ending a destination (that is, the destination is changing),
  239. // call ecEndGroupAction.
  240. // Always restore relevant info from the top of the SAVE list.
  241. //
  242. int
  243. CRtfParser::PopRtfState(void)
  244. {
  245. SAVE *psaveOld;
  246. if (!m_psave)
  247. return ecStackUnderflow;
  248. if (m_rds != m_psave->rds)
  249. { // todo:
  250. // if ((ec = EndGroupAction(rds)) != ecOK)
  251. // return ec;
  252. }
  253. m_rds = m_psave->rds;
  254. m_ris = m_psave->ris;
  255. psaveOld = m_psave;
  256. m_psave = m_psave->pNext;
  257. m_cGroup--;
  258. delete psaveOld;
  259. return ecOK;
  260. }
  261. //
  262. // ReleaseRtfState
  263. // when find specific keyword and want to abort the parser abnormally
  264. // call this function to flash the state stack
  265. //
  266. int CRtfParser::ReleaseRtfState(void)
  267. {
  268. SAVE *psaveOld;
  269. while(psaveOld = m_psave)
  270. {
  271. assert(m_cGroup);
  272. m_psave = m_psave->pNext;
  273. m_cGroup--;
  274. delete psaveOld;
  275. }
  276. return ecOK;
  277. }
  278. //
  279. // ParseChar
  280. //
  281. // Route the character to the appropriate destination stream.
  282. //
  283. int
  284. CRtfParser::ParseChar(BYTE ch, BSTATUS bsStatus)
  285. {
  286. int ec;
  287. if (m_ris == risBin && --m_cbBin <= 0)
  288. m_ris = risNorm;
  289. switch (m_rds)
  290. {
  291. case rdsSkip:
  292. // Toss this character.
  293. bsStatus = bsDefault;
  294. break;
  295. case rdsNorm:
  296. // Output a character. Properties are valid at this point.
  297. break;
  298. default:
  299. // handle other destinations....
  300. break;
  301. }
  302. // set status, trigger the conversion if any
  303. if ((ec = SetStatus(bsStatus)) != ecOK) {
  304. return ec;
  305. }
  306. // save the char
  307. if ((ec = SaveByte(ch)) != ecOK) {
  308. return ec;
  309. }
  310. return ec;
  311. }
  312. //
  313. // ParseRtfKeyword
  314. //
  315. // get a control word (and its associated value) and
  316. // call TranslateKeyword to dispatch the control.
  317. //
  318. int
  319. CRtfParser::ParseRtfKeyword()
  320. {
  321. BOOL fNeg = FALSE;
  322. char *pch;
  323. char szKeyword[30];
  324. char szParameter[20];
  325. BYTE ch;
  326. szKeyword[0] = '\0';
  327. szParameter[0] = '\0';
  328. if (GetByte(&ch) != ecOK)
  329. return ecEndOfFile;
  330. if (!isalpha(ch)) // a control symbol; no delimiter.
  331. {
  332. szKeyword[0] = (char) ch;
  333. szKeyword[1] = '\0';
  334. return TranslateKeyword(szKeyword, szParameter);
  335. }
  336. for (pch = szKeyword; isalpha(ch); GetByte(&ch))
  337. *pch++ = (char) ch;
  338. *pch = '\0';
  339. if (ch == '-')
  340. {
  341. fNeg = TRUE;
  342. if (GetByte(&ch) != ecOK)
  343. return ecEndOfFile;
  344. }
  345. if (isdigit(ch))
  346. {
  347. pch = szParameter;
  348. if (fNeg) *pch++ = '-';
  349. for (; isdigit(ch); GetByte(&ch))
  350. *pch++ = (char) ch;
  351. *pch = '\0';
  352. }
  353. if (ch != ' ') {
  354. unGetByte(ch);
  355. } else {
  356. strcat(szParameter, " "); // append the space to keyword
  357. }
  358. return TranslateKeyword(szKeyword, szParameter);
  359. }
  360. //
  361. // TranslateKeyword.
  362. // Inputs:
  363. // szKeyword: The RTF control to evaluate.
  364. int
  365. CRtfParser::TranslateKeyword(char *szKeyword, char* szParameter)
  366. {
  367. BSTATUS bsStatus;
  368. int isym;
  369. int ec;
  370. BYTE ch;
  371. // check specific keyword first
  372. if (m_sKeyword.wStatus & KW_ENABLE)
  373. {
  374. if (strcmp(szKeyword, m_sKeyword.szKeyword) == 0)
  375. {
  376. strcpy(m_sKeyword.szParameter, szParameter);
  377. if (szParameter[0] != '\0' && szParameter[0] != ' ')
  378. m_sKeyword.wStatus |= KW_PARAM;
  379. m_sKeyword.wStatus |= KW_FOUND;
  380. return ecOK;
  381. }
  382. }
  383. // search for szKeyword in rgsymRtf
  384. for (isym = 0; isym < g_iSymMax; isym++) {
  385. if (strcmp(szKeyword, g_rgSymRtf[isym].szKeyword) == 0)
  386. break;
  387. }
  388. if (isym == g_iSymMax) // control word not found
  389. {
  390. if (m_fSkipDestIfUnk) // if this is a new destination
  391. m_rds = rdsSkip; // skip the destination
  392. // else just discard it
  393. m_fSkipDestIfUnk = FALSE;
  394. ec = ecOK;
  395. goto gotoExit;
  396. }
  397. // found it! use kwd and idx to determine what to do with it.
  398. m_fSkipDestIfUnk = FALSE;
  399. switch (g_rgSymRtf[isym].kwd)
  400. {
  401. case kwdChar:
  402. break;
  403. case kwdDest:
  404. ec = ChangeDest((IDEST)g_rgSymRtf[isym].idx);
  405. break;
  406. case kwdSpec:
  407. ec = ParseSpecialKeyword((IPFN)g_rgSymRtf[isym].idx, szParameter);
  408. break;
  409. default:
  410. ec = ecBadTable;
  411. }
  412. gotoExit:
  413. // save keyword and parameter
  414. if (m_ris == risHex) {
  415. bsStatus = bsHex;
  416. } else {
  417. bsStatus =bsDefault;
  418. }
  419. ParseChar('\\', bsStatus);
  420. while (ch = *szKeyword++) ParseChar(ch, bsStatus);
  421. while (ch = *szParameter++) ParseChar(ch, bsStatus);
  422. return ec;
  423. }
  424. //
  425. // ParseSpecialKeyword
  426. //
  427. // Evaluate an RTF control that needs special processing.
  428. //
  429. int
  430. CRtfParser::ParseSpecialKeyword(IPFN ipfn, char* szParameter)
  431. {
  432. if (m_rds == rdsSkip && ipfn != ipfnBin) // if we're skipping, and it's not
  433. return ecOK; // the \bin keyword, ignore it.
  434. switch (ipfn)
  435. {
  436. case ipfnBin:
  437. m_ris = risBin;
  438. m_cbBin = atol(szParameter);
  439. break;
  440. case ipfnSkipDest:
  441. m_fSkipDestIfUnk = TRUE;
  442. break;
  443. case ipfnHex:
  444. m_ris = risHex;
  445. break;
  446. default:
  447. return ecBadTable;
  448. }
  449. return ecOK;
  450. }
  451. //
  452. // ChangeDest
  453. //
  454. // Change to the destination specified by idest.
  455. // There's usually more to do here than this...
  456. //
  457. int
  458. CRtfParser::ChangeDest(IDEST idest)
  459. {
  460. if (m_rds == rdsSkip) // if we're skipping text,
  461. return ecOK; // don't do anything
  462. switch (idest)
  463. {
  464. case idestPict:
  465. case idestSkip:
  466. default:
  467. m_rds = rdsSkip; // when in doubt, skip it...
  468. break;
  469. }
  470. return ecOK;
  471. }
  472. //
  473. // GetByte
  474. //
  475. // Get one char from input buffer
  476. //
  477. int
  478. CRtfParser::GetByte(BYTE* pch)
  479. {
  480. if (m_uCursor >= m_cchInput) {
  481. return ecEndOfFile;
  482. }
  483. *pch = *(m_pchInput + m_uCursor);
  484. m_uCursor ++;
  485. return ecOK;
  486. }
  487. //
  488. // unGetByte
  489. //
  490. // adjust the cursor, return one char
  491. //
  492. int
  493. CRtfParser::unGetByte(BYTE ch)
  494. {
  495. if (m_uCursor) {
  496. m_uCursor--;
  497. }
  498. return ecOK;
  499. }
  500. //
  501. // SaveByte
  502. //
  503. // Save one char to output buffer
  504. //
  505. int
  506. CRtfParser::SaveByte(BYTE ch)
  507. {
  508. if (m_uOutPos >= m_cchOutput) {
  509. return ecBufTooSmall;
  510. }
  511. *(m_pchOutput + m_uOutPos) = ch;
  512. m_uOutPos++; // output buffer ++
  513. m_cchConvLen++; // mapping range also ++
  514. return ecOK;
  515. }
  516. //
  517. // SetStatus
  518. //
  519. // set the buffer status, if buffer status changed then start convert
  520. //
  521. int
  522. CRtfParser::SetStatus(BSTATUS bsStatus)
  523. {
  524. PBYTE pchDBCS, pchWCHAR, pchUniDes;
  525. UINT i, cchLen;
  526. assert(m_uOutPos == m_uConvStart + m_cchConvLen);
  527. if (bsStatus != m_bsStatus)
  528. {
  529. switch(m_bsStatus)
  530. {
  531. case bsDefault:
  532. // control symbol, keyword, group char...
  533. break;
  534. case bsText:
  535. // here we got Ansi text
  536. // we do not do conversion for ansi text
  537. /*
  538. pchWCHAR = new BYTE[m_cchConvLen*2 + 8];
  539. if (!pchWCHAR) return ecOutOfMemory;
  540. MapFunc(m_pchOutput + m_uConvStart, m_cchConvLen,
  541. pchWCHAR, &cchLen);
  542. // replace old buffer with mapped buffer
  543. for (i=0; i<cchLen; i++, m_uConvStart++) {
  544. *(m_pchOutput + m_uConvStart) = *(pchWCHAR + i);
  545. }
  546. // set new output buffer position
  547. m_uOutPos = m_uConvStart;
  548. //
  549. delete [] pchWCHAR;
  550. */
  551. break;
  552. case bsHex:
  553. // when we are here,
  554. // the rtf contains DBCS chars like "\'xx\'xx"
  555. // we only need to do DBCS->Unicode conversion, since we can not get
  556. // \upr keyword here (\upr is skipped, see keyword table)
  557. // so the MapFunc can be only (ANSI->Unicode) converter
  558. // we will map DBCS string "\'xx\'xx" to
  559. // "{\upr{"\'xx\'xx"}{\*\ud{\uc0 "Unicode string"}}}
  560. // in which Unicode string is like this:
  561. // \u12345\u-12345....
  562. // rtf treat unicode value as signed 16-bit decimal
  563. // so we don't distinquish 16-bit or 32-bit wide char, all
  564. // processed as 2-byte WCHAR
  565. if (m_cchConvLen == 0) {
  566. break;
  567. }
  568. pchDBCS = new BYTE[m_cchConvLen * 3 + 8];
  569. if (!pchDBCS) return ecOutOfMemory;
  570. pchWCHAR = pchDBCS + m_cchConvLen;
  571. // length: pchDBCS = m_cchConvLen
  572. // pchWCHAR = m_cchConvLen * 2 + 8
  573. // map Hex string to DBCS string
  574. // return cchLen in Byte
  575. Hex2Char(m_pchOutput + m_uConvStart, m_cchConvLen, pchDBCS, m_cchConvLen, &cchLen);
  576. // map DBCS string to Unicode string
  577. // return cchLen in WCHAR
  578. cchLen = AnsiStrToUnicodeStr(pchDBCS, cchLen, (PWCH)pchWCHAR, cchLen+4);
  579. // MapFunc(pchDBCS, cchLen, pchWCHAR, &cchLen);
  580. // allocate a buffer for unicode destination
  581. // since one WCHAR map to max \u-xxxxx, that's 8 bytes
  582. // adding other 20 bytes for surrounding keywords and group chars
  583. // adding DBCS strings
  584. pchUniDes = new BYTE[cchLen * 8 + 32 + m_cchConvLen];
  585. if (!pchUniDes) {
  586. delete [] pchDBCS;
  587. return ecOutOfMemory;
  588. }
  589. // map to unicode destination
  590. GetUnicodeDestination(pchUniDes, (LPWSTR)pchWCHAR, cchLen, &cchLen);
  591. // replace old hex with new hex
  592. for (i=0; i<cchLen; i++, m_uConvStart++) {
  593. *(m_pchOutput + m_uConvStart) = *(pchUniDes + i);
  594. }
  595. // set new output position
  596. m_uOutPos = m_uConvStart;
  597. //
  598. delete [] pchDBCS;
  599. delete [] pchUniDes;
  600. break;
  601. default:
  602. assert(0);
  603. return ecAssertion;
  604. }
  605. // clean map buffer
  606. m_uConvStart = m_uOutPos;
  607. m_cchConvLen = 0;
  608. // set status
  609. m_bsStatus = bsStatus;
  610. }
  611. return ecOK;
  612. }
  613. //
  614. // Hex2Char
  615. //
  616. // convert hex string to char string
  617. //
  618. int
  619. CRtfParser::Hex2Char(BYTE* pchSrc, UINT cchSrc, BYTE* pchDes, UINT cchDes, UINT* pcchLen)
  620. {
  621. BYTE* pchTmp = pchDes;
  622. BYTE ch;
  623. BYTE b = 0;
  624. BYTE cNibble = 2;
  625. // should be \'xx\'xx\'xx
  626. assert (cchSrc % 4 == 0);
  627. *pcchLen = 0;
  628. if (cchDes < cchSrc/4) {
  629. goto gotoError;
  630. }
  631. while (cchSrc--)
  632. {
  633. ch = *pchSrc++;
  634. if (ch == '\\') {
  635. if (*pchSrc != '\'') {
  636. goto gotoError;
  637. }
  638. } else if (ch == '\'') {
  639. }
  640. else
  641. {
  642. b = b << 4;
  643. if (isdigit(ch))
  644. b += (char) ch - '0';
  645. else
  646. {
  647. if (islower(ch))
  648. {
  649. if (ch < 'a' || ch > 'f')
  650. goto gotoError;
  651. b += (char) ch - 'a' + 10;
  652. }
  653. else
  654. {
  655. if (ch < 'A' || ch > 'F')
  656. goto gotoError;
  657. b += (char) ch - 'A' + 10;
  658. }
  659. }
  660. cNibble--;
  661. if (!cNibble)
  662. {
  663. *pchDes++ = b;
  664. cNibble = 2;
  665. b = 0;
  666. }
  667. }
  668. }
  669. *pcchLen = (UINT)(pchDes - pchTmp);
  670. return ecOK;
  671. gotoError:
  672. assert(0);
  673. return ecInvalidHex;
  674. }
  675. #define LONIBBLE(c) (c&0x0f)
  676. #define HINIBBLE(c) ((c&0xf0)>>4)
  677. //
  678. // Char2Hex
  679. //
  680. // convert char string to hex string
  681. //
  682. int
  683. CRtfParser::Char2Hex(BYTE* pchSrc, UINT cchSrc, BYTE* pchDes, UINT cchDes, UINT* pcchLen)
  684. {
  685. BYTE* pchTmp = pchDes;
  686. BYTE ch,c;
  687. *pcchLen = 0;
  688. if (cchDes < cchSrc * 4) {
  689. goto gotoError;
  690. }
  691. while(cchSrc--)
  692. {
  693. *pchDes++ = '\\';
  694. *pchDes++ = '\'';
  695. ch = *pchSrc++;
  696. c = HINIBBLE(ch);
  697. if(c>9 && c<=0xF) {
  698. c += 'a'-10;
  699. } else if (c<=9) {
  700. c += '0';
  701. } else {
  702. goto gotoError;
  703. }
  704. *pchDes++ = c;
  705. c = LONIBBLE(ch);
  706. if(c>9 && c<=0xF) {
  707. c += 'a'-10;
  708. } else if (c<=9) {
  709. c += '0';
  710. } else {
  711. goto gotoError;
  712. }
  713. *pchDes++ = c;
  714. }
  715. *pcchLen = (UINT)(pchDes - pchTmp);
  716. return ecOK;
  717. gotoError:
  718. assert(0);
  719. return ecInvalidHex;
  720. }
  721. //
  722. // GetUnicodeDestination
  723. //
  724. // convert unicode string to unicode destination in RTF
  725. // the format is:
  726. // "{\upr{\'xx\'xx}{\*\ud{\uc0 \u12345\u-12345}}
  727. //
  728. int
  729. CRtfParser::GetUnicodeDestination(BYTE* pchUniDes, LPWSTR pwchStr, UINT wchLen, UINT* pcchLen)
  730. {
  731. static char pch1[] = "{\\upr{";
  732. static char pch2[] = "}{\\*\\ud{\\uc0 ";
  733. static char pch3[] = "}}}";
  734. UINT cchLen, cchDone;
  735. // copy \upr
  736. cchLen = strlen(pch1);
  737. memcpy(pchUniDes, pch1, cchLen);
  738. // copy DBCS string
  739. memcpy(pchUniDes + cchLen, m_pchOutput+m_uConvStart, m_cchConvLen);
  740. cchLen += m_cchConvLen;
  741. // copy middle part
  742. memcpy(pchUniDes + cchLen, pch2, strlen(pch2));
  743. cchLen += strlen(pch2);
  744. // copy unicode string
  745. for (UINT i=0; i<wchLen; i++)
  746. {
  747. WideCharToKeyword(pwchStr[i], pchUniDes + cchLen, &cchDone);
  748. cchLen += cchDone;
  749. }
  750. // copy last part
  751. memcpy(pchUniDes + cchLen, pch3, strlen(pch3));
  752. cchLen += strlen(pch3);
  753. // return
  754. *pcchLen = cchLen;
  755. return ecOK;
  756. }
  757. //
  758. // WideCharToKeyword
  759. //
  760. // map one wide char to \u keyword
  761. //
  762. int
  763. CRtfParser::WideCharToKeyword(WCHAR wch, BYTE* pchDes, UINT* pcchLen)
  764. {
  765. short num = (short) wch;
  766. char* pch = (char*) pchDes;
  767. sprintf(pch,"\\u%d", num);
  768. *pcchLen = strlen(pch);
  769. return ecOK;
  770. }