123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638 |
- unit WHTMLHlp;
- interface
- uses Objects,WHTML,WHelp;
- const
- ListIndent = 2;
- DefIndent = 4;
- MaxTopicLinks = 500;
- type
- PTopicLinkCollection = ^TTopicLinkCollection;
- TTopicLinkCollection = object(TStringCollection)
- procedure Insert(Item: Pointer); virtual;
- function At(Index: sw_Integer): PString;
- function AddItem(Item: string): integer;
- end;
- TParagraphAlign = (paLeft,paCenter,paRight);
- PHTMLTopicRenderer = ^THTMLTopicRenderer;
- THTMLTopicRenderer = object(THTMLParser)
- function BuildTopic(P: PTopic; AURL: string; HTMLFile: PTextFile; ATopicLinks: PTopicLinkCollection): boolean;
- public
- function DocAddTextChar(C: char): boolean; virtual;
- procedure DocSoftBreak; virtual;
- procedure DocTYPE; virtual;
- procedure DocHTML(Entered: boolean); virtual;
- procedure DocHEAD(Entered: boolean); virtual;
- procedure DocMETA; virtual;
- procedure DocTITLE(Entered: boolean); virtual;
- procedure DocBODY(Entered: boolean); virtual;
- procedure DocAnchor(Entered: boolean); virtual;
- procedure DocHeading(Level: integer; Entered: boolean); virtual;
- procedure DocParagraph(Entered: boolean); virtual;
- procedure DocBreak; virtual;
- procedure DocImage; virtual;
- procedure DocBold(Entered: boolean); virtual;
- procedure DocCite(Entered: boolean); virtual;
- procedure DocCode(Entered: boolean); virtual;
- procedure DocEmphasized(Entered: boolean); virtual;
- procedure DocItalic(Entered: boolean); virtual;
- procedure DocKbd(Entered: boolean); virtual;
- procedure DocPreformatted(Entered: boolean); virtual;
- procedure DocSample(Entered: boolean); virtual;
- procedure DocStrong(Entered: boolean); virtual;
- procedure DocTeleType(Entered: boolean); virtual;
- procedure DocVariable(Entered: boolean); virtual;
- procedure DocList(Entered: boolean); virtual;
- procedure DocOrderedList(Entered: boolean); virtual;
- procedure DocListItem; virtual;
- procedure DocDefList(Entered: boolean); virtual;
- procedure DocDefTerm; virtual;
- procedure DocDefExp; virtual;
- procedure DocHorizontalRuler; virtual;
- private
- URL: string;
- Topic: PTopic;
- TopicLinks: PTopicLinkCollection;
- TextPtr: sw_word;
- InTitle: boolean;
- InBody: boolean;
- InAnchor: boolean;
- InParagraph: boolean;
- InPreformatted: boolean;
- TopicTitle: string;
- Indent: integer;
- AnyCharsInLine: boolean;
- CurHeadLevel: integer;
- PAlign: TParagraphAlign;
- LinkIndexes: array[0..MaxTopicLinks] of sw_integer;
- LinkPtr: sw_integer;
- LastTextChar: char;
- { Anchor: TAnchor;}
- procedure AddText(S: string);
- procedure AddChar(C: char);
- end;
- PHTMLHelpFile = ^THTMLHelpFile;
- THTMLHelpFile = object(THelpFile)
- constructor Init(AFileName: string; AID: word; ATOCEntry: string);
- destructor Done; virtual;
- public
- function LoadIndex: boolean; virtual;
- function SearchTopic(HelpCtx: THelpCtx): PTopic; virtual;
- function ReadTopic(T: PTopic): boolean; virtual;
- private
- Renderer: PHTMLTopicRenderer;
- FileName: string;
- CurFileName: string;
- TOCEntry: string;
- TopicLinks: PTopicLinkCollection;
- end;
- implementation
- uses WUtils,
- Dos;
- const
- {$ifdef LINUX}
- dirsep = '/';
- {$else}
- dirsep = '\';
- {$endif}
- function FormatPath(Path: string): string;
- var P: sw_integer;
- SC: char;
- begin
- if ord(DirSep)=ord('/') then
- SC:='\'
- else
- SC:='/';
- repeat
- P:=Pos(SC,Path);
- if P>0 then Path[P]:=DirSep;
- until P=0;
- FormatPath:=Path;
- end;
- function CompletePath(const Base, InComplete: string): string;
- var Drv,BDrv: string[40]; D,BD: DirStr; N,BN: NameStr; E,BE: ExtStr;
- P: sw_integer;
- Complete: string;
- begin
- Complete:=FormatPath(InComplete);
- FSplit(FormatPath(InComplete),D,N,E);
- P:=Pos(':',D); if P=0 then Drv:='' else begin Drv:=copy(D,1,P); Delete(D,1,P); end;
- FSplit(FormatPath(Base),BD,BN,BE);
- P:=Pos(':',BD); if P=0 then BDrv:='' else begin BDrv:=copy(BD,1,P); Delete(BD,1,P); end;
- if copy(D,1,1)<>'\' then
- Complete:=BD+D+N+E;
- if Drv='' then
- Complete:=BDrv+Complete;
- Complete:=FExpand(Complete);
- CompletePath:=Complete;
- end;
- function CompleteURL(const Base, URLRef: string): string;
- var P: integer;
- Drive: string[20];
- IsComplete: boolean;
- S: string;
- begin
- IsComplete:=false;
- P:=Pos(':',URLRef);
- if P=0 then Drive:='' else Drive:=UpcaseStr(copy(URLRef,1,P-1));
- if Drive<>'' then
- if (Drive='MAILTO') or (Drive='FTP') or (Drive='HTTP') or (Drive='GOPHER') then
- IsComplete:=true;
- if IsComplete then S:=URLRef else
- S:=CompletePath(Base,URLRef);
- CompleteURL:=S;
- end;
- function EncodeHTMLCtx(FileID: integer; LinkNo: word): longint;
- var Ctx: longint;
- begin
- Ctx:=(longint(FileID) shl 16)+LinkNo;
- EncodeHTMLCtx:=Ctx;
- end;
- procedure DecodeHTMLCtx(Ctx: longint; var FileID: word; var LinkNo: word);
- begin
- if (Ctx shr 16)=0 then
- begin
- FileID:=$ffff; LinkNo:=0;
- end
- else
- begin
- FileID:=Ctx shr 16; LinkNo:=Ctx and $ffff;
- end;
- end;
- function CharStr(C: char; Count: byte): string;
- var S: string;
- begin
- S[0]:=chr(Count);
- if Count>0 then FillChar(S[1],Count,C);
- CharStr:=S;
- end;
- procedure TTopicLinkCollection.Insert(Item: Pointer);
- begin
- AtInsert(Count,Item);
- end;
- function TTopicLinkCollection.At(Index: sw_Integer): PString;
- begin
- At:=inherited At(Index);
- end;
- function TTopicLinkCollection.AddItem(Item: string): integer;
- var Idx: sw_integer;
- begin
- if Item='' then Idx:=-1 else
- if Search(@Item,Idx)=false then
- begin
- AtInsert(Count,NewStr(Item));
- Idx:=Count-1;
- end;
- AddItem:=Idx;
- end;
- function THTMLTopicRenderer.DocAddTextChar(C: char): boolean;
- var Added: boolean;
- begin
- Added:=false;
- if InTitle then
- begin
- TopicTitle:=TopicTitle+C;
- Added:=true;
- end
- else
- if InBody then
- begin
- if (InPreFormatted) or (C<>#32) or (LastTextChar<>C) then
- if (C<>#32) or (AnyCharsInLine=true) or (InPreFormatted=true) then
- begin
- AddChar(C);
- LastTextChar:=C;
- Added:=true;
- end;
- end;
- DocAddTextChar:=Added;
- end;
- procedure THTMLTopicRenderer.DocSoftBreak;
- begin
- if InPreformatted then DocBreak else
- if AnyCharsInLine then
- begin
- AddChar(' ');
- LastTextChar:=' ';
- end;
- end;
- procedure THTMLTopicRenderer.DocTYPE;
- begin
- end;
- procedure THTMLTopicRenderer.DocHTML(Entered: boolean);
- begin
- end;
- procedure THTMLTopicRenderer.DocHEAD(Entered: boolean);
- begin
- end;
- procedure THTMLTopicRenderer.DocMETA;
- begin
- end;
- procedure THTMLTopicRenderer.DocTITLE(Entered: boolean);
- begin
- if Entered then
- begin
- TopicTitle:='';
- end
- else
- begin
- { render topic title here }
- if TopicTitle<>'' then
- begin
- AddText(' '+TopicTitle+' Ü'); DocBreak;
- AddText(' '+CharStr('ß',length(TopicTitle)+3)); DocBreak;
- end;
- end;
- InTitle:=Entered;
- end;
- procedure THTMLTopicRenderer.DocBODY(Entered: boolean);
- begin
- InBody:=Entered;
- end;
- procedure THTMLTopicRenderer.DocAnchor(Entered: boolean);
- var HRef: string;
- begin
- if Entered and InAnchor then DocAnchor(false);
- if Entered then
- begin
- if DocGetTagParam('HREF',HRef)=false then HRef:='';
- if (HRef<>'') and (copy(HRef,1,1)<>'#') then
- begin
- InAnchor:=true;
- AddChar(hscLink);
- HRef:=CompleteURL(URL,HRef);
- LinkIndexes[LinkPtr]:=TopicLinks^.AddItem(HRef);
- Inc(LinkPtr);
- end;
- end
- else
- begin
- if InAnchor=true then AddChar(hscLink);
- InAnchor:=false;
- end;
- end;
- procedure DecodeAlign(Align: string; var PAlign: TParagraphAlign);
- begin
- Align:=UpcaseStr(Align);
- if Align='LEFT' then PAlign:=paLeft else
- if Align='CENTER' then PAlign:=paCenter else
- if Align='RIGHT' then PAlign:=paRight;
- end;
- procedure THTMLTopicRenderer.DocHeading(Level: integer; Entered: boolean);
- var Align: string;
- begin
- if Entered then
- begin
- DocBreak;
- CurHeadLevel:=Level;
- PAlign:=paLeft;
- if DocGetTagParam('ALIGN',Align) then
- DecodeAlign(Align,PAlign);
- end
- else
- begin
- CurHeadLevel:=0;
- DocBreak;
- end;
- end;
- procedure THTMLTopicRenderer.DocParagraph(Entered: boolean);
- var Align: string;
- begin
- if Entered and InParagraph then DocParagraph(false);
- if Entered then
- begin
- if AnyCharsInLine then DocBreak;
- if DocGetTagParam('ALIGN',Align) then
- DecodeAlign(Align,PAlign);
- end
- else
- begin
- { if AnyCharsInLine then }DocBreak;
- PAlign:=paLeft;
- end;
- InParagraph:=Entered;
- end;
- procedure THTMLTopicRenderer.DocBreak;
- begin
- if (CurHeadLevel=1) or (PAlign=paCenter) then
- AddChar(hscCenter);
- if (PAlign=paRight) then
- AddChar(hscRight);
- AddChar(hscLineBreak);
- if Indent>0 then
- AddText(CharStr(#255,Indent)+hscLineStart);
- AnyCharsInLine:=false;
- end;
- procedure THTMLTopicRenderer.DocImage;
- var Alt: string;
- begin
- if DocGetTagParam('ALT',Alt)=false then Alt:='IMG';
- if Alt<>'' then
- begin
- AddText('['+Alt+']');
- end;
- end;
- procedure THTMLTopicRenderer.DocBold(Entered: boolean);
- begin
- end;
- procedure THTMLTopicRenderer.DocCite(Entered: boolean);
- begin
- end;
- procedure THTMLTopicRenderer.DocCode(Entered: boolean);
- begin
- end;
- procedure THTMLTopicRenderer.DocEmphasized(Entered: boolean);
- begin
- end;
- procedure THTMLTopicRenderer.DocItalic(Entered: boolean);
- begin
- end;
- procedure THTMLTopicRenderer.DocKbd(Entered: boolean);
- begin
- end;
- procedure THTMLTopicRenderer.DocPreformatted(Entered: boolean);
- begin
- if AnyCharsInLine then DocBreak;
- DocBreak;
- InPreformatted:=Entered;
- end;
- procedure THTMLTopicRenderer.DocSample(Entered: boolean);
- begin
- end;
- procedure THTMLTopicRenderer.DocStrong(Entered: boolean);
- begin
- end;
- procedure THTMLTopicRenderer.DocTeleType(Entered: boolean);
- begin
- end;
- procedure THTMLTopicRenderer.DocVariable(Entered: boolean);
- begin
- end;
- procedure THTMLTopicRenderer.DocList(Entered: boolean);
- begin
- if Entered then
- begin
- Inc(Indent,ListIndent);
- DocBreak;
- end
- else
- begin
- Dec(Indent,ListIndent);
- if AnyCharsInLine then DocBreak;
- end;
- end;
- procedure THTMLTopicRenderer.DocOrderedList(Entered: boolean);
- begin
- DocList(Entered);
- end;
- procedure THTMLTopicRenderer.DocListItem;
- begin
- if AnyCharsInLine then
- DocBreak;
- AddText('þ'+hscLineStart);
- end;
- procedure THTMLTopicRenderer.DocDefList(Entered: boolean);
- begin
- if Entered then
- begin
- { if LastChar<>hscLineBreak then DocBreak;}
- end
- else
- begin
- if AnyCharsInLine then DocBreak;
- end;
- end;
- procedure THTMLTopicRenderer.DocDefTerm;
- begin
- DocBreak;
- end;
- procedure THTMLTopicRenderer.DocDefExp;
- begin
- Inc(Indent,DefIndent);
- DocBreak;
- Dec(Indent,DefIndent);
- end;
- procedure THTMLTopicRenderer.DocHorizontalRuler;
- var OAlign: TParagraphAlign;
- begin
- OAlign:=PAlign;
- if AnyCharsInLine then DocBreak;
- PAlign:=paCenter;
- DocAddText(' '+CharStr('Ä',60)+' ');
- DocBreak;
- PAlign:=OAlign;
- end;
- procedure THTMLTopicRenderer.AddChar(C: char);
- begin
- if (Topic=nil) or (TextPtr=MaxBytes) then Exit;
- Topic^.Text^[TextPtr]:=ord(C);
- Inc(TextPtr);
- if (C>#15) and ((C<>' ') or (InPreFormatted=true)) then
- AnyCharsInLine:=true;
- end;
- procedure THTMLTopicRenderer.AddText(S: string);
- var I: sw_integer;
- begin
- for I:=1 to length(S) do
- AddChar(S[I]);
- end;
- function THTMLTopicRenderer.BuildTopic(P: PTopic; AURL: string; HTMLFile: PTextFile;
- ATopicLinks: PTopicLinkCollection): boolean;
- var OK: boolean;
- TP: pointer;
- I: sw_integer;
- begin
- URL:=AURL;
- Topic:=P; TopicLinks:=ATopicLinks;
- OK:=Assigned(Topic) and Assigned(HTMLFile) and Assigned(TopicLinks);
- if OK then
- begin
- if (Topic^.TextSize<>0) and Assigned(Topic^.Text) then
- begin
- FreeMem(Topic^.Text,Topic^.TextSize);
- Topic^.TextSize:=0; Topic^.Text:=nil;
- end;
- Topic^.TextSize:=MaxHelpTopicSize;
- GetMem(Topic^.Text,Topic^.TextSize);
- TopicTitle:='';
- InTitle:=false; InBody:={false}true; InAnchor:=false;
- InParagraph:=false; InPreformatted:=false;
- Indent:=0; CurHeadLevel:=0;
- PAlign:=paLeft;
- TextPtr:=0; LinkPtr:=0;
- AnyCharsInLine:=false;
- LastTextChar:=#0;
- OK:=Process(HTMLFile);
- if OK then
- begin
- { --- topic links --- }
- if (Topic^.Links<>nil) and (Topic^.LinkSize>0) then
- begin
- FreeMem(Topic^.Links,Topic^.LinkSize);
- Topic^.Links:=nil; Topic^.LinkCount:=0;
- end;
- Topic^.LinkCount:=LinkPtr{TopicLinks^.Count}; { <- eeeeeek! }
- GetMem(Topic^.Links,Topic^.LinkSize);
- for I:=0 to Topic^.LinkCount-1 do
- begin
- Topic^.Links^[I].FileID:=Topic^.FileID;
- Topic^.Links^[I].Context:=EncodeHTMLCtx(Topic^.FileID,LinkIndexes[I]+1);
- end;
- { --- topic text --- }
- GetMem(TP,TextPtr);
- Move(Topic^.Text^,TP^,TextPtr);
- FreeMem(Topic^.Text,Topic^.TextSize);
- Topic^.Text:=TP; Topic^.TextSize:=TextPtr;
- end
- else
- begin
- DisposeTopic(Topic);
- Topic:=nil;
- end;
- end;
- BuildTopic:=OK;
- end;
- constructor THTMLHelpFile.Init(AFileName: string; AID: word; ATOCEntry: string);
- begin
- inherited Init(AID);
- FileName:=AFileName; TOCEntry:=ATOCEntry;
- if FileName='' then Fail;
- New(Renderer, Init);
- New(TopicLinks, Init(50,500));
- end;
- function THTMLHelpFile.LoadIndex: boolean;
- begin
- IndexEntries^.Insert(NewIndexEntry(TOCEntry,ID,0));
- LoadIndex:=true;
- end;
- function THTMLHelpFile.SearchTopic(HelpCtx: THelpCtx): PTopic;
- function MatchCtx(P: PTopic): boolean; {$ifndef FPC}far;{$endif}
- begin
- MatchCtx:=P^.HelpCtx=HelpCtx;
- end;
- var FileID,LinkNo: word;
- P: PTopic;
- FName: string;
- begin
- DecodeHTMLCtx(HelpCtx,FileID,LinkNo);
- if (HelpCtx<>0) and (FileID<>ID) then P:=nil else
- if (FileID=ID) and (LinkNo>TopicLinks^.Count) then P:=nil else
- begin
- P:=Topics^.FirstThat(@MatchCtx);
- if P=nil then
- begin
- if LinkNo=0 then
- FName:=FileName
- else
- FName:=TopicLinks^.At(LinkNo-1)^;
- P:=NewTopic(ID,HelpCtx,0,FName);
- Topics^.Insert(P);
- end;
- end;
- SearchTopic:=P;
- end;
- function THTMLHelpFile.ReadTopic(T: PTopic): boolean;
- var OK: boolean;
- HTMLFile: PMemoryTextFile;
- Name: string;
- Link: string;
- P: sw_integer;
- begin
- OK:=T<>nil;
- if OK then
- begin
- if T^.HelpCtx=0 then Name:=FileName else
- begin
- Link:=TopicLinks^.At((T^.HelpCtx and $ffff)-1)^;
- Link:=FormatPath(Link);
- P:=Pos('#',Link); if P>0 then Delete(Link,P,255);
- { if CurFileName='' then Name:=Link else
- Name:=CompletePath(CurFileName,Link);}
- Name:=Link;
- end;
- HTMLFile:=New(PDOSTextFile, Init(Name));
- if HTMLFile=nil then
- begin
- New(HTMLFile, Init);
- HTMLFile^.AddLine('<HEAD><TITLE>Page not available</TITLE></HEAD>');
- HTMLFile^.AddLine(
- '<BODY>'+
- 'Sorry, can''t access the URL: '+Name+'... <br><br>'+
- '</BODY>');
- end;
- OK:=Renderer^.BuildTopic(T,Name,HTMLFile,TopicLinks);
- if OK then CurFileName:=Name;
- if HTMLFile<>nil then Dispose(HTMLFile, Done);
- end;
- ReadTopic:=OK;
- end;
- destructor THTMLHelpFile.Done;
- begin
- inherited Done;
- if Renderer<>nil then Dispose(Renderer, Done);
- if TopicLinks<>nil then Dispose(TopicLinks, Done);
- end;
- END.
|