i want to render a web-page (i.e. TWebBrowser
) to a device context. i want to use Internet Explorer's layout engine to render content to a device context (i.e. metafile, pdf metafile).
Starting with Internet Explorer 9 the IHTMLElementRender
interface is no longer supported:
IHTMLElementRender interface
Use this interface to draw the contents of an element to a specified device context, normally a printer.
Members
The IHTMLElementRender interface inherits from the IUnknown interface but does not have additional members.
To the point that they no longer mention that the DrawToDC method even exists:
IHTMLElementRender::DrawToDC Method
Deprecated. Draws the contents of the element to the specified device context.
Syntax
HRESULT DrawToDC( HDC hDC );
Parameters
- hDC
[in] An HDC specifying the device to be drawn to, typically a printer.Return Value
Returns S_OK if successful, or an error value otherwise.
Remarks
As of Windows Internet Explorer 9, this method is deprecated and should not be used.
With some printers, running IHTMLElementRender::DrawToDC may cause problems. You can ensure that IHTMLElementRender::DrawToDC works properly on all printers by running IHTMLElementRender::SetDocumentPrinter method first, and then passing the modified device context to IHTMLElementRender::DrawToDC.
Note: I'm quoting all the documentation so that it can still be found when Microsoft finally removes it from MSDN altogether. Along with the interface declaration:
IHTMLElementRender = interface(IUnknown)
['{3050F669-98B5-11CF-BB82-00AA00BDCE0B}']
function DrawToDC(hdc: HDC): HResult; stdcall;
function SetDocumentPrinter(const bstrPrinterName: WideString; hdc: HDC): HResult; stdcall;
end;
Use the IViewObject
interface
i've tried converting to use the IViewObject
(e.g. How to render HTML element without using web browser?):
procedure RenderWebBrowserToMetafile(Browser: TWebBrowser; Metafilename: string);
var
view: IViewObject;
m: TMetafile;
mc: TMetafileCanvas;
w, h: Integer;
r: TRect;
dpix: Integer;
dpiy: Integer;
dc: HDC;
begin
w := WebBrowserScrollWidth(Browser);
h := WebBrowserScrollHeightStrict(Browser);
//96dpi screen to 300dpi metafile (destined for printer)
dc := GetDC(0);
try
dpix := GetDeviceCaps(GetDC(0), LOGPIXELSX);
dpiy := GetDeviceCaps(GetDC(0), LOGPIXELSY);
finally
ReleaseDC(0, dc);
end;
w := MulDiv(w, 300, dpix);
h := MulDiv(h, 300, dpiy);
view := Browser.Document as IViewObject;
m := TMetafile.Create;
try
m.Width := w;
m.Height := h;
r := Rect(0, 0, w, h);
mc := TMetafileCanvas.Create(m, 0);
try
view.Draw(
DVASPECT_CONTENT, //draw aspect
1, //index
nil, //pAspectInfo
nil, //pDVTargetDevice
0, //Target Device Context
mc.handle, //hdcDraw
@r, //target Bounds
@w, //window bounds (for metafiles)
nil, //continue function
0); //continue value
finally
mc.Free;
end;
m.SaveToFile(Metafilename);
finally
m.Free;
end;
end;
The problem with this code is that it only renders the visible area of the browser (i.e. without scrolled content):
i need to render the entire web-page (e.g. for printing).
i tried changing the draw aspect from DVASPECT_CONTENT
(as this example does), to DVASPECT_DOCPRINT
:
DVASPECT_DOCPRINT
Provides a representation of the object on the screen as though it were printed to a printer using the Print command from the File menu. The described data may represent a sequence of pages.
With the same result; rather than rendering the page, only the visible portion is rendered:
How can i ask the IE9 rendering engine to render?
i also tried using different indexes along with DVASPECT_DOCPRINT
. Although IViewObject
is not documented, maybe that's how you print different "pages":
view.Draw(DVASPECT_DOCPRINT, 1, ...);
view.Draw(DVASPECT_DOCPRINT, 2, ...);
view.Draw(DVASPECT_DOCPRINT, 3, ...);
...
view.Draw(DVASPECT_DOCPRINT, n, ...);
But the WebBrowser doesn't care what the index is; only rendering the current View
(which i suppose makes sense when using IViewObject
).
How do i render a browser to a device context?
Note: The question is tagged with delphi
, but there's nothing in this question that is delphi specific.
Update: i also tried other combinations of aspect and index:
view.Draw(DVASPECT_CONTENT, -1, ...);
view.Draw(DVASPECT_DOCPRINT, -1, ...);
view.Draw(DVASPECT_CONTENT, 0, ...);
view.Draw(DVASPECT_DOCPRINT, 0, ...);
Using IViewObject, you can create a screenshot of the current viewport. I use a trick to create a screenshot of a web page (without any frames) or a frame document.
Note that you should be careful of doing this when the document is extremely large.