2011. április 5., kedd
Load TWebBrowser's document content from stream or string
Problem/Question/Abstract:
I hate to face the fact that TWebBrowser does not come with Lines property and LoadFromStream/LoadFromFile method as we all familiar working with TMemo. To create temporary file and then call Navigate/Navigate2 method seems quite awful way.
Answer:
Here I want to show you how to reverse what was done by TWebBrowser.Document's IPersistStreamInit implementation in my previous article "Saving raw HTML source from TWebBrowser to disk". Beside Save() method, it also has Load() method. As you might guess from the method name, the later allows you to load the content from any object that implements IStream.
Here's how to do it:
procedure LoadDocFromStream(WB: TWebBrowser; Stream: TStream);
var
PersistStreamInit: IPersistStreamInit;
StreamIntf: IStream;
StreamAdapter: TStreamAdapter;
begin
PersistStreamInit := WB as IPersistStreamInit;
StreamAdapter := TStreamAdapter.Create(Stream);
StreamIntf := StreamAdapter as IStream;
PersistStreamInit.Load(StreamIntf);
end;
After knowing which doing what from each statement above, now it is the time to reduce your typing task :)
procedure LoadDocFromStream(WB: TWebBrowser; Stream: TStream);
begin
(WB.Document as IPersistStreamInit).Load(
TStreamAdapter.Create(Stream, soReference));
end;
That's all the way to do it. Quite simple eh? If you have a string variable holds your HTML content, simply create TStringStream from it and pass it to second function parameter.
But wait, there's another way to work with string source! For you JavaScript guys whom play often with IE DOM should familiar with this script code:
<script language="JavaScript">
<!--
document.write("<HTML><BODY><H2>Hello World!</H2>/BODY></HTML>");
document.close();
//-->
</script>
Yes, that can be done with Delphi and TWebBrowser too. The main difference is the type of involved object. The first method is using TWebBrowser.Document's IPersistStreamInit implementation (IPersistStreamInit is IPersistStream descendant which is actually exposes Save() and Load() methods). The second is using its IHTMLDocument2 implementation method.
Here's how to do it:
procedure LoadDocFromString(WB: TWebBrowser; const HTMLString: string);
var
v: OleVariant;
HTMLDocument: IHTMLDocument2;
begin
HTMLDocument := WB.Document as IHTMLDocument2;
v := VarArrayCreate([0, 0], varVariant);
v[0] := HTMLString;
HTMLDocument.Write(PSafeArray(TVarData(v).VArray));
HTMLDocument.Close;
end;
Remember to put ActiveX and MSHTML units to your uses clause (IHTMLDocument2 is declared inside the later unit). If your Delphi version does not come with it, you'll need to import "Microsoft HTML Object Library" from your Delphi IDE or use command line tool $(DELPHI)\bin\tlibimp.exe. In this case, you'll get MSHTML_TLB.pas.
There is a preliminary condition should be met though. All methods above will fail if WebBrowser does not contain valid document. You must load initial document (call it "dummy document") for the first time before using the functions above. I love to hear if anyone knows how to assign a dummy document better than my workaround below. Here's the snippet code:
if not Assigned(WebBrowser.Document) then
LoadBlankDocAndWaitUntilDocLoaded;
LoadContentFromStringOrStream;
Finally to include the document validity checking, I rewrote all those above and make the final 'ready to use' code:
procedure LoadBlankDoc(WB: TWebBrowser);
begin
WB.Navigate('about:blank', EmptyParam, EmptyParam, EmptyParam, EmptyParam);
while WB.ReadyState <> READYSTATE_COMPLETE do
begin
Application.ProcessMessages;
Sleep(0);
end;
end;
procedure CheckDocReady(WB: TWebBrowser);
begin
if not Assigned(WB.Document) then
LoadBlankDoc(WB);
end;
procedure LoadDocFromStream(WB: TWebBrowser; Stream: TStream);
begin
CheckDocReady(WB);
(WB.Document as IPersistStreamInit).Load(TStreamAdapter.Create(Stream));
end;
procedure LoadDocFromString(WB: TWebBrowser; const HTMLString: string);
var
v: OleVariant;
HTMLDocument: IHTMLDocument2;
begin
CheckDocReady(WB);
HTMLDocument := WB.Document as IHTMLDocument2;
v := VarArrayCreate([0, 0], varVariant);
v[0] := HTMLString;
HTMLDocument.Write(PSafeArray(TVarData(v).VArray));
HTMLDocument.Close;
end;
I have used this method but the images dont appear to load in the html document that is displayed, I think you might need to specify the base location for the file, is there a way of doing this on the fly without have to manually code this into the html document passed to the twebbroswer??
The problem was that the browser failed to know where the document was saved so it couldnt find the images specified in the html code. The way to tell the browser where the original file resided is to insert the tag into the head section, I didnt find a way of making this possible until today using an html parser written by Przemyslaw Jankowski, which I found on http://vclcomponents.com , using this freeware unit I was able to write the following procedures/functions to accomplish the task.
function AdjustFile(HTMLDoc, SpecifiedFolder: string): string;
var
p: THtmlParser;
t: TTag;
begin
p := THtmlParser.Create;
try
p.Text := HTMLDoc;
p.GotoBeginning;
while p.NextTag do
if LowerCase(p.Tag.Name) = '/head' then
begin
t := TTag.Create;
t.Name := 'BASE';
t.Params.Add('HREF="' + SpecifiedFolder + '"');
p.InsertTag(t);
p.InsertText(#13#10);
t.Free;
Break;
end;
Result := p.Text;
finally
p.Free;
end;
end;
procedure LoadDocFromString(WB: TWebBrowser; HTMLString, SpecifiedFolder: string);
var
v: OleVariant;
HTMLDocument: IHTMLDocument2;
begin
CheckDocReady(WB);
HTMLDocument := WB.Document as IHTMLDocument2;
v := VarArrayCreate([0, 0], varVariant);
v[0] := AdjustFile(HTMLString, SpecifiedFolder);
HTMLDocument.Write(PSafeArray(TVarData(v).VArray));
HTMLDocument.Close;
end;
SpecifiedFolder is something like:- c:\saved work\html\version 5
ofcourse the above code does not check for the existence of a base tag already or what to do if no head section exists.
Component Download: LoadWBContent.zip
Feliratkozás:
Megjegyzések küldése (Atom)
Nincsenek megjegyzések:
Megjegyzés küldése