rtfpars.pp 27 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081
  1. Unit RTFPars;
  2. {
  3. $Id$
  4. This file is part of the Free Pascal run time library.
  5. Copyright (c) 1999-2000 by Michael Van Canneyt, Member of the
  6. Free Pascal development team
  7. This unit implements a RTF Parser.
  8. See the file COPYING.FPC, included in this distribution,
  9. for details about the copyright.
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  13. **********************************************************************}
  14. {$mode objfpc}
  15. interface
  16. Uses classes,sysutils;
  17. {$i rtfdata.inc}
  18. type Trtferrorhandler = Procedure (s : string) of object;
  19. TRTFParser = class(TObject)
  20. private
  21. FOnRTFError : TRTFerrorHandler;
  22. FfontList : PRTFFont;
  23. FcolorList : PRTFColor;
  24. FstyleList : PRTFStyle;
  25. FrtfClass,
  26. FrtfMajor,
  27. FrtfMinor,
  28. FrtfParam : Integer;
  29. rtfTextBuf : string [rtfBufSiz];
  30. rtfTextLen : Integer;
  31. pushedChar : Integer; { pushback char if read too far }
  32. pushedClass : Integer; { pushed token info for RTFUngetToken() }
  33. pushedMajor,
  34. pushedMinor,
  35. pushedParam : Integer;
  36. pushedTextBuf : String[rtfBufSiz];
  37. FStream : TStream;
  38. ccb : array [0..rtfMaxClass] of TRTFFuncPtr; { class callbacks }
  39. dcb : array [0..rtfMaxDestination] of TRTFFuncPtr; { destination callbacks }
  40. readHook : TRTFFUNCPTR;
  41. Procedure Error (msg : String);
  42. Procedure LookupInit ;
  43. Procedure ReadFontTbl ;
  44. Procedure ReadColorTbl;
  45. Procedure ReadStyleSheet ;
  46. Procedure ReadInfoGroup ;
  47. Procedure ReadPictGroup ;
  48. Function CheckCM (Aclass, major: Integer) : Boolean;
  49. Function CheckCMM (Aclass, major, minor : Integer) : Boolean;
  50. Function CheckMM (major, minor : Integer) : Boolean;
  51. Procedure Real_RTFGetToken;
  52. Function GetChar : Integer;
  53. Procedure Lookup (S : String);
  54. Function GetFont (num : Integer) : PRTFFont;
  55. Function GetColor (num : Integer) : PRTFColor;
  56. Function GetStyle (num : Integer) : PRTFStyle;
  57. Procedure setClassCallback (Aclass : Integer; Acallback : TRTFFuncPtr);
  58. Function GetClassCallback (Aclass : Integer) : TRTFFuncPtr;
  59. Procedure SetDestinationCallback (ADestination : Integer; Acallback : TRTFFuncPtr);
  60. Function GetDestinationCallback (Adestination : Integer) : TRTFFuncPtr ;
  61. Procedure SetStream (Astream : TStream);
  62. public
  63. Constructor Create (AStream : TStream);
  64. Destructor Destroy; override;
  65. Procedure GetReadHook (Var q : TRTFFuncPtr);
  66. Function GetToken : Integer;
  67. Function PeekToken : Integer;
  68. Procedure ResetParser;
  69. Procedure RouteToken;
  70. Procedure SkipGroup;
  71. Procedure StartReading;
  72. Procedure SetReadHook (Hook : TRTFFuncPtr);
  73. Procedure UngetToken;
  74. Procedure SetToken (Aclass, major, minor, param : Integer; text : string);
  75. Procedure ExpandStyle (n : Integer);
  76. { Properties }
  77. Property Colors [Index : Integer]: PRTFColor Read GetColor;
  78. Property ClassCallBacks [AClass : Integer]: TRTFFuncptr
  79. Read GetClassCallBack
  80. Write SetClassCallback;
  81. Property DestinationCallBacks [Adestination : Integer]: TRTFFuncptr
  82. Read GetdestinationCallBack
  83. Write SetdestinationCallback;
  84. Property Fonts [Index : Integer]: PRTFFont Read GetFont;
  85. Property OnRTFError : TRTFerrorHandler Read FOnRTFError Write FOnRTFError;
  86. Property rtfClass : Integer Read FrtfClass;
  87. Property rtfMajor : Integer Read FrtfMajor;
  88. Property rtfMinor : Integer Read FrtfMinor;
  89. Property rtfParam : Integer Read FrtfParam;
  90. Property Stream : TStream Read FStream Write SetStream;
  91. Property Styles [index : Integer] : PRTFStyle Read GetStyle;
  92. end;
  93. Implementation
  94. Const EOF = -255;
  95. { ---------------------------------------------------------------------
  96. Utility functions
  97. ---------------------------------------------------------------------}
  98. Function Hash (s : String) : Integer;
  99. var
  100. val,i : integer;
  101. Begin
  102. val:=0;
  103. for i:=1 to length(s) do
  104. val:=val+ord(s[i]);
  105. Hash:=val;
  106. End;
  107. Function isalpha (s : integer) : Boolean;
  108. begin
  109. result:= ( (s>=ord('A')) and (s<=ord('Z')))
  110. or (((s>=ord('a')) and ((s<=ord('z')) ))
  111. );
  112. end;
  113. Function isdigit (s : integer) : Boolean;
  114. begin
  115. result:= ( (s>=ord('0')) and (s<=ord('9')) )
  116. end;
  117. Function HexVal (c : Integer) : Integer;
  118. Begin
  119. if (c>=ord('A')) and (C<=ord('Z')) then inc (c,32);
  120. if c<ord ('A') then
  121. result:=(c - ord('0')) { '0'..'9' }
  122. else
  123. result:= (c - ord('a') + 10); { 'a'..'f' }
  124. End;
  125. { ---------------------------------------------------------------------
  126. Initialize the reader. This may be called multiple times,
  127. to read multiple files. The only thing not reset is the input
  128. stream; that must be done with RTFSetStream().
  129. ---------------------------------------------------------------------}
  130. Constructor TRTFParser.Create (Astream : TStream);
  131. Begin
  132. inherited create;
  133. { initialize lookup table }
  134. LookupInit ;
  135. Fstream := Astream;
  136. FfontList :=nil;
  137. FcolorList :=nil;
  138. FstyleList :=nil;
  139. onrtferror:=nil;
  140. ResetParser;
  141. end;
  142. Procedure TRTFParser.ResetParser;
  143. var
  144. cp : PRTFColor;
  145. fp : PRTFFont;
  146. sp : PRTFStyle;
  147. ep,eltlist : PRTFStyleElt;
  148. i : integer;
  149. begin
  150. for i:=0 to rtfMaxClass-1 do
  151. setClassCallback (i, Nil);
  152. for i:=0 to rtfMaxDestination-1 do
  153. SetDestinationCallback (i,nil);
  154. { install built-in destination readers }
  155. SetDestinationCallback (rtfFontTbl, @ReadFontTbl);
  156. SetDestinationCallback (rtfColorTbl, @ReadColorTbl);
  157. SetDestinationCallback (rtfStyleSheet, @ReadStyleSheet);
  158. SetDestinationCallback (rtfInfo, @ReadInfoGroup);
  159. SetDestinationCallback (rtfPict, @ReadPictGroup);
  160. SetReadHook (Nil);
  161. { dump old lists if necessary }
  162. while FfontList<>nil do
  163. Begin
  164. fp := FfontList^.rtfNextFont;
  165. dispose (FfontList);
  166. FfontList := fp;
  167. End;
  168. while FcolorList<>nil do
  169. Begin
  170. cp := FcolorList^.rtfNextColor;
  171. dispose (FcolorList);
  172. FcolorList := cp;
  173. End;
  174. while FstyleList<>nil do
  175. Begin
  176. sp := FstyleList^.rtfNextStyle;
  177. eltList := FstyleList^.rtfSSEList;
  178. while eltList<>nil do
  179. Begin
  180. ep:=eltList^.rtfNextSE;
  181. dispose(eltList);
  182. eltList:= ep;
  183. End;
  184. Dispose (FstyleList);
  185. FstyleList := sp;
  186. End;
  187. FrtfClass := -1;
  188. pushedClass := -1;
  189. pushedChar := EOF;
  190. { Reset the stream if it is assigned }
  191. if assigned (FStream) then
  192. FStream.seek(0,soFromBeginning);
  193. End;
  194. Destructor TRTFParser.Destroy;
  195. var
  196. cp : PRTFColor;
  197. fp : PRTFFont;
  198. sp : PRTFStyle;
  199. ep,eltlist : PRTFStyleElt;
  200. begin
  201. { Dump the lists. }
  202. while FfontList<>nil do
  203. Begin
  204. fp := FfontList^.rtfNextFont;
  205. dispose (FfontList);
  206. FfontList := fp;
  207. End;
  208. while FcolorList<>nil do
  209. Begin
  210. cp := FcolorList^.rtfNextColor;
  211. dispose (FcolorList);
  212. FcolorList := cp;
  213. End;
  214. while FstyleList<>nil do
  215. Begin
  216. sp := FstyleList^.rtfNextStyle;
  217. eltList := FstyleList^.rtfSSEList;
  218. while eltList<>nil do
  219. Begin
  220. ep:=eltList^.rtfNextSE;
  221. dispose(eltList);
  222. eltList:= ep;
  223. End;
  224. Dispose (FstyleList);
  225. FstyleList := sp;
  226. End;
  227. { Dump rest }
  228. inherited destroy;
  229. end;
  230. { ---------------------------------------------------------------------
  231. Callback table manipulation routines
  232. ---------------------------------------------------------------------}
  233. Procedure TRTFParser.SetClassCallback (Aclass : Integer; Acallback : TRTFFuncPtr);
  234. Begin
  235. if (aclass>=0) and (Aclass<rtfMaxClass) then
  236. ccb[Aclass]:= Acallback;
  237. End;
  238. Function TRTFParser.GetClassCallback (Aclass : Integer) : TRTFFuncPtr;
  239. Begin
  240. if (Aclass>=0) and (Aclass<rtfMaxClass) then
  241. GetClassCallback :=ccb[Aclass]
  242. else
  243. GetClassCallback:=nil;
  244. End;
  245. { ---------------------------------------------------------------------
  246. Install or return a writer callback for a destination type
  247. ---------------------------------------------------------------------}
  248. Procedure TRTFParser.SetDestinationCallback (ADestination : Integer; Acallback : TRTFFuncPtr);
  249. Begin
  250. if (Adestination>=0) and (Adestination<rtfMaxDestination) then
  251. dcb[ADestination] := Acallback;
  252. End;
  253. Function TRTFParser.GetDestinationCallback (Adestination : Integer) : TRTFFuncPtr ;
  254. Begin
  255. if (Adestination>=0) and (ADestination<rtfMaxDestination) then
  256. Result:=dcb[Adestination]
  257. Else
  258. Result:=nil;
  259. End;
  260. { ---------------------------------------------------------------------
  261. Token reading routines
  262. ---------------------------------------------------------------------}
  263. { Read the input stream, invoking the writer's callbacks where appropriate. }
  264. Procedure TRTFParser.StartReading;
  265. Begin
  266. { Reset stream. }
  267. FStream.Seek (0,soFromBeginning);
  268. { Start reading. }
  269. while (GetToken<>rtfEOF) do
  270. RouteToken;
  271. End;
  272. { Route a token. If it's a destination for which a reader is
  273. installed, process the destination internally, otherwise
  274. pass the token to the writer's class callback. }
  275. Procedure TRTFParser.RouteToken;
  276. Var
  277. p : TRTFFuncPtr;
  278. Begin
  279. if (rtfClass < 0) or (rtfClass>=rtfMaxClass) then
  280. Error ('No such class : '+rtfTextBuf)
  281. else
  282. begin
  283. if (CheckCM (rtfControl, rtfDestination)) then
  284. Begin
  285. { invoke destination-specific callback if there is one }
  286. p:=GetDestinationCallback (rtfMinor);
  287. if assigned(p) then
  288. Begin
  289. p;
  290. exit
  291. End;
  292. End;
  293. { invoke class callback if there is one }
  294. p:= GetClassCallback (rtfClass);
  295. if assigned(p) then
  296. p;
  297. end;
  298. End;
  299. { Skip to the end of the current group. When this returns,
  300. writers that maintain a state stack may want to call their
  301. state unstacker; global vars will still be set to the group's
  302. closing brace. }
  303. Procedure TRTFParser.SkipGroup;
  304. Var
  305. level : Integer;
  306. Begin
  307. level:= 1;
  308. while (GetToken<>rtfEOF) do
  309. if (rtfClass=rtfGroup) then
  310. Begin
  311. if (rtfMajor=rtfBeginGroup) then
  312. inc(level)
  313. else if (rtfMajor=rtfEndGroup) then
  314. Begin
  315. dec(level);
  316. if (level < 1) then
  317. exit; { end of initial group }
  318. End;
  319. End;
  320. End;
  321. { Read one token. Call the read hook if there is one. The
  322. token class is the return value. Returns rtfEOF when there
  323. are no more tokens. }
  324. Function TRTFParser.GetToken : Integer;
  325. var p : TRTFFuncPTR;
  326. Begin
  327. GetReadHook (p);
  328. while true do
  329. Begin
  330. Real_RTFGetToken;
  331. if (assigned(p)) then
  332. p; { give read hook a look at token }
  333. { Silently discard newlines and carriage returns. }
  334. if not ((rtfClass=rtfText) and ((rtfMajor=13) or (rtfmajor=10))) then
  335. break;
  336. End;
  337. result:=rtfClass;
  338. End;
  339. { ---------------------------------------------------------------------
  340. Install or return a token reader hook.
  341. ---------------------------------------------------------------------}
  342. Procedure TRTFParser.SetReadHook (Hook : TRTFFuncPtr);
  343. Begin
  344. readHook := Hook;
  345. End;
  346. Procedure TRTFParser.GetReadHook (Var q : TRTFFuncPtr);
  347. Begin
  348. Q:=readHook;
  349. End;
  350. Procedure TRTFParser.UngetToken;
  351. Begin
  352. if (pushedClass >= 0) then { there's already an ungotten token }
  353. Error ('cannot unget two tokens');
  354. if (rtfClass < 0) then
  355. Error ('no token to unget');
  356. pushedClass := rtfClass;
  357. pushedMajor := rtfMajor;
  358. pushedMinor := rtfMinor;
  359. pushedParam := rtfParam;
  360. rtfTextBuf := pushedTextBuf;
  361. End;
  362. Function TRTFParser.PeekToken : Integer;
  363. Begin
  364. Real_RTFGetToken;
  365. UngetToken;
  366. Result:=rtfClass;
  367. End;
  368. Procedure TRTFParser.Real_RTFGetToken;
  369. var sign,c,c2 : Integer;
  370. Begin
  371. { check for pushed token from RTFUngetToken() }
  372. if (pushedClass >= 0) then
  373. Begin
  374. FrtfClass := pushedClass;
  375. FrtfMajor := pushedMajor;
  376. FrtfMinor := pushedMinor;
  377. FrtfParam := pushedParam;
  378. rtfTextBuf := pushedTextBuf;
  379. rtfTextLen := length (rtfTextBuf);
  380. pushedClass := -1;
  381. exit;
  382. End;
  383. { initialize token vars }
  384. FrtfClass := rtfUnknown;
  385. FrtfParam := rtfNoParam;
  386. rtfTextBuf := '';
  387. rtfTextLen := 0;
  388. { get first character, which may be a pushback from previous token }
  389. if (pushedChar <> EOF) then
  390. Begin
  391. c := pushedChar;
  392. rtfTextBuf:=rtfTextBuf+chr(c);
  393. inc(rtftextlen);
  394. pushedChar := EOF;
  395. End
  396. else
  397. begin
  398. c:=GetChar;
  399. if C=EOF then
  400. Begin
  401. FrtfClass := rtfEOF;
  402. exit;
  403. End;
  404. end;
  405. if c=ord('{') then
  406. Begin
  407. FrtfClass := rtfGroup;
  408. FrtfMajor := rtfBeginGroup;
  409. exit;
  410. End;
  411. if c=ord('}') then
  412. Begin
  413. FrtfClass := RTFGROUP;
  414. FrtfMajor := rtfEndGroup;
  415. exit;
  416. End;
  417. if c<>ord('\') then
  418. Begin
  419. { Two possibilities here:
  420. 1) ASCII 9, effectively like \tab control symbol
  421. 2) literal text char }
  422. if c=ord(#8) then { ASCII 9 }
  423. Begin
  424. FrtfClass := rtfControl;
  425. FrtfMajor := rtfSpecialChar;
  426. FrtfMinor := rtfTab;
  427. End
  428. else
  429. Begin
  430. FrtfClass := rtfText;
  431. FrtfMajor := c;
  432. End;
  433. exit;
  434. End;
  435. c:=getchar;
  436. if (c=EOF) then
  437. { early eof, whoops (class is rtfUnknown) }
  438. exit;
  439. if ( not isalpha (c)) then
  440. Begin
  441. { Three possibilities here:
  442. 1) hex encoded text char, e.g., \'d5, \'d3
  443. 2) special escaped text char, e.g., \, \;
  444. 3) control symbol, e.g., \_, \-, \|, \<10> }
  445. if c=ord('''') then { hex char }
  446. Begin
  447. c:=getchar;
  448. if (c<>EOF) then
  449. begin
  450. c2:=getchar;
  451. if (c2<>EOF) then
  452. Begin
  453. { should do isxdigit check! }
  454. FrtfClass := rtfText;
  455. FrtfMajor := HexVal (c) * 16 + HexVal (c2);
  456. exit;
  457. End;
  458. end;
  459. { early eof, whoops (class is rtfUnknown) }
  460. exit;
  461. End;
  462. if pos (chr(c),':{};\')<>0 then { escaped char }
  463. Begin
  464. FrtfClass := rtfText;
  465. FrtfMajor := c;
  466. exit;
  467. End;
  468. { control symbol }
  469. Lookup (rtfTextBuf); { sets class, major, minor }
  470. exit;
  471. End;
  472. { control word }
  473. while (isalpha (c)) do
  474. Begin
  475. c:=GetChar;
  476. if (c=EOF) then
  477. break;
  478. End;
  479. { At this point, the control word is all collected, so the
  480. major/minor numbers are determined before the parameter
  481. (if any) is scanned. There will be one too many characters
  482. in the buffer, though, so fix up before and restore after
  483. looking up. }
  484. if (c<>EOF) then
  485. delete(rtfTextBuf,length(rtfTextbuf),1);
  486. Lookup (rtfTextBuf); { sets class, major, minor }
  487. if (c <>EOF) then
  488. rtfTextBuf:=rtfTextBuf+chr(c);
  489. { Should be looking at first digit of parameter if there
  490. is one, unless it's negative. In that case, next char
  491. is '-', so need to gobble next char, and remember sign. }
  492. sign := 1;
  493. if c = ord('-') then
  494. Begin
  495. sign := -1;
  496. c := GetChar;
  497. End;
  498. if (c<>EOF) then
  499. if isdigit (c) then
  500. Begin
  501. FrtfParam := 0;
  502. while (isdigit (c)) do { gobble parameter }
  503. Begin
  504. FrtfParam := FrtfParam * 10 + c - ord('0');
  505. c:=GetChar;
  506. if (c=EOF) then
  507. break;
  508. End;
  509. FrtfParam:= sign*FrtfParam;
  510. End;
  511. { If control symbol delimiter was a blank, gobble it.
  512. Otherwise the character is first char of next token, so
  513. push it back for next call. In either case, delete the
  514. delimiter from the token buffer. }
  515. if (c<>EOF) then
  516. Begin
  517. if c<>ord (' ') then
  518. pushedChar := c;
  519. Delete (rtfTextBuf,rtfTextLen,1);
  520. Dec (rtfTextLen);
  521. End;
  522. End;
  523. Function TRTFParser.GetChar : Integer;
  524. var c : byte;
  525. Begin
  526. if FStream.read(c,1)<>0 then
  527. begin
  528. if (c and 128)=128 then c:=ord('?');
  529. Result:=c;
  530. rtfTextBuf:=rtfTextBuf+chr(c);
  531. inc(rtfTextLen);
  532. end
  533. else
  534. Result:=EOF;
  535. End;
  536. { Synthesize a token by setting the global variables to the
  537. values supplied. Typically this is followed with a call
  538. to RTFRouteToken().
  539. If param is non-negative, it becomes part of the token text. }
  540. Procedure TRTFParser.SetToken (Aclass, major, minor, param : Integer; text : string);
  541. Begin
  542. FrtfClass := Aclass;
  543. FrtfMajor := major;
  544. FrtfMinor := minor;
  545. FrtfParam := param;
  546. if (param=rtfNoParam) then
  547. rtfTextBuf:=text
  548. else
  549. rtfTextBuf:=text+IntTostr(param);
  550. rtfTextLen:=length(rtfTextBuf);
  551. End;
  552. { ---------------------------------------------------------------------
  553. Special destination readers. They gobble the destination so the
  554. writer doesn't have to deal with them. That's wrong for any
  555. translator that wants to process any of these itself. In that
  556. case, these readers should be overridden by installing a different
  557. destination callback.
  558. NOTE: The last token read by each of these reader will be the
  559. destination's terminating '', which will then be the current token.
  560. That 'End;' token is passed to RTFRouteToken() - the writer has already
  561. seen the 'Begin' that began the destination group, and may have pushed a
  562. state; it also needs to know at the end of the group that a state
  563. should be popped.
  564. It's important that rtfdata.inc and the control token lookup table list
  565. as many symbols as possible, because these readers unfortunately
  566. make strict assumptions about the input they expect, and a token
  567. of class rtfUnknown will throw them off easily.
  568. ----------------------------------------------------------------------}
  569. { Read Begin \fonttbl ... End; destination. Old font tables don't have
  570. braces around each table entry; try to adjust for that.}
  571. Procedure TRTFParser.ReadFontTbl;
  572. var
  573. fp : PRTFFont;
  574. bp : string[rtfbufsiz];
  575. old : Integer;
  576. Begin
  577. old := -1;
  578. While true do
  579. Begin
  580. GetToken;
  581. if CheckCM (rtfGroup, rtfEndGroup) then
  582. break;
  583. if (old < 0) then { first entry - determine tbl type }
  584. Begin
  585. if CheckCMM (rtfControl, rtfCharAttr, rtfFontNum) then
  586. old:=1 { no brace }
  587. else if CheckCM (rtfGroup, rtfBeginGroup) then
  588. old:= 0 { brace }
  589. else { can't tell! }
  590. Error ('FTErr - Cannot determine format')
  591. End;
  592. if (old=0) then { need to find "Begin" here }
  593. Begin
  594. if not CheckCM (rtfGroup, rtfBeginGroup) then
  595. Error ('FTErr - missing {');
  596. GetToken; { yes, skip to next token }
  597. End;
  598. new(fp);
  599. if (fp=nil) then
  600. Error ('FTErr - cannot allocate font entry');
  601. fp^.rtfNextFont:= FfontList;
  602. FfontList:=fp;
  603. if not CheckCMM (rtfControl, rtfCharAttr, rtfFontNum) then
  604. Error ('FTErr - missing font number');
  605. fp^.rtfFNum := rtfParam;
  606. { Read optionalcommands. Recognize only fontfamily}
  607. GetToken;
  608. if not CheckCM (rtfControl, rtfFontFamily) then
  609. error ('FTErr - missing font family ');
  610. fp^.rtfFFamily := rtfMinor;
  611. { Read optional commands/groups. Recognize none at this point..}
  612. GetToken;
  613. while (rtfclass=rtfcontrol) or ((rtfclass=rtfgroup) or (rtfclass=rtfunknown)) do
  614. begin
  615. if rtfclass=rtfgroup then SkipGroup;
  616. GetToken
  617. end;
  618. { Read font name }
  619. bp:='';
  620. while (rtfclass=rtfText) do
  621. Begin
  622. if rtfMajor=ord(';') then
  623. break;
  624. bp:=bp+chr(rtfMajor);
  625. GetToken
  626. End;
  627. if bp='' then
  628. Error ('FTErr - missing font name');
  629. fp^.rtffname:=bp;
  630. { Read alternate font}
  631. if (old=0) then { need to see "End;" here }
  632. Begin
  633. GetToken;
  634. if not CheckCM (rtfGroup, rtfEndGroup) then
  635. Error ('FTErr - missing }');
  636. End;
  637. End;
  638. RouteToken; { feed "End;" back to router }
  639. End;
  640. { The color table entries have color values of -1 if
  641. the default color should be used for the entry (only
  642. a semi-colon is given in the definition, no color values).
  643. There will be a problem if a partial entry (1 or 2 but
  644. not 3 color values) is given. The possibility is ignored
  645. here. }
  646. Procedure TRTFParser.ReadColorTbl;
  647. var
  648. cp : PRTFColor;
  649. cnum : Integer;
  650. Begin
  651. cnum:=0;
  652. While true do
  653. Begin
  654. GetToken;
  655. if CheckCM (rtfGroup, rtfEndGroup) then
  656. break;
  657. new(cp);
  658. if (cp=nil) then
  659. Error ('CTErr - cannot allocate color entry');
  660. cp^.rtfCNum :=cnum;
  661. cp^.rtfCRed :=-1;
  662. cp^.rtfCGreen:=-1;
  663. cp^.rtfCBlue :=-1;
  664. cp^.rtfNextColor := FColorList;
  665. inc(cnum);
  666. FcolorList:=cp;
  667. while true do
  668. Begin
  669. if not CheckCM (rtfControl, rtfColorName) then
  670. break;
  671. case rtfMinor of
  672. rtfRed: cp^.rtfCRed :=rtfParam;
  673. rtfGreen: cp^.rtfCGreen :=rtfParam;
  674. rtfBlue: cp^.rtfCBlue :=rtfParam;
  675. End;
  676. GetToken;
  677. End;
  678. if not CheckCM (rtfText, ord(';')) then
  679. Error ('CTErr - malformed entry');
  680. End;
  681. RouteToken; { feed "End;" back to router }
  682. End;
  683. { The "Normal" style definition doesn't contain any style number
  684. (why?), all others do. Normal style is given style 0. }
  685. Procedure TRTFParser.ReadStyleSheet;
  686. var
  687. sp : PRTFStyle;
  688. sep,sepLast : PRTFStyleElt;
  689. bp : string[rtfBufSiz];
  690. Begin
  691. While true do
  692. Begin
  693. GetToken;
  694. if CheckCM (rtfGroup, rtfEndGroup) then
  695. break;
  696. new (sp);
  697. if sp=nil then
  698. Error ('SSErr - cannot allocate stylesheet entry');
  699. sp^.rtfSNum := -1;
  700. sp^.rtfSBasedOn := rtfBasedOnNone;
  701. sp^.rtfSNextPar := -1;
  702. sp^.rtfSSEList := nil;
  703. sepLast:=nil;
  704. sp^.rtfNextStyle := FstyleList;
  705. sp^.rtfExpanding := 0;
  706. FstyleList := sp;
  707. if not CheckCM (rtfGroup, rtfBeginGroup) then
  708. Error ('SSErr - missing {');
  709. while GetToken=rtfControl do
  710. Begin
  711. if (CheckMM (rtfParAttr, rtfStyleNum)) then
  712. Begin
  713. sp^.rtfSNum:=rtfParam;
  714. break;
  715. End;
  716. if (CheckMM (rtfStyleAttr, rtfBasedOn)) then
  717. Begin
  718. sp^.rtfSBasedOn:=rtfParam;
  719. break;
  720. End;
  721. if (CheckMM (rtfStyleAttr, rtfNext)) then
  722. Begin
  723. sp^.rtfSNextPar:=rtfParam;
  724. break;
  725. End;
  726. new(sep);
  727. if sep=nil then
  728. Error ('SSErr - cannot allocate style element');
  729. sep^.rtfSEClass:=rtfClass;
  730. sep^.rtfSEMajor:=rtfMajor;
  731. sep^.rtfSEMinor:=rtfMinor;
  732. sep^.rtfSEParam:=rtfParam;
  733. sep^.rtfSEText:=rtfTextBuf;
  734. if sepLast=nil then
  735. sp^.rtfSSEList:=sep { first element }
  736. else { add to end }
  737. sepLast^.rtfNextSE:=sep;
  738. sep^.rtfNextSE:=nil;
  739. sepLast:=sep;
  740. End;
  741. if sp^.rtfSNextPar=-1 then { \snext not given }
  742. sp^.rtfSNextPar:=sp^.rtfSNum; { next is itself }
  743. if rtfClass<>rtfText then
  744. Error ('SSErr - missing style name');
  745. while rtfClass=rtfText do
  746. Begin
  747. if rtfMajor=ord(';') then
  748. Begin
  749. GetToken;
  750. break;
  751. End;
  752. bp:=bp+chr(rtfMajor);
  753. GetToken;
  754. End;
  755. if (sp^.rtfSNum < 0) then { no style number was specified }
  756. Begin { (only legal for Normal style) }
  757. if bp<>'Normal' then
  758. Error ('SSErr - missing style number');
  759. sp^.rtfSNum:=0;
  760. End;
  761. sp^.rtfSName:=bp;
  762. if not CheckCM (rtfGroup, rtfEndGroup) then
  763. Error ('SSErr - missing }');
  764. End;
  765. RouteToken; { feed "End;" back to router }
  766. End;
  767. Procedure TRTFParser.ReadInfoGroup;
  768. Begin
  769. SkipGroup ;
  770. RouteToken ; { feed "End;" back to router }
  771. End;
  772. Procedure TRTFParser.ReadPictGroup;
  773. Begin
  774. SkipGroup ;
  775. RouteToken ; { feed "End;" back to router }
  776. End;
  777. { ----------------------------------------------------------------------
  778. Routines to return pieces of stylesheet, or font or color tables
  779. ----------------------------------------------------------------------}
  780. Function TRTFParser.GetStyle (num : Integer) : PRTFStyle;
  781. var
  782. s : PRTFSTyle;
  783. Begin
  784. s:=Fstylelist;
  785. if num<>1 then
  786. while s<>nil do
  787. Begin
  788. if (s^.rtfSNum=num) then break;
  789. s:=s^.rtfNextStyle;
  790. End;
  791. result:=s; { NULL if not found }
  792. End;
  793. Function TRTFParser.GetFont (num : Integer) : PRTFFont;
  794. Var
  795. f :PRTFFont;
  796. Begin
  797. f:=FfontList;
  798. if num<>-1 then
  799. while f<>nil do
  800. Begin
  801. if f^.rtfFNum=num then break;
  802. f:=f^.rtfNextFont;
  803. End;
  804. result:=f; { NULL if not found }
  805. End;
  806. Function TRTFParser.GetColor (num : Integer) : PRTFColor;
  807. var
  808. c : PRTFColor;
  809. Begin
  810. c:=Fcolorlist;
  811. if (num<>-1) then
  812. while c<>nil do
  813. Begin
  814. if c^.rtfCNum=num then break;
  815. c:=c^.rtfNextColor;
  816. End;
  817. Result:=c; { NULL if not found }
  818. End;
  819. { ---------------------------------------------------------------------
  820. Expand style n, if there is such a style.
  821. ---------------------------------------------------------------------}
  822. Procedure TRTFParser.ExpandStyle (n : Integer);
  823. var
  824. s : PRTFStyle;
  825. se : PRTFStyleElt;
  826. Begin
  827. if n=-1 then exit;
  828. s:=GetStyle (n);
  829. if s=nil then exit;
  830. if (s^.rtfExpanding<>0) then
  831. Error ('Style expansion loop, style '+inttostr(n));
  832. s^.rtfExpanding:=1; { set expansion flag for loop detection }
  833. {
  834. Expand "based-on" style. This is done by synthesizing
  835. the token that the writer needs to see in order to trigger
  836. another style expansion, and feeding to token back through
  837. the router so the writer sees it.
  838. }
  839. SetToken (rtfControl, rtfParAttr, rtfStyleNum, s^.rtfSBasedOn, '\s');
  840. RouteToken;
  841. {
  842. Now route the tokens unique to this style. RTFSetToken()
  843. isn't used because it would add the param value to the end
  844. of the token text, which already has it in.
  845. }
  846. se:=s^.rtfSSEList;
  847. while se<>nil do
  848. Begin
  849. FrtfClass:=se^.rtfSEClass;
  850. FrtfMajor:=se^.rtfSEMajor;
  851. FrtfMinor:=se^.rtfSEMinor;
  852. FrtfParam:=se^.rtfSEParam;
  853. rtfTextBuf:=se^.rtfSEText;
  854. rtfTextLen:=length (rtfTextBuf);
  855. RouteToken;
  856. se:=se^.rtfNextSE
  857. End;
  858. s^.rtfExpanding:=0; { done - clear expansion flag }
  859. End;
  860. { ---------------------------------------------------------------------
  861. Initialize lookup table hash values.
  862. Only need to do this the first time it's called.
  863. ---------------------------------------------------------------------}
  864. Procedure TRTFParser.LookupInit;
  865. var count : Integer;
  866. Begin
  867. count:=0;
  868. while rtfkey[count].rtfKStr<>'' do
  869. begin
  870. rtfkey[count].rtfKHash:=Hash (rtfkey[count].rtfKStr);
  871. inc(count)
  872. End;
  873. End;
  874. { ---------------------------------------------------------------------
  875. Determine major and minor number of control token. If it's
  876. not found, the class turns into rtfUnknown.
  877. ---------------------------------------------------------------------}
  878. Procedure TRTFParser.Lookup (S : String);
  879. var
  880. thehash,rp : Integer;
  881. Begin
  882. delete(s,1,1); { skip over the leading \ character }
  883. thehash:=Hash (s);
  884. rp:=0;
  885. while rtfkey[rp].rtfKstr<>'' do
  886. Begin
  887. if (thehash=rtfkey[rp].rtfKHash) and (s=rtfkey[rp].rtfKStr) then
  888. Begin
  889. FrtfClass:=rtfControl;
  890. FrtfMajor:=rtfkey[rp].rtfKMajor;
  891. FrtfMinor:=rtfkey[rp].rtfKMinor;
  892. exit;
  893. End;
  894. inc(rp);
  895. End;
  896. FrtfClass:=rtfUnknown;
  897. End;
  898. Procedure TRTFParser.Error (msg : String);
  899. { Call errorhandler }
  900. begin
  901. if assigned(onrtferror) then onrtferror(msg);
  902. end;
  903. { ---------------------------------------------------------------------
  904. Token comparison routines
  905. ---------------------------------------------------------------------}
  906. Function TRTFParser.CheckCM (Aclass, major: Integer) : Boolean;
  907. Begin
  908. Result:=(rtfClass=Aclass) and (rtfMajor=major);
  909. End;
  910. Function TRTFParser.CheckCMM (Aclass, major, minor : Integer) : Boolean;
  911. Begin
  912. Result:=(rtfClass=Aclass) and ((rtfMajor=major) and (rtfMinor=minor));
  913. End;
  914. Function TRTFParser.CheckMM (major, minor : Integer) : Boolean;
  915. Begin
  916. Result:=(rtfMajor=major) and (rtfMinor=minor);
  917. End;
  918. Procedure TRTFParser.SetStream (Astream : TStream);
  919. begin
  920. FStream:=Astream;
  921. end;
  922. end.
  923. {
  924. $Log$
  925. Revision 1.2 2000-07-13 11:33:00 michael
  926. + removed logs
  927. }