rtfpars.pp 27 KB

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