How to print WKWebView in Sequoia?

I want to print the content of a WKWebView. I've done some searching, and many people have struggled with this over the years. Some claimed success, but their solutions don't work for me. One person created images for each pages and printed that, but then if you were to print to PDF, you'd get a PDF containing images rather than text.

If I just call the printView(_:)) method of the view, I get blank pages.

With the following more elaborate code, I get a partial printout, 11 out of what should be about 13 pages.

let info = NSPrintInfo.shared
info.topMargin = 72.0;
info.bottomMargin = 72.0;
info.leftMargin = 72.0;
info.rightMargin = 72.0;
info.isVerticallyCentered = false;
info.isHorizontallyCentered = false;
info.horizontalPagination = .fit;
info.verticalPagination = .automatic;

let printOp = webView!.printOperation( with: info )
printOp.canSpawnSeparateThread = true
printOp.view?.frame = NSMakeRect( 0, 0,
    info.paperSize.width, info.paperSize.height )

printOp.runModal(for: webView.window!, delegate: self,
    didRun: nil, contextInfo: nil )

When I run the above under the debugger, I see console messages saying

CGContextClipToRect: invalid context 0x0.

Once the print dialog appears, if I touch (but not change) the selected printer, then the page count changes to the correct value.

My code is much more elaborate than that.

To start with, I create my own print operation via "printOperationWithSettings:error:" (still Objective-C).

I don't call "runModal" at all.

WebView is really tricky with printing, at least for me. I create a new view just for printing, then load it, and print in my load completion using NSDocument's "printDocumentWithSettings:...".

And of course, all this still uses the long-deprecated WebView class. I don't know if Apple ever added print support WKWebView. Still works in Tahoe. You seem to be trying to use the new logic.

I don't plan on using WKWebView in the future anyway. So if Apple eventually removes the old WebView I won't be affected.

In general, the NSPrintOperation code path you're attempting is known to work.

We'd be interested to see a test app/content where it is failing in the way you describe.

That said:

One person created images for each pages and printed that, but then if you were to print to PDF, you'd get a PDF containing images rather than text.

Which is exactly what the createPDF: API is for.

That asks the WKWebView to draw into a PDF the exact same way it would draw to the screen or to print, and it results in a nice vectorized PDF instead of rasterized images.

I tried your code in a small app with the printer simulator printing one of our release notes pages. Though I did see three blank pages at the end of the print, it printed ok. Can you provide some URLs to content you're having trouble printing?

Which is exactly what the createPDF: API is for.

That asks the WKWebView to draw into a PDF the exact same way it would draw to the screen or to print, and it results in a nice vectorized PDF instead of rasterized images.

Yes, I tried that, but it produced a PDF with one ridiculously tall page. Is there another way to produce a PDF with pagination?

I'll work on distilling this down to a good test app for a bug report.

Can you provide some URLs to content you're having trouble printing?

My content is available here:

https://github.com/jwwalker/PlainCalc/blob/master/PlainCalc3/Resources/Help/PlainCalcHelp.md.html

This is a "MarkDeep" file. It mostly looks like MarkDown, but functions as HTML, and displays correctly in a WKWebView.

I swapped out my old deprecated code for the new WKWebView code similar to what you're using. It was surprisingly easy. It does require macOS 11. And I lose my custom headers and footers. But otherwise, it works fine.

In my code, I'm using fit pagination for both horizontal and vertical. I think that may fix your ridiculously tall pages. Safari also generates such pages when exporting to PDF, so perhaps "automatic" is what's doing that.

Otherwise, I have a document-based app and I don't call runModal on the print operation. As I said above, I'm using NSDocument's "printDocumentWithSettings:..." after the web view has completely loaded.

@Etresoft :

In my code, I'm using fit pagination for both horizontal and vertical. I think that may fix your ridiculously tall pages. Safari also generates such pages when exporting to PDF, so perhaps "automatic" is what's doing that.

When you export to PDF, that's not the same as printing to PDF. There is no opportunity to set a pagination option.

I was just about to give up and tell you to create a demo project that just opened one web view and printed it. But I thought I should try that first myself. I did and also got 2 blank pages.

There are lots and lots of possible options. But in this demo app, here is how I fixed it using a Swift Storyboard document-based app:

In Xcode Interface Builder, find the print menu item. Change its sent action from "print:" to "printDocument:" in NSResponder.

Observe that the app now prints correctly. I tried this with your sample and got 12 pages, including the last. Nothing seems to be missing except horizontal overview inside a div in the web view, but that's a different problem.

I poked around for a bit. It looks like the old XIB templates in Xcode used "printDocument:" as the sent action. For some reason, in the newer Storyboard templates this was changed to just "print:", which doesn't seem to work. No clue about SwiftUI.

I have a newer app in Swift that isn't document-based. In this app, I seem to have already changed the print action to "printView:", which I implemented in my view controller. My old Objective-C app uses printDocument, which the XIB template also uses.

So this solves your blank page problem with "printView:". This is also complicated by the NSView and NSWindow Objective-C methods "print:" that are re-defined in Swift as "printView:" and "printWindow:".

I'm still not sure why you're getting partial output. My demo app uses your code and prints fine. I played around with it a bit, putting the print method in both the view controller and the document. Worked fine either place. Perhaps the demo app would be a good place to start after all.

horizontal overview inside a div

No idea what you mean by that?

We'd be interested to see a test app/content where it is failing in the way you describe.

I've posted a test app here:

https://github.com/jwwalker/WKWebView-print-test

By the way, although I titled this post with "Sequoia", I just now tested on a different machine running a Tahoe 26.1 beta, and observed the same behavior.

Bug report: FB20581927

Hello and thank you for providing a sample. I was able to reproduce your results using the sample. It looks like the number of pages is not being calculated properly and the printed content is getting truncated because of that.

Thank you for filing a bug report (FB20581927). At this time your bug remains open and under investigation. Please continue to test your software in release and pre-release versions of system software made available to you through your developer.apple.com account. When you do, add details about that into your bug report to keep it up to date. Please see https://developer.apple.com/download/.

No idea what you mean by that?

It looks like an autocorrection. It should have been "overflow".

But that's irrelevant. I was just confused by your GitHub link. It was GitHub that printed properly. Now that I know what you're doing, I can reproduce the problem.

It seems clear that it's the dynamic markdown that's causing all the trouble. If you look closely in the print preview, you can see that the top margin on page 1 is wrong. The first element is right up at the top of the page. That throws off the page count because the full print does it properly.

I was also able to easily hack up my older app and have it swap out its own data for your markdown-html. The first time I tried it, both the preview and the output came out short, at only 8 pages. But all subsequent attempts worked properly, printing all 13 pages (with my headers). I think WebKit is caching this data. That's why it only fails the first time.

When I swapped out the legacy for the new, all I could only ever get was a single page. I think the new renders much faster and that's why it only has a single page at print time. This old app does both the legacy and the new WebKit, so it has a lot of overhead. That explains why I can only get a single page whereas a simple app gets 11.

I'm taking care to avoid printing until the document ready event in Javascript. So this markdown parsing must be happening after that. That would explain these results. It simply hasn't finished rendering the page when it gets the page count.

And I ran the rendered HTML from Safari through the W3C HTML validator. It's not valid HTML. Like any browser, WebKit tries to compensate. But combined with the Javscript rendering, it seems to be too much for it to handle.

Good luck on your bug report. But until that's fixed, I recommend static HTML.

How to print WKWebView in Sequoia?
 
 
Q