Using CEF for HTML page thumbnail generation

Do not post support requests, bug reports or feature requests. Discuss CEF here. Non-CEF related discussion goes in General Discussion!

Using CEF for HTML page thumbnail generation

Postby Mayhew » Thu Jan 21, 2016 3:11 pm

I was thinking about ways to leverage CEF's offscreen mode for web page thumbnailing. Basically I would like to create an offscreen browser, give it a page, and take the resulting bitmap buffer and save it to disk. I want to do this completely headlessly, i.e. no visible main window, etc.

Ideally I would like to create an offscreen browser within my existing CEF based app which doesn't use offscreen mode and leverage the second offscreen browser for image generation. Based on the APIs it doesn't look like the two could coexist in the same app/processes. The other option would be to use CEF in a windowless command-line app but I'm not sure that is supported or possible.

Has anyone tried this sort of thing?
Mayhew
Expert
 
Posts: 288
Joined: Mon Apr 18, 2011 8:02 pm

Re: Using CEF for HTML page thumbnail generation

Postby magreenblatt » Thu Jan 21, 2016 3:34 pm

You can create windowed and windowless browsers in the same app.
magreenblatt
Site Admin
 
Posts: 9836
Joined: Fri May 29, 2009 6:57 pm

Re: Using CEF for HTML page thumbnail generation

Postby Mayhew » Thu Jan 21, 2016 4:33 pm

Oh, that is great.

Since windowless_rendering_enabled is part of CefSettings I just assumed it was an all or nothing setting. So I would set windowless_rendering_enabled to true, create my windowed browser like I currently do, then create a windowless version that just spsecifies a non-zero windowless_frame_rate?
Mayhew
Expert
 
Posts: 288
Joined: Mon Apr 18, 2011 8:02 pm

Re: Using CEF for HTML page thumbnail generation

Postby magreenblatt » Thu Jan 21, 2016 4:45 pm

Windowless rendering requires a CefRenderHandler. You can see an example in cefclient.
magreenblatt
Site Admin
 
Posts: 9836
Joined: Fri May 29, 2009 6:57 pm

Re: Using CEF for HTML page thumbnail generation

Postby Mayhew » Thu Jan 21, 2016 4:56 pm

Yes and Implement a CefRenderHandler. Perfect. I'll check out the client unit tests for OSR. Thanks.
Mayhew
Expert
 
Posts: 288
Joined: Mon Apr 18, 2011 8:02 pm

Re: Using CEF for HTML page thumbnail generation

Postby Mayhew » Tue Jan 26, 2016 9:18 pm

Just a followup for anyone interested in doing this. It works great. Some points to keep in mind.

  • You must remember to set windowless_rendering_enabled=true in your CefSettings.
  • You must provide a valid, albeit fake, url to CefFrame::LoadString() if this is how you put html data into the offscreen browser. I at first thought it just wanted a protocol like 'http' but passing that will cause the renderer process for the off-screen browser to crash. You must pass something like "http://somefakeurl.com"
  • You will need to convert the buffer from OnPaint to some valid image format if you want to say return the image to a custom resource handler. I used libpng for this. If you use libpng you must call png_set_bgr() to set the pixel format to match what CEF is storing which is BGRA.
  • If you are using a custom resource handler so you can capture a thumbnail of a page and want to return the image as a base64 encoded string. You can use CefBase64Encode which is found in cef_parser.h
  • A minimal or fully default CefClient is only needed for this browser. In fact, my class is just a subclass of CefClient and CefRenderHandler. In it I have a member to create the browser which looks like the following
    Code: Select all
    bool OffscreenBrowser::CreateBrowser() {
      CefWindowInfo window_info;

    #if defined(_WINDOWS)
      window_info.SetAsWindowless(GetDesktopWindow(), false);
    #elif defined(MAC)
      window_info.SetAsWindowless(kNullWindowHandle, false);
    #elif defined(LINUX)
      window_info.SetAsWindowless(kNullWindowHandle, false);
    #endif

      CefBrowserSettings browser_settings;
      GetBrowserSettingsHelper(&browser_settings);
      browser_settings.windowless_frame_rate = 10;
      browser_ = CefBrowserHost::CreateBrowserSync(window_info, this, "about:blank",
                                                   browser_settings, NULL);
      if (browser_.get()) {
        // your browser has been created.
      } else {
        // browser creation failed.
      }
      return (browser_.get() != NULL);
    }

Mayhew
Expert
 
Posts: 288
Joined: Mon Apr 18, 2011 8:02 pm

Re: Using CEF for HTML page thumbnail generation

Postby magreenblatt » Wed Jan 27, 2016 12:38 pm

Mayhew wrote:You must provide a valid, albeit fake, url to CefFrame::LoadString() if this is how you put html data into the offscreen browser. I at first thought it just wanted a protocol like 'http' but passing that will cause the renderer process for the off-screen browser to crash. You must pass something like "http://somefakeurl.com"

When you were using a custom scheme (e.g. not 'http') did you register it via CefApp::OnRegisterCustomSchemes in all processes?

An alternative to LoadString would be loading your HTML content via a data: URI passed to LoadURL(). You can create a data: URI using this function (from tests/cefclient/browser/test_runner.cc):

Code: Select all
std::string GetDataURI(const std::string& data,
                       const std::string& mime_type) {
  return "data:" + mime_type + ";base64," +
      CefURIEncode(CefBase64Encode(data.data(), data.size()), false).ToString();
}
magreenblatt
Site Admin
 
Posts: 9836
Joined: Fri May 29, 2009 6:57 pm

Re: Using CEF for HTML page thumbnail generation

Postby Mayhew » Wed Jan 27, 2016 4:14 pm

I wasn't using a custom scheme/protocol. I'm just using my own resource handler so I can use a REST API to grab the screenshots via XHR from within my web page housed in the main windowed browser.

Thanks for the URI idea. Is there a benefit to using LoadURL vs LoadString?
Mayhew
Expert
 
Posts: 288
Joined: Mon Apr 18, 2011 8:02 pm

Re: Using CEF for HTML page thumbnail generation

Postby magreenblatt » Wed Jan 27, 2016 4:28 pm

Mayhew wrote:Is there a benefit to using LoadURL vs LoadString?

LoadString requires that a renderer process already exists for the browser. LoadURL will work even for a browser that has been newly created.
magreenblatt
Site Admin
 
Posts: 9836
Joined: Fri May 29, 2009 6:57 pm

Re: Using CEF for HTML page thumbnail generation

Postby Mayhew » Fri Feb 19, 2016 2:08 pm

The only drawback to the data URI approach is that the page doesn't seem to be able to reference/load external content (js, css). Perhaps, because it doesn't have a root domain?

The problem I'm having now is knowing exactly when I can capture a fully rendered image. I wait for OnLoadEnd() but if I Invalidate and grab the buffer too quickly, I end up with a blank image. This obviously depends on the complexity of the page. I either have to wait some arbitrary amount of time or wait for some arbitrary number of OnPaint calls to happen. If a page has animations, there is a never ending stream of OnPaints so it is hard to know exactly when CSS rendering, etc has stabilized and it is safe to grab an image.

I haven't found any other callback I can listen to to help me determine when a page is fully loaded and rendered so I can capture an image. Any ideas on a better way to do this?
Mayhew
Expert
 
Posts: 288
Joined: Mon Apr 18, 2011 8:02 pm

Next

Return to CEF Discussion

Who is online

Users browsing this forum: No registered users and 3 guests