Printing!

I've spent the better part of a week trying to understand why plugins were not printing properly from within WebKit. While this is probably not a critical topic for most WebKit users, I am using the plugin infrastructure to allow WebKit to host a UI widget from my main application. The really cool thing about this design is that I can call up a WebKit view in my main program, display some custom UI elements hosted by my main program inside the WebView, and even have the plugin (launched by WebKit) access internal objects in my running application!

Unfortunately, the whole grand vision teetered on the brink of failure, because I could not get the hosted widgets to appear in a printed copy of my WebKit instance. This was a real annoyance, after I'd *already* spent the better part of a week getting the main printing infrastructure working under WinCairo in the first place.

The plugins, which displayed so nicely on-screen, didn't show up at all in hard copy -- even when there was nothing else being displayed on the screen. Fearing some kind of strange interaction between the third-part binary I was embedding in my plugin, I wrote an extremely simple example NPAPI plugin (http://files.me.com/bfulgham/o7vlik), which renders a box in the region supplied by the calling web page. But again, the plugin rendered perfectly on-screen, but did not display in the printout.

It wasn't until I monkeyed with the margin, header, and footer that I saw tiny little rectangles being drawn in the upper-left-hand corner of the page. The plugins were getting drawn, but they were extremely tiny, and they were in the wrong location.

Searching through the code, I found the following in PluginViewWin:

// The plugin expects the DC to be in client coordinates, so we translate
// the DC to make that so.
XFORM transform;
transform.eDx = locationInWindow.x();
transform.eDy = locationInWindow.y();


Of course! This was ignoring the scaling effects of printing to a page (~1600 dpi) versus on-screen (~96 dpi). I also discovered that header and footer extents were being ignored, which was why my tiny rectangles were getting drawn in the upper-left-hand corner of the page -- and only if I made the header big enough that the web page did not cover the region.

So, I happily filed Bug 32909 and figured that would be the end of it.

Only, it wasn't. Once I corrected the scaling and translation issues, I found that although my on-screen display was correct, my print output continued to omit the plugin. Even worse, I could sometimes see the edges of rectangles peeking from around rendered portions of the page (when margin or header/footer sizes were manipulated). In at least one case, I was able to click on the edge of the rectangle in PDF Viewer, copy the object (hidden behind a blank area), and paste the *full* rectangle into another document. WTF?

After more trial and error, I discovered that changing the rendering type used for the page from cairo_win32_printing_surface_t to cairo_win32_surface_t my plugins *did* display properly! The only problem, was that my printing operations went from using very little memory, to using over 100 MB of RAM to print.

A bit of correspondence with the Cairo team helped me see that the problem was due to the dual nature of the drawing operations. The plugin is handed a Windows device context handle to the printing surface. But the majority of the drawing events in WinCairo are done against a cairo_t context. When this context is backed by a cairo_win32_printing_surface_t, drawing operations become slightly order-dependent. Drawing directly to the printer context (as my plugin was doing) would get drawn to the printer device context immediately. Later, the entire Cairo surface was shipped to the printer, overwriting (actually *overlaying*) the previous drawing operations.

And that explained it: when this multiple layered printing context was played back as a PDF file, the plugin contents were at the lowest Z-level, but the main page rendering was sitting on top of them making it appear as though the plugin was not rendering at all.

At last, a workable solution was found. Prior to the plugin drawing, I had to add the following logic:

#if PLATFORM(CAIRO)
// Must flush drawings up to this point to the backing metafile, otherwise the
// plugin region will be overwritten with any clear regions specified in the
// cairo-controlled portions of the rendering.
PlatformGraphicsContext* ctx = context->platformContext();
cairo_show_page(ctx);
#endif


Now, each time I go to print a plugin, the Cairo context is flushed to the printing surface. Then the plugin is drawn (on *top* of this existing rendering), then control returns to WebKit for more Cairo rendering, followed by more flushes (if more plugins are being drawn), and so forth.

In this way, I was finally able to get a correct print version of my plugin-containing page. See Bug 33022.

I also pushed some general printing adjustments to the WInLauncher program to help other see the correct steps to take to execute printing. Bug 33537.

Comments

Popular Posts