How does one Print all WKWebView On AND Offscreen content OSX and iOS

前端 未结 4 1018
予麋鹿
予麋鹿 2020-12-08 10:13

This question is about printing ALL content (including off screen content) of WKWebView. Currently (still, as of iOS 10.2 or OSX 10.12) there is NO working solution

相关标签:
4条回答
  • 2020-12-08 10:24

    NOTE from OP: This solution has the same issue that has existed for quite some time. If one uses the unpublished method and ignores the errors thrown and sets margins, paper size, and the following flags on NSPrintInfo: orientation = NSPaperOrientationLandscape and horizontalPagination = NSPrintingPaginationModeAutomatic, the result is the same as it has been for quite some time and which is a print dialog displays and the print preview has ONLY 1 page and which is missing the content off to the right. WKWebView does not understand how to paginate content off to the right.

    There may be a solution worth investigating by subclassing the WKWebView and overriding knowsPageRange: and some of the other print related methods for a View. Also this might work in the vertical direction. I didn't try that as my use case is the need to correctly paginate in the horizontal direction with HTML content in TABLE columns columns too far right to fit on one page. The above mentioned settings DID used to work in the WebView class then subsequently broke in both WebView and WKWebView - End NOTE from OP

    I've successfully used the SPI -[WKWebView _printOperationWithPrintInfo:] passing the usual [NSPrintInfo sharedPrintInfo]. Note that you CAN'T use -runOperation on the returned NSPrintOperation. You must use -runOperationModalForWindow:.... which is quite similar. The problem resides in the WebKit internals that expects a running runloop and a preview to be made internally to know the number of pages.

    It definitely works with offscreen content, if what you mean by offscreen is "not fully displayed on screen". I still have a WKWebView displayed in a window, but it's very tiny and only displays a very short fraction of the entire webview content (21 A4 pages!). Hope this helps!

    PS: Tested on 10.12, 10.14 and 10.15. Code is like this:

         SEL printSelector = NSSelectorFromString(@"_printOperationWithPrintInfo:"); // This is SPI on WKWebView. Apparently existing since 10.11 ?
    
         NSMutableDictionary *printInfoDict = [[[NSPrintInfo sharedPrintInfo] dictionary] mutableCopy];
         printInfoDict[NSPrintJobDisposition] = NSPrintSaveJob; // means you want a PDF file, not printing to a real printer.
         printInfoDict[NSPrintJobSavingURL] = [NSURL fileURLWithPath:[@"~/Desktop/wkwebview_print_test.pdf" stringByExpandingTildeInPath]]; // path of the generated pdf file
         printInfoDict[NSPrintDetailedErrorReporting] = @YES; // not necessary         
    
         // customize the layout of the "printing"
         NSPrintInfo *customPrintInfo = [[NSPrintInfo alloc] initWithDictionary:printInfoDict]; 
         [customPrintInfo setHorizontalPagination: NSPrintingPaginationModeAutomatic];
         [customPrintInfo setVerticalPagination: NSPrintingPaginationModeAutomatic];
         [customPrintInfo setVerticallyCentered:NO];
         [customPrintInfo setHorizontallyCentered:NO];
         customPrintInfo.leftMargin = 0;
         customPrintInfo.rightMargin = 0;
         customPrintInfo.topMargin = 5;
         customPrintInfo.bottomMargin = 5;
    
         NSPrintOperation *printOperation = (NSPrintOperation*) [_webView performSelector:printSelector withObject:customPrintInfo];
    
         [printOperation setShowsPrintPanel:NO];
         [printOperation setShowsProgressPanel:NO];
    
    //    BOOL printSuccess = [printOperation runOperation]; // THIS DOES NOT WORK WITH WKWEBVIEW! Use runOperationModalForWindow: instead (asynchronous)
         [printOperation runOperationModalForWindow:self.window delegate:self didRunSelector:@selector(printOperationDidRun:success:contextInfo:) contextInfo:nil]; // THIS WILL WORK, but is async
    
    0 讨论(0)
  • 2020-12-08 10:24

    In iOS, I pass UIView.viewPrintFormatter() to UIActivityViewController to allow users to print everything

    let myShare = [webView.viewPrintFormatter()]
    let avc = UIActivityViewController(activityItems: myShare, applicationActivities: nil)
    
    present(avc, animated: true, completion: nil)
    
    0 讨论(0)
  • 2020-12-08 10:27

    I have exhausted every possible way to print the WKWebView directly with no success. The only workaround I can think of would be to convert the web page to a PDF object and then print said object. I will update with code if I get the workaround to work.

    0 讨论(0)
  • 2020-12-08 10:42

    This isn't the correct answer because Apple needs to supply the correct answer with a working print method or .evaluateJavaScript("window.print()", completionHandler: nil)

    But I have a stupid solution that "works" for me and perhaps it will help other people with a work around until then.

    Step 1: Grab the HTML and fixup the <body> tag with <body onload='window.print()'>. If you are fetching the html from someplace and not loading your own, you'll want to use some regular expressions. I won't go into that.

    Step 2: Save the html in a file someplace and keep the full path in a variable. In my example: filename

    Step 3: Wire your print button to this code:

    RunCommand(command: "/usr/bin/open \(filename)")
    

    See code for RunCommand below. This leaves a dumb safari window laying around, but it makes it possible to get something printed without saving to a file and then remembering where you stuck it so you can open it with Safari on your own to do the printing.

    func RunCommand(command: String)  -> (success: Bool, result: String) {
            let cc = command.components(separatedBy: " ")
            let process = Process()
            process.launchPath = cc[0]
            var cp: [String] = []
            for i in (1..<cc.count) {
                cp.append(cc[i])
            }
            process.arguments = cp
            let pipe = Pipe()
            process.standardOutput = pipe
            process.launch()
            let data = pipe.fileHandleForReading.readDataToEndOfFile()
            if (data.count > 0) {
                let output = String(data: data, encoding: String.Encoding.utf8)
                // let output = NSString(data: data, encoding: String.Encoding.utf8.rawValue)
                return (success: true, result: output!)
            }
            return (success: true, result: "")
    
        }
    

    It would be really nice if Apple could fix this. I have an in-house app that does all its reporting in HTML and reports are something you kind of want to print.

    0 讨论(0)
提交回复
热议问题