DesktopHTMLViewer to PDF paging

I want to export the contents of the DesktopHTMLViewer to a PDF.

I can generate a PDF using DesktopHTMLViewer.PrintToPDF(FolderItem) but .. that generates a one-page PDF of any size.

How can I set a page size, so the contents of the DesktopHTMLViewer flow from page to page?

Or maybe another way to create a PDF which then takes the contents of the DesktopHTMLViewer and flows it across pages itself?

Thanks in advance for any ideas.

Unless I missed an announcement, currently there is not a built in way to set this.

You could use(Just an option I’m throwing out there) DesktopHtmlViewer.Print and then select page size?

Forum Topic: DesktopHTMLViewer.PrintToPDF(someFile)

1 Like

The export can be HTML, TEXT and PDF, all at the same time. The user is already presented with a ā€˜SelectFolder’-dialog to store the output files in, and then also a print-dialog in between is a bit weird.

@Marc_Vos
Yes a print dialog sounds no good for the UI/UX you have for end user

Was just a thought to give an alternative as I did not know user use case :grin:

I might ditch the PDF option and let the user export the viewer as HTML. (S)He can then open it in a browser and print it to PDF.

you should have the options you need using this : https://wkhtmltopdf.org/ as a shell class in xojo.
it should be soon included in my xojo port of fpdf : Xojo FPDF - Pure Xojo PDF Generation with Full Unicode Support - verynicesw

That really needs to stop being suggested, the project has been dead for years.

From the downloads page:

The last stable series is 0.12.6, which was released on June 11, 2020

Update: Reading the License file shows that anyone who uses the library must make available ā€œCorresponding Application Codeā€. I am not a lawyer, but in spirit that means if you use the library, whether you want to or not, your app automatically becomes open source. Kind of the gotcha for LGPL that makes commercial developers wary of such libraries.

4 Likes

The only tools I found for converting html to pdf are either way too simple for my html or way too expensive.

1 Like

Mac? Windows?
You could check the functionality in my MBS Xojo Plugins.

On macOS you can get a WKWebViewMBS object for the DesktopHTMLViewer and use the printOperation method there.

For Windows we have the WebView2ControlMBS, which has a PrintToPdf method. There you can pass a WebView2PrintSettingsMBS object with the desired page size.

2 Likes

The html needs to be loaded into an html viewer which is annoying to do in a thread. There are sometimes errors from macOS (that was ā€œError when printingā€). It’s a 2 step process which is also annoying. There are really odd problems like the html saying 2 times it was printed. There are other problems. And finally it’s slow.

@Christian_Schmitz Thanks for the tip! Currently it’s for Mac only. Got it working!

Dim w As WKWebViewMBS = Window1.HTMLViewer1.WKWebViewMBS
Dim pi As NSPrintInfoMBS = NSPrintInfoMBS.sharedPrintInfo
pi.SetSaveDestination(f)
pi.horizontalPagination = pi.NSAutoPagination
pi.verticalPagination = pi.NSAutoPagination
pi.topMargin = 28
pi.rightMargin = 28
pi.bottomMargin = 28
pi.leftMargin = 28

Dim po As NSPrintOperationMBS = w.printOperation(pi)
po.showsPrintPanel = False
po.showsProgressPanel = False
po.runOperationModalForWindow(Self)

where f is pre-created FolderItem for the outputfile.

1 Like

@Christian_Schmitz Now I’m trying to run this in the background and process multiple selected rows from a listbox. This means I cannot update the HTMLviewer while looping through the the selected rows, so I want to load the HTML directly into the WKWebViewMBS and then output that to PDF.

But I get a NilObjectException when I do this:

Dim w As WKWebViewMBS
w.LoadHTML(s)

where s is a string containing some HTML code, like <table><tr><td>… etc..

What can that be?

And when I do this:

Dim w As WKWebViewMBS = Window1.HTMLViewer1.WKWebViewMBS
w.LoadHTML(s)

I do not get an error, but a PDF with empty pages, which suggests that it knows the size, but it doesn’t output the contents.

You need to do everything with timers and waiting for stuff to be finished. As I said above handling all that is annoying.

In the first snippet the w variable is nil. You need to create a WebView object.

And second one, well the loading is asynchronously. You need to have code checking status and wait until this is loaded.

The best may have a modal dialog to host the web viewer and run the process with a timer.

How do I do that?

This namely never ends:

while (w.IsLoading)
  SleepMBS(0.1)
wend

You block the main thread, so it can’t do anything.

You need to use a timer and check back later. Your current method must end.

Start loading the html:

Public Sub SetHtml(hasHtml as String, hasUUID as String)

theHtml = hasHtml
if not HasPrintStyles then EnsurePrintCSS
'EnsureReadyMarker
UUID = hasUUID
PrintCounter = 0

dim thePrefs as new WKPreferencesMBS(WKWebViewControlMBS1.WKWebView)
#Pragma BreakOnExceptions False
try
thePrefs.crossOriginResourcePolicyEnabled = True
WKWebViewControlMBS1.WKWebView.Configuration.limitsNavigationsToAppBoundDomains = True
catch err as NSExceptionMBS
'ignore, not available on bug sir
end try
#Pragma BreakOnExceptions True

if Thread.Current <> nil then
HtmlTimer.RunMode = Timer.RunModes.Single
else
WKWebViewControlMBS1.LoadHTML(theHtml, ā€œā€)
end if
DocumentTimer.RunMode = Timer.RunModes.Single

End Sub

Load the html:

Sub Action() Handles Action

Globals.theErrorLog.LogitemToConsole(currentMethodName + " 1")
WKWebViewControlMBS1.LoadHTML(theHtml, ā€œā€)

HtmlTimer.RunMode = Timer.RunModes.Off

Globals.theErrorLog.LogitemToConsole(currentMethodName + " 2")
End Sub

Html was loaded:

Sub didFinishNavigation(navigation as WKNavigationMBS) Handles didFinishNavigation

#Pragma Unused navigation

Globals.theErrorLog.LogitemToConsole(currentMethodName + " 1")
'as soon as the html viewer has loaded data it can be printed
DocumentTimer.RunMode = Timer.RunModes.Off

me.EvaluateJavaScript(BlockQuoteCSSScript)

TempItem = Nil

'duplicate
if theHtml = ā€œā€ then
Break
Return
end if


Print

Globals.theErrorLog.LogitemToConsole(currentMethodName + " 2")
End Sub

And then the html can be printed in the print method.

1 Like

Ok, so I find this is not doable for looping through many selected rows of a listbox - way to complex for me with timers and such. I’ll allow PDF output only when just 1 row is selected.

Thanks for all your thoughts and input.