|
@@ -25,12 +25,15 @@ unit chmfilewriter;
|
|
|
interface
|
|
|
|
|
|
uses
|
|
|
- Classes, SysUtils, chmwriter;
|
|
|
+ Classes, SysUtils, chmwriter, inifiles, contnrs,
|
|
|
+ {for html scanning } dom,SAX_HTML,dom_html;
|
|
|
|
|
|
type
|
|
|
TChmProject = class;
|
|
|
+ TChmProjectErrorKind = (chmerror,chmwarning,chmhint,chmnote,chmnone);
|
|
|
|
|
|
TChmProgressCB = procedure (Project: TChmProject; CurrentFile: String) of object;
|
|
|
+ TChmErrorCB = procedure (Project: TChmProject;errorkind:TChmProjectErrorKind;msg:String;detaillevel:integer=0);
|
|
|
|
|
|
{ TChmProject }
|
|
|
|
|
@@ -46,25 +49,37 @@ type
|
|
|
FMakeSearchable: Boolean;
|
|
|
FFileName: String;
|
|
|
FOnProgress: TChmProgressCB;
|
|
|
+ FOnError : TChmErrorCB;
|
|
|
FOutputFileName: String;
|
|
|
FTableOfContentsFileName: String;
|
|
|
FTitle: String;
|
|
|
+ FWindows : TObjectList;
|
|
|
+ FMergeFiles : TStringlist;
|
|
|
+ fDefaultWindow : string;
|
|
|
+ fScanHtmlContents : Boolean;
|
|
|
+ fOtherFiles : TStrings; // Files found in a scan.
|
|
|
+ fAllowedExtensions: TStringList;
|
|
|
protected
|
|
|
function GetData(const DataName: String; out PathInChm: String; out FileName: String; var Stream: TStream): Boolean;
|
|
|
procedure LastFileAdded(Sender: TObject);
|
|
|
+ procedure readIniOptions(keyvaluepairs:tstringlist);
|
|
|
+ procedure ScanHtml;
|
|
|
public
|
|
|
constructor Create; virtual;
|
|
|
destructor Destroy; override;
|
|
|
procedure LoadFromFile(AFileName: String); virtual;
|
|
|
+ procedure LoadFromhhp (AFileName:String;LeaveInclude:Boolean); virtual;
|
|
|
procedure SaveToFile(AFileName: String); virtual;
|
|
|
procedure WriteChm(AOutStream: TStream); virtual;
|
|
|
function ProjectDir: String;
|
|
|
procedure AddFileWithContext(contextid:integer;filename:ansistring;contextname:ansistring='');
|
|
|
+ procedure Error(errorkind:TChmProjectErrorKind;msg:String;detaillevel:integer=0);
|
|
|
// though stored in the project file, it is only there for the program that uses the unit
|
|
|
// since we actually write to a stream
|
|
|
property OutputFileName: String read FOutputFileName write FOutputFileName;
|
|
|
property FileName: String read FFileName write FFileName;
|
|
|
- property Files: TStrings read FFiles write FFiles;
|
|
|
+ property Files: TStrings read FFiles write FFiles; // html files
|
|
|
+ property OtherFiles: TStrings read FOtherFiles write FOtherFiles; // other files (.css, img etc)
|
|
|
property AutoFollowLinks: Boolean read FAutoFollowLinks write FAutoFollowLinks;
|
|
|
property TableOfContentsFileName: String read FTableOfContentsFileName write FTableOfContentsFileName;
|
|
|
property MakeBinaryTOC: Boolean read FMakeBinaryTOC write FMakeBinaryTOC;
|
|
@@ -74,8 +89,13 @@ type
|
|
|
property MakeSearchable: Boolean read FMakeSearchable write FMakeSearchable;
|
|
|
property DefaultPage: String read FDefaultPage write FDefaultPage;
|
|
|
property DefaultFont: String read FDefaultFont write FDefaultFont;
|
|
|
-
|
|
|
+ property Windows :TObjectList read FWindows write FWindows;
|
|
|
+ property MergeFiles :TStringlist read FMergeFiles write FMergefiles;
|
|
|
property OnProgress: TChmProgressCB read FOnProgress write FOnProgress;
|
|
|
+ property OnError : TChmErrorCB read FOnError write FOnError;
|
|
|
+ property DefaultWindow : String read FDefaultWindow write FDefaultWindow;
|
|
|
+ property ScanHtmlContents : Boolean read fScanHtmlContents write fScanHtmlContents;
|
|
|
+ property AllowedExtensions : TStringList read FAllowedExtensions;
|
|
|
end;
|
|
|
|
|
|
TChmContextNode = Class
|
|
@@ -84,9 +104,14 @@ type
|
|
|
ContextName : AnsiString;
|
|
|
End;
|
|
|
|
|
|
+
|
|
|
+
|
|
|
+Const
|
|
|
+ ChmErrorKindText : array[TCHMProjectErrorKind] of string = ('Error','Warning','Hint','Note','');
|
|
|
+
|
|
|
implementation
|
|
|
|
|
|
-uses XmlCfg, chmsitemap;
|
|
|
+uses XmlCfg, chmsitemap, CHMTypes;
|
|
|
|
|
|
{ TChmProject }
|
|
|
|
|
@@ -153,6 +178,13 @@ end;
|
|
|
constructor TChmProject.Create;
|
|
|
begin
|
|
|
FFiles := TStringList.Create;
|
|
|
+ FOtherFiles := TStringList.Create;
|
|
|
+ FAllowedExtensions:=TStringList.Create;
|
|
|
+ FAllowedExtensions.add('.HTM');
|
|
|
+ FAllowedExtensions.add('.HTML');
|
|
|
+ FWindows:=TObjectList.Create(True);
|
|
|
+ FMergeFiles:=TStringlist.Create;
|
|
|
+ ScanHtmlContents:=False;
|
|
|
end;
|
|
|
|
|
|
destructor TChmProject.Destroy;
|
|
@@ -160,16 +192,113 @@ var i : integer;
|
|
|
begin
|
|
|
for i:=0 to ffiles.count -1 do
|
|
|
ffiles.objects[i].free;
|
|
|
+ FMergeFiles.Free;
|
|
|
FFiles.Free;
|
|
|
+ FOtherFiles.Free;
|
|
|
+ FWindows.Free;
|
|
|
inherited Destroy;
|
|
|
end;
|
|
|
|
|
|
+
|
|
|
+Type
|
|
|
+ TSectionEnum = (secOptions,secWindows,secFiles,secMergeFiles,secAlias,secMap,secInfoTypes,secTextPopups,secUnknown);
|
|
|
+ TOptionEnum = (OPTAUTO_INDEX,OPTAUTO_TOC,OPTBINARY_INDEX,OPTBINARY_TOC,OPTCITATION,
|
|
|
+ OPTCOMPRESS,OPTCOPYRIGHT,OPTCOMPATIBILITY,OPTCOMPILED_FILE,OPTCONTENTS_FILE,
|
|
|
+ OPTCREATE_CHI_FILE,OPTDBCS,OPTDEFAULT_FONT,OPTDEFAULT_WINDOW,OPTDEFAULT_TOPIC,
|
|
|
+ OPTDISPLAY_COMPILE_NOTES,OPTDISPLAY_COMPILE_PROGRESS,OPTENHANCED_DECOMPILATION,OPTERROR_LOG_FILE,OPTFLAT,
|
|
|
+ OPTFULL_TEXT_SEARCH_STOP_LIST,OPTFULL_TEXT_SEARCH,OPTIGNORE,OPTINDEX_FILE,OPTLANGUAGE,OPTPREFIX,
|
|
|
+ OPTSAMPLE_STAGING_PATH,OPTSAMPLE_LIST_FILE,OPTTMPDIR,OPTTITLE,OPTCUSTOM_TAB,OPTUNKNOWN);
|
|
|
+
|
|
|
+Const
|
|
|
+ SectionNames : Array[TSectionEnum] of String =
|
|
|
+ ('OPTIONS','WINDOWS','FILES','MERGE FILES','ALIAS','MAP','INFOTYPES','TEXT POPUPS','UNKNOWN');
|
|
|
+
|
|
|
+ OptionKeys : array [TOptionEnum] of String =
|
|
|
+ ('AUTO INDEX','AUTO TOC','BINARY INDEX','BINARY TOC','CITATION',
|
|
|
+ 'COMPRESS','COPYRIGHT','COMPATIBILITY','COMPILED FILE','CONTENTS FILE',
|
|
|
+ 'CREATE CHI FILE','DBCS','DEFAULT FONT','DEFAULT WINDOW','DEFAULT TOPIC',
|
|
|
+ 'DISPLAY COMPILE NOTES','DISPLAY COMPILE PROGRESS','ENHANCED DECOMPILATION','ERROR LOG FILE','FLAT',
|
|
|
+ 'FULL-TEXT SEARCH STOP LIST','FULL TEXT SEARCH','IGNORE','INDEX FILE','LANGUAGE','PREFIX',
|
|
|
+ 'SAMPLE STAGING PATH','SAMPLE LIST FILE','TMPDIR','TITLE','CUSTOM TAB','UNKNOWN');
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+function FindSectionName (const name:string):TSectionEnum;
|
|
|
+
|
|
|
+begin
|
|
|
+ result:=low(TSectionEnum);
|
|
|
+ while (result<secUnknown) and (name<>SectionNames[Result]) do
|
|
|
+ inc(result);
|
|
|
+end;
|
|
|
+
|
|
|
+function FindOptionName(Const name:string):TOptionEnum;
|
|
|
+
|
|
|
+begin
|
|
|
+ result:=low(TOptionEnum);
|
|
|
+ while (result<optUnknown) and (name<>OptionKeys[Result]) do
|
|
|
+ inc(result);
|
|
|
+end;
|
|
|
+
|
|
|
+procedure TChmProject.readIniOptions(keyvaluepairs:tstringlist);
|
|
|
+var i : integer;
|
|
|
+ Opt : TOptionEnum;
|
|
|
+ OptVal,
|
|
|
+ OptValUpper : string;
|
|
|
+begin
|
|
|
+ for i:=0 to keyvaluepairs.count-1 do
|
|
|
+ begin
|
|
|
+ Opt:=findoptionname(uppercase(keyvaluepairs.names[i]));
|
|
|
+ optval :=keyvaluepairs.valuefromindex[i];
|
|
|
+ optvalupper:=uppercase(OptVal);
|
|
|
+ case Opt Of
|
|
|
+ OPTAUTO_INDEX : ;
|
|
|
+ OPTAUTO_TOC : ;
|
|
|
+ OPTBINARY_INDEX : MakeBinaryIndex:=optvalupper='YES';
|
|
|
+ OPTBINARY_TOC : MakeBinaryToc :=optvalupper='YES';
|
|
|
+ OPTCITATION : ;
|
|
|
+ OPTCOMPRESS : ; // Doesn't seem to have effect in workshop
|
|
|
+ OPTCOPYRIGHT : ;
|
|
|
+ OPTCOMPATIBILITY : ;
|
|
|
+ OPTCOMPILED_FILE : OutputFilename:=optval;
|
|
|
+ OPTCONTENTS_FILE : TableOfContentsFileName:=optval;
|
|
|
+ OPTCREATE_CHI_FILE : ;
|
|
|
+ OPTDBCS : ; // What this field makes unicode is not known?
|
|
|
+ OPTDEFAULT_FONT : defaultfont:=optval;
|
|
|
+ OPTDEFAULT_WINDOW : defaultwindow:=optval;
|
|
|
+ OPTDEFAULT_TOPIC : defaultpage:=optval;
|
|
|
+ OPTDISPLAY_COMPILE_NOTES : ;
|
|
|
+ OPTDISPLAY_COMPILE_PROGRESS : ;
|
|
|
+ OPTENHANCED_DECOMPILATION : ;
|
|
|
+ OPTERROR_LOG_FILE : ;
|
|
|
+ OPTFLAT : ;
|
|
|
+ OPTFULL_TEXT_SEARCH_STOP_LIST: ;
|
|
|
+ OPTFULL_TEXT_SEARCH : MakeSearchable:=optvalupper='YES';
|
|
|
+ OPTIGNORE : ;
|
|
|
+ OPTINDEX_FILE : Indexfilename:=optval;
|
|
|
+ OPTLANGUAGE : ;
|
|
|
+ OPTPREFIX : ; // doesn't seem to have effect
|
|
|
+ OPTSAMPLE_STAGING_PATH : ;
|
|
|
+ OPTSAMPLE_LIST_FILE : ;
|
|
|
+ OPTTMPDIR : ;
|
|
|
+ OPTTITLE : Title:=optval;
|
|
|
+ OPTCUSTOM_TAB : ;
|
|
|
+ OPTUNKNOWN : ; // can be used for errors on unknown keys
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+
|
|
|
procedure TChmProject.LoadFromFile(AFileName: String);
|
|
|
var
|
|
|
Cfg: TXMLConfig;
|
|
|
+ MergeFileCount,
|
|
|
+ WinCount,
|
|
|
FileCount: Integer;
|
|
|
I : Integer;
|
|
|
nd : TChmContextNode;
|
|
|
+ win: TCHMWindow;
|
|
|
+ s : String;
|
|
|
+
|
|
|
begin
|
|
|
Cfg := TXMLConfig.Create(nil);
|
|
|
Cfg.Filename := AFileName;
|
|
@@ -185,20 +314,270 @@ begin
|
|
|
nd.contextname:=Cfg.GetValue('Files/FileName'+IntToStr(I)+'/ContextName','');
|
|
|
Files.AddObject(nd.urlname,nd);
|
|
|
end;
|
|
|
+
|
|
|
+ FileCount := Cfg.GetValue('OtherFiles/Count/Value', 0);
|
|
|
+ for I := 0 to FileCount-1 do
|
|
|
+ begin
|
|
|
+ s:=Cfg.GetValue('OtherFiles/FileName'+IntToStr(I)+'/Value','');
|
|
|
+ OtherFiles.Add(s);
|
|
|
+ end;
|
|
|
+
|
|
|
+ WinCount:= Cfg.GetValue('Windows/Count/Value', 0);
|
|
|
+ for i:=0 To WinCount-1 do
|
|
|
+ begin
|
|
|
+ win:=TCHMWindow.Create;
|
|
|
+ win.loadfromxml(cfg,'Windows/item'+inttostr(i)+'/');
|
|
|
+ fwindows.add(win);
|
|
|
+ end;
|
|
|
+
|
|
|
+ Mergefilecount:=Cfg.getValue('MergeFiles/Count/Value', 0);
|
|
|
+ for i:=0 To MergeFileCount-1 do
|
|
|
+ Mergefiles.add(Cfg.getValue('MergeFiles/FileName'+IntToStr(I)+'/value',''));
|
|
|
+
|
|
|
+ // load some values that changed key backwards compatible.
|
|
|
+
|
|
|
IndexFileName := Cfg.GetValue('Files/IndexFile/Value','');
|
|
|
+ if IndexFileName='' Then
|
|
|
+ IndexFileName := Cfg.GetValue('Settings/IndexFile/Value','');
|
|
|
+
|
|
|
TableOfContentsFileName := Cfg.GetValue('Files/TOCFile/Value','');
|
|
|
+ If TableOfContentsFileName='' then
|
|
|
+ TableOfContentsFileName := Cfg.GetValue('Settings/TOCFile/Value','');
|
|
|
+
|
|
|
// For chm file merging, bintoc must be false and binindex true. Change defaults in time?
|
|
|
- MakeBinaryTOC := Cfg.GetValue('Files/MakeBinaryTOC/Value', True);
|
|
|
- MakeBinaryIndex:= Cfg.GetValue('Files/MakeBinaryIndex/Value', False);
|
|
|
+ // OTOH, merging will be mostly done for fpdoc files, and that doesn't care about defaults.
|
|
|
+
|
|
|
+ S:=Cfg.GetValue('Files/MakeBinaryTOC/Value', '');
|
|
|
+ if s='' Then
|
|
|
+ MakeBinaryTOC := Cfg.GetValue('Settings/MakeBinaryTOC/Value', True)
|
|
|
+ else
|
|
|
+ MakeBinaryTOC := Cfg.GetValue('Files/MakeBinaryTOC/Value', True);
|
|
|
+
|
|
|
+ S:=Cfg.GetValue('Files/MakeBinaryIndex/Value', '');
|
|
|
+ if s='' Then
|
|
|
+ MakeBinaryIndex := Cfg.GetValue('Settings/MakeBinaryIndex/Value', False)
|
|
|
+ else
|
|
|
+ MakeBinaryIndex := Cfg.GetValue('Files/MakeBinaryIndex/Value', False);
|
|
|
+
|
|
|
AutoFollowLinks := Cfg.GetValue('Settings/AutoFollowLinks/Value', False);
|
|
|
MakeSearchable := Cfg.GetValue('Settings/MakeSearchable/Value', False);
|
|
|
DefaultPage := Cfg.GetValue('Settings/DefaultPage/Value', '');
|
|
|
Title := Cfg.GetValue('Settings/Title/Value', '');
|
|
|
OutputFileName := Cfg.GetValue('Settings/OutputFileName/Value', '');
|
|
|
- DefaultFont := Cfg.GetValue('Settings/DefaultFont/Value', '');
|
|
|
+ DefaultFont := Cfg.GetValue('Settings/DefaultFont/Value', '');
|
|
|
+ DefaultWindow:= Cfg.GetValue('Settings/DefaultWindow/Value', '');
|
|
|
+ ScanHtmlContents:= Cfg.GetValue('Settings/ScanHtmlContents/Value', False);
|
|
|
+
|
|
|
Cfg.Free;
|
|
|
end;
|
|
|
|
|
|
+function cleanupstring(const s:string):string;
|
|
|
+var
|
|
|
+ i:integer;
|
|
|
+begin
|
|
|
+ i:=pos(';',s);
|
|
|
+ if i>0 then
|
|
|
+ result:=trim(copy(s,1,i-1))
|
|
|
+ else
|
|
|
+ result:=trim(s);
|
|
|
+end;
|
|
|
+
|
|
|
+procedure TChmProject.LoadFromhhp (AFileName:String;LeaveInclude:Boolean);
|
|
|
+// leaveinclude=true leaves includefiles includefiles.
|
|
|
+
|
|
|
+procedure addalias(const key,value :string);
|
|
|
+
|
|
|
+var i,j : integer;
|
|
|
+ node: TCHMContextNode;
|
|
|
+ keyupper : string;
|
|
|
+begin
|
|
|
+ { Defaults other than global }
|
|
|
+ MakeBinaryIndex:=True;
|
|
|
+
|
|
|
+ {$ifdef hhp_debug}
|
|
|
+ writeln('alias entry:',key,'=',value);
|
|
|
+ {$endif}
|
|
|
+ keyupper:=uppercase(value);
|
|
|
+ i:=0; j:=files.count;
|
|
|
+ while (i<j) and (uppercase(TCHMContextnode(files.objects[i]).UrlName)<>keyupper) do
|
|
|
+ inc(i);
|
|
|
+ if i=j then
|
|
|
+ begin
|
|
|
+ {$ifdef hhp_debug}
|
|
|
+ writeln('alias new node:',key);
|
|
|
+ {$endif}
|
|
|
+ node:=TCHMContextNode.create;
|
|
|
+ node.URLName:=value;
|
|
|
+ node.contextname:=key;
|
|
|
+ end
|
|
|
+ else
|
|
|
+ begin
|
|
|
+ node:=TCHMContextNode(Files.objects[i]);
|
|
|
+ node.ContextName:=key;
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+procedure processalias(strs:TStringlist);
|
|
|
+var i,j : integer;
|
|
|
+ s : string;
|
|
|
+ strls2:tstringlist;
|
|
|
+
|
|
|
+begin
|
|
|
+ for i:=0 to strs.count-1 do
|
|
|
+ begin
|
|
|
+ s:=cleanupstring(strs[i]);
|
|
|
+ if uppercase(copy(s,1,8))='#INCLUDE' then
|
|
|
+ begin
|
|
|
+ delete(s,1,8);
|
|
|
+ s:=trim(s);
|
|
|
+ if fileexists(s) then
|
|
|
+ begin
|
|
|
+ strls2:=TstringList.create;
|
|
|
+ strls2.loadfromfile(s);
|
|
|
+ processalias(strls2);
|
|
|
+ strls2.free;
|
|
|
+ end;
|
|
|
+
|
|
|
+ end
|
|
|
+ else
|
|
|
+ begin
|
|
|
+ s:=cleanupstring(s);
|
|
|
+ j:=pos('=',s);
|
|
|
+ if j>0 then
|
|
|
+ addalias(trim(copy(s,1,j-1)),copy(s,j+1,length(s)-j));
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+procedure addmap(const key,value :string);
|
|
|
+
|
|
|
+var i,j : integer;
|
|
|
+ node: TCHMContextNode;
|
|
|
+ keyupper : string;
|
|
|
+begin
|
|
|
+ {$ifdef hhp_debug}
|
|
|
+ writeln('map entry:',key,'=',value);
|
|
|
+ {$endif}
|
|
|
+ keyupper:=uppercase(key);
|
|
|
+ i:=0; j:=files.count;
|
|
|
+ while (i<j) and (uppercase(TCHMContextnode(files.objects[i]).contextname)<>keyupper) do
|
|
|
+ inc(i);
|
|
|
+ if i=j then
|
|
|
+ raise Exception.create('context "'+key+'" not found!')
|
|
|
+ else
|
|
|
+ begin
|
|
|
+ node:=TCHMContextNode(Files.objects[i]);
|
|
|
+ node.Contextnumber:=strtointdef(value,0);
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+procedure processmap(strs:TStringlist);
|
|
|
+var i,j : integer;
|
|
|
+ s : string;
|
|
|
+ strls2:tstringlist;
|
|
|
+
|
|
|
+begin
|
|
|
+ for i:=0 to strs.count-1 do
|
|
|
+ begin
|
|
|
+ s:=cleanupstring(strs[i]);
|
|
|
+ {$ifdef hhp_debug}
|
|
|
+ writeln('map item:',s);
|
|
|
+ {$endif}
|
|
|
+ if uppercase(copy(s,1,8))='#INCLUDE' then
|
|
|
+ begin
|
|
|
+ delete(s,1,8);
|
|
|
+ s:=trim(s);
|
|
|
+ if fileexists(s) then
|
|
|
+ begin
|
|
|
+ strls2:=TstringList.create;
|
|
|
+ strls2.loadfromfile(s);
|
|
|
+ processmap(strls2);
|
|
|
+ strls2.free;
|
|
|
+ end;
|
|
|
+ end
|
|
|
+ else
|
|
|
+ begin
|
|
|
+ s:=cleanupstring(s);
|
|
|
+ if uppercase(copy(s,1,7))='#DEFINE' Then
|
|
|
+ begin
|
|
|
+ delete(s,1,7);
|
|
|
+ s:=trim(s);
|
|
|
+ j:=pos(' ',s);
|
|
|
+ if j>0 then
|
|
|
+ addmap(trim(copy(s,1,j-1)),copy(s,j+1,length(s)-j));
|
|
|
+ end
|
|
|
+ else
|
|
|
+ begin
|
|
|
+ {$ifdef hhp_debug}
|
|
|
+ writeln('map leftover:',s);
|
|
|
+ {$endif}
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+var
|
|
|
+ Fini : TMemIniFile; // TMemInifile is more compatible with Delphi. Delphi's API based TIniFile fails on .hhp files.
|
|
|
+ secs,strs : TStringList;
|
|
|
+ i,j : Integer;
|
|
|
+ section : TSectionEnum;
|
|
|
+ nd : TChmContextNode;
|
|
|
+
|
|
|
+begin
|
|
|
+ Fini:=TMeminiFile.Create(AFileName);
|
|
|
+ secs := TStringList.create;
|
|
|
+ strs := TStringList.create;
|
|
|
+ fini.readsections(secs);
|
|
|
+
|
|
|
+ // Do the files section first so that we can emit errors if
|
|
|
+ // other sections reference unknown files.
|
|
|
+
|
|
|
+ fini.readsectionvalues(SectionNames[secFiles] ,strs);
|
|
|
+ if strs.count>0 then
|
|
|
+ for j:=0 to strs.count-1 do
|
|
|
+ begin
|
|
|
+ nd:=TChmContextNode.Create;
|
|
|
+ nd.urlname:=strs[j];
|
|
|
+ nd.contextnumber:=0;
|
|
|
+ nd.contextname:='';
|
|
|
+ Files.AddObject(nd.urlname,nd);
|
|
|
+ end;
|
|
|
+
|
|
|
+ // aliases also add file nodes.
|
|
|
+
|
|
|
+ fini.readsectionvalues(SectionNames[secAlias] ,strs); // resolve all aliases.
|
|
|
+ if strs.count>0 then
|
|
|
+ processalias(strs);
|
|
|
+
|
|
|
+ // map files only add to existing file nodes.
|
|
|
+ fini.readsectionvalues(SectionNames[secmap] ,strs);
|
|
|
+ if strs.count>0 then
|
|
|
+ processmap(strs);
|
|
|
+
|
|
|
+
|
|
|
+ for i:=0 to secs.count-1 do
|
|
|
+ begin
|
|
|
+ section:=FindSectionName(Uppercase(Secs[i]));
|
|
|
+ if section<>secunknown then
|
|
|
+ fini.readsectionvalues(secs[i] ,strs);
|
|
|
+ case section of
|
|
|
+ secOptions : readinioptions(strs);
|
|
|
+ secWindows : for j:=0 to strs.count-1 do
|
|
|
+ FWindows.add(TCHMWindow.Create(strs[j]));
|
|
|
+ secFiles : ; // already done
|
|
|
+ secMergeFiles: FMergeFiles.Assign(Strs); // just a filelist
|
|
|
+ secAlias : ; // already done
|
|
|
+ secMap : ; // already done
|
|
|
+ secInfoTypes : ; // unused for now.
|
|
|
+ secTextPopups: ; // rarely used.
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+ secs.free;
|
|
|
+ strs.free;
|
|
|
+ fini.free;
|
|
|
+ ScanHtmlContents:=true;
|
|
|
+end;
|
|
|
+
|
|
|
procedure TChmProject.AddFileWithContext(contextid:integer;filename:ansistring;contextname:ansistring='');
|
|
|
var x : integer;
|
|
|
nd : TChmContextNode;
|
|
@@ -247,16 +626,41 @@ begin
|
|
|
Cfg.SetValue('Files/FileName'+IntToStr(I)+'/ContextName', nd.contextname);
|
|
|
end;
|
|
|
end;
|
|
|
- Cfg.SetValue('Files/IndexFile/Value', IndexFileName);
|
|
|
- Cfg.SetValue('Files/TOCFile/Value', TableOfContentsFileName);
|
|
|
- Cfg.SetValue('Files/MakeBinaryTOC/Value',MakeBinaryTOC);
|
|
|
- Cfg.SetValue('Files/MakeBinaryIndex/Value',MakeBinaryIndex);
|
|
|
+
|
|
|
+ Cfg.SetValue('OtherFiles/Count/Value', OtherFiles.Count);
|
|
|
+ for I := 0 to OtherFiles.Count-1 do
|
|
|
+ Cfg.SetValue('OtherFiles/FileName'+IntToStr(I)+'/Value', OtherFiles.Strings[I]);
|
|
|
+
|
|
|
+
|
|
|
+ Cfg.SetValue('Windows/Count/Value', FWindows.count);
|
|
|
+ for i:=0 To FWindows.Count-1 do
|
|
|
+ TCHMWindow(FWindows[i]).savetoxml(cfg,'Windows/item'+inttostr(i)+'/');
|
|
|
+
|
|
|
+ Cfg.SetValue('MergeFiles/Count/Value', FMergeFiles.count);
|
|
|
+ for i:=0 To FMergeFiles.Count-1 do
|
|
|
+ Cfg.SetValue('MergeFiles/FileName'+IntToStr(I)+'/value',FMergeFiles[i]);
|
|
|
+
|
|
|
+ // delete legacy keys.
|
|
|
+ Cfg.DeleteValue('Files/IndexFile/Value');
|
|
|
+ Cfg.DeleteValue('Files/TOCFile/Value');
|
|
|
+ Cfg.DeleteValue('Files/MakeBinaryTOC/Value');
|
|
|
+ Cfg.DeleteValue('Files/MakeBinaryIndex/Value');
|
|
|
+ Cfg.SetValue('Settings/IndexFile/Value', IndexFileName);
|
|
|
+ Cfg.SetValue('Settings/TOCFile/Value', TableOfContentsFileName);
|
|
|
+ Cfg.SetValue('Settings/MakeBinaryTOC/Value',MakeBinaryTOC);
|
|
|
+ Cfg.SetValue('Settings/MakeBinaryIndex/Value',MakeBinaryIndex);
|
|
|
+
|
|
|
Cfg.SetValue('Settings/AutoFollowLinks/Value', AutoFollowLinks);
|
|
|
Cfg.SetValue('Settings/MakeSearchable/Value', MakeSearchable);
|
|
|
Cfg.SetValue('Settings/DefaultPage/Value', DefaultPage);
|
|
|
Cfg.SetValue('Settings/Title/Value', Title);
|
|
|
Cfg.SetValue('Settings/OutputFileName/Value', OutputFileName);
|
|
|
Cfg.SetValue('Settings/DefaultFont/Value', DefaultFont);
|
|
|
+
|
|
|
+ Cfg.SetValue('Settings/DefaultWindow/Value', DefaultWindow);
|
|
|
+ Cfg.SetValue('Settings/ScanHtmlContents/Value', ScanHtmlContents);
|
|
|
+
|
|
|
+
|
|
|
Cfg.Flush;
|
|
|
Cfg.Free;
|
|
|
end;
|
|
@@ -266,6 +670,134 @@ begin
|
|
|
Result := ExtractFilePath(FileName);
|
|
|
end;
|
|
|
|
|
|
+procedure TChmProject.Error(errorkind:TChmProjectErrorKind;msg:String;detaillevel:integer=0);
|
|
|
+begin
|
|
|
+ if assigned(OnError) then
|
|
|
+ OnError(self,errorkind,msg,detaillevel);
|
|
|
+end;
|
|
|
+
|
|
|
+procedure TChmProject.ScanHtml;
|
|
|
+
|
|
|
+procedure checkattributes(node:TDomNode;attributename:string;filelist :TStringList);
|
|
|
+var
|
|
|
+ Attributes: TDOMNamedNodeMap;
|
|
|
+ atnode : TDomNode;
|
|
|
+ fn : String;
|
|
|
+begin
|
|
|
+ if assigned(node) then
|
|
|
+ begin
|
|
|
+ Attributes:=node.Attributes;
|
|
|
+ if assigned(attributes) then
|
|
|
+ begin
|
|
|
+ atnode :=attributes.GetNamedItem(attributename);
|
|
|
+ if assigned(atnode) then
|
|
|
+ begin
|
|
|
+ fn:=atnode.nodevalue;
|
|
|
+ if (fn<>'') then
|
|
|
+ filelist.add(fn);
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+
|
|
|
+function scantags(prnt:TDomNode;filelist:TStringlist):TDomNode;
|
|
|
+// Seach first matching tag in siblings
|
|
|
+var chld: TDomNode;
|
|
|
+begin
|
|
|
+ result:=nil;
|
|
|
+ if assigned(prnt ) then
|
|
|
+ begin
|
|
|
+ chld:=prnt.firstchild;
|
|
|
+ while assigned(chld) do
|
|
|
+ begin
|
|
|
+ scantags(chld,filelist); // depth first.
|
|
|
+ if (chld is TDomElement) then
|
|
|
+ begin
|
|
|
+ // writeln(tdomelement(chld).tagname,' ',chld.classname );
|
|
|
+ if tdomelement(chld).tagname='link'then
|
|
|
+ begin
|
|
|
+ //printattributes(chld,'');
|
|
|
+ checkattributes(chld,'href',filelist);
|
|
|
+ end;
|
|
|
+ if tdomelement(chld).tagname='img'then
|
|
|
+ begin
|
|
|
+ //printattributes(chld,'');
|
|
|
+ checkattributes(chld,'src',filelist);
|
|
|
+ end;
|
|
|
+
|
|
|
+ end;
|
|
|
+ chld:=chld.nextsibling;
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+var
|
|
|
+ filelist, localfilelist: TStringList;
|
|
|
+ domdoc : THTMLDocument;
|
|
|
+ i,j : Integer;
|
|
|
+ fn,s : string;
|
|
|
+ ext : String;
|
|
|
+begin
|
|
|
+ filelist:= TStringList.create;
|
|
|
+ localfilelist:= TStringList.create;
|
|
|
+
|
|
|
+ for j:=0 to Files.count-1 do
|
|
|
+ begin
|
|
|
+ fn:=files[j];
|
|
|
+ localfilelist.clear;
|
|
|
+ if (FAllowedExtensions.Indexof(uppercase(extractfileext(fn)))<>-1) then
|
|
|
+ begin
|
|
|
+ if fileexists(fn) then
|
|
|
+ begin
|
|
|
+ domdoc:=THtmlDocument.Create;
|
|
|
+ try
|
|
|
+ Error(chmnote,'Scanning file '+fn+'.',5);
|
|
|
+ ReadHtmlFile(domdoc,fn);
|
|
|
+
|
|
|
+ scantags(domdoc,localfilelist);
|
|
|
+ for i:=0 to localFilelist.count-1 do
|
|
|
+ begin
|
|
|
+ s:=localfilelist[i];
|
|
|
+ if fileexists(s) then // correct for relative path .html file?
|
|
|
+ begin
|
|
|
+ filelist.add(s);
|
|
|
+ Error(ChmNote,'Found file '+s+' while scanning '+fn,1);
|
|
|
+ end
|
|
|
+ else
|
|
|
+ begin
|
|
|
+ Error(ChmWarning,'Found file '+s+' while scanning '+fn+', but couldn''t find it on disk',2);
|
|
|
+ end
|
|
|
+ end;
|
|
|
+ except
|
|
|
+ on e:exception do
|
|
|
+ Error(ChmError,'Html parsing '+fn+', failed. Please submit a bug.');
|
|
|
+ end;
|
|
|
+ domdoc.free;
|
|
|
+ end
|
|
|
+ else
|
|
|
+ begin
|
|
|
+ Error(chmnote,'Can''t find file '+fn+' to scan it.',5);
|
|
|
+ end;
|
|
|
+ end
|
|
|
+ else
|
|
|
+ Error(chmnote,'Not scanning file because of unknown extension '+fn,5);
|
|
|
+ end;
|
|
|
+ if filelist.count>0 then
|
|
|
+ for i:=0 to filelist.count-1 do
|
|
|
+ begin
|
|
|
+ if otherfiles.indexof(filelist[i])=-1 then
|
|
|
+ begin
|
|
|
+ otherfiles.add(filelist[i]);
|
|
|
+ Error(chmnote,'Added media file '+filelist[i],5);
|
|
|
+ end
|
|
|
+ else
|
|
|
+ Error(chmnote,'Ignored duplicate found file '+filelist[i],5);
|
|
|
+ end;
|
|
|
+ filelist.free;
|
|
|
+ localfilelist.free;
|
|
|
+end;
|
|
|
+
|
|
|
procedure TChmProject.WriteChm(AOutStream: TStream);
|
|
|
var
|
|
|
Writer : TChmWriter;
|
|
@@ -274,6 +806,11 @@ var
|
|
|
nd : TChmContextNode;
|
|
|
I : Integer;
|
|
|
begin
|
|
|
+ // Scan html for "rest" files.
|
|
|
+
|
|
|
+ If ScanHtmlContents Then
|
|
|
+ ScanHtml; // Since this is slowing we opt to skip this step, and only do this on html load.
|
|
|
+
|
|
|
IndexStream := nil;
|
|
|
TOCStream := nil;
|
|
|
|
|
@@ -283,9 +820,13 @@ begin
|
|
|
Writer.OnGetFileData := @GetData;
|
|
|
Writer.OnLastFile := @LastFileAdded;
|
|
|
|
|
|
- // give it the list of files
|
|
|
+ // give it the list of html files
|
|
|
Writer.FilesToCompress.AddStrings(Files);
|
|
|
|
|
|
+ // give it the list of other files
|
|
|
+
|
|
|
+ Writer.FilesToCompress.AddStrings(OtherFiles);
|
|
|
+
|
|
|
// now some settings in the chm
|
|
|
Writer.DefaultPage := DefaultPage;
|
|
|
Writer.Title := Title;
|
|
@@ -293,15 +834,24 @@ begin
|
|
|
Writer.FullTextSearch := MakeSearchable;
|
|
|
Writer.HasBinaryTOC := MakeBinaryTOC;
|
|
|
Writer.HasBinaryIndex := MakeBinaryIndex;
|
|
|
+ Writer.IndexName := IndexFileName;
|
|
|
+ Writer.TocName := TableOfContentsFileName;
|
|
|
|
|
|
for i:=0 to files.count-1 do
|
|
|
begin
|
|
|
nd:=TChmContextNode(files.objects[i]);
|
|
|
+ if not fileexists(files[i]) then
|
|
|
+ Error(chmWarning,'File '+Files[i]+' does not exist');
|
|
|
if assigned(nd) and (nd.contextnumber<>0) then
|
|
|
Writer.AddContext(nd.ContextNumber,files[i]);
|
|
|
end;
|
|
|
+ if FWIndows.Count>0 then
|
|
|
+ Writer.Windows:=FWIndows;
|
|
|
|
|
|
// and write!
|
|
|
+
|
|
|
+ Error(chmnone,'Writing CHM '+OutputFileName,0);
|
|
|
+
|
|
|
Writer.Execute;
|
|
|
|
|
|
if Assigned(TOCStream) then TOCStream.Free;
|