Welcome Guest Search | Active Topics | Sign In | Register

PdfDocument.Print method Options
Civica Pty Ltd
Posted: Wednesday, January 18, 2017 2:44:01 AM
Rank: Member
Groups: Member

Joined: 7/9/2014
Posts: 20
Hi,

I've got to start printing PDFs - some will come from Microsoft Word, some will be from EO.PDF's HtmlToPdf feature, and some will come from elsewhere (photocopiers, etc).

I happened to notice in the EO.PDF 2017 release there's a new Print() method on PdfDocument. This is perfect.

However... There's no way to know when the print has finished. I know things are queued in Windows print queues - I literally mean that I want to get one of your `ManualTask` instances, or something like it, to know when the print job has been rendered (or perhaps thrown an error) so that I can do things like release memory, close my process, report back to the user, etc.

To get a better understanding of printing I let ReSharper show me some of the code behind the Print() method. From what I can see it looks like that Print() method creates a new PdfViewer, slaps it into a WinForms form, loads the PDF into it and then does the print.

So I wrote my own version of that code - it's pretty much identical.
The only difference is that I added some reflection to get to the underlying WebView of the PdfViewer (which itself is just a WebViewHost) and then added a listener to the AfterPrint event.

However I'm finding that AfterPrint event is not getting called. This is despite the print occurring succesfully.

In case I'm making this much harder than required, I want to print PDFs in two circumstances
1. Create a simple console app that accepts a printer name, a printer bin and then a list of PDFs on the command line. It'll load each PDF into a byte array and print to that printer. The console app will run unattended. I'll need to at least wait until the generation of the print job is complete before letting it terminate.

2. Similar to #1, but it might be a longer living app, running as a Windows service. It'll need to report back to a web service when it has succesfully generated a print job or if some error happened. So again I'll need to at least know if printing was successful.

After reading the code I'm also wary of knowing whether or not there could be memory leaks. A new WinForms form is created to host the PdfViewer control and a listener is added to PdfViewer's IsReadyChanged. It may well be the case that this all gets garbage collected but with so many moving parts, threads, etc I just want to be sure. There's no Close() or Dispose() method on PdfDocument to get rid of the ThreadRunner instance for example. That may well be fine - again I'm just wanting to be sure that the ThreadRunner doesn't register itself in some static list. For example, I was previously bitten by not calling HtmlToPdf.ClearResult() in production and ended up with several GB of RAM used in my small website - I wasn't using the static method variant of doing that conversion so it never occurred to me to need to clear some statically held per-thread state.

I'm happy with a workaround or to be shown some setting that I've missed. It may well be that I need to do my own WebView instance, load a PDF into it, ensure that's finished (see https://www.essentialobjects.com/forum/postst9675_EOWebBrowser--Detect-PDF-Has-Finished-Loading.aspx), hook the print event and then be ready to go.

Thank you very much for your help!



Relevant code below - some bits like the Arguments class haven't been included since it just indicates duplex, printer name, etc settings. The Log calls are just to Serilog and can be commented out.


Code: C#
public static class PdfPrint
    {
        public static Task Print(Arguments settings)
        {
            var result = new TaskCompletionSource<object>();
            try
            {
                //configuration of printer settings            
                var printerSettings = new PrinterSettings()
                {
                    Copies = (short) settings.Copies,
                    Duplex = settings.Duplex.ToSystemDrawing(settings.Orientation == Orientation.Landscape),
                    PrinterName = settings.DestinationPrinterName
                };

                //and page settings
                var pageSettings = new PageSettings()
                {
                    Color = settings.Colour,
                    Landscape = settings.Orientation == Orientation.Landscape,
                    PaperSource = settings.ToPaperSource(),
                    //PrinterSettings = printerSettings
                };

                Log.Information("Printer and page settings {@printerSettings}  {@pageSettings}", printerSettings, pageSettings);

                //grab the PDF contents
                var bytes = File.ReadAllBytes(settings.SourceFileName);

                Log.Information("PDF bytes loaded {bytes}", bytes.Length);

                //begin the tedious task of handling EO.PDF rendering & printing

                var manualTask = new ManualTask();
                ThreadRunner threadRunner = null;
                DoxPdfViewer pdfViewer = null;

                //Monitor code not strictly necessary but doesn't hurt
                lock (manualTask)
                {
                    if (threadRunner == null)
                    {
                        threadRunner = new ThreadRunner();
                    }

                    Log.Information("EO.PDF ThreadRunner created");

                    threadRunner.Send(() =>
                    {
                        pdfViewer = new DoxPdfViewer();
                        pdfViewer.IsReadyChanged += (sender, args) =>
                        {
                            if (pdfViewer.IsReady)
                            {
                                Log.Information("EO.PDF PdfViewer is ready");
                                manualTask.Complete();
                            }
                            else
                            {
                                Log.Warning("EO.PDF PdfViewer ReadyChanged fired but still not ready");
                            }
                        };
                        pdfViewer.Dock = DockStyle.Fill;
                        var form = new Form()
                        {
                            ShowInTaskbar = false,
                            FormBorderStyle = FormBorderStyle.None,
                            WindowState = FormWindowState.Minimized,
                            Location = new Point(-10000, -10000),
                            Size = new Size(600, 400)
                        };
                        form.Controls.Add(pdfViewer);
                        form.Show();
                    });
                }
                if (!manualTask.WaitOne(5000))
                {
                    Log.Error("EO.PDF PdfViewer initialisation took too long");
                    throw new Exception("Timed out waiting for pdf viewer to initialise");
                }

                Log.Debug("EO.PDF PdfViewer initialisation complete - moving on to print phase");

                threadRunner.Post(() =>
                {
                    Log.Information("EO.PDF PdfViewer commencing PDF load");
                    var loadWait = pdfViewer.Load(bytes);
                    
                    loadWait.OnDone(() =>
                    {
                        Log.Information("EO.PDF PdfViewer PDF load completed - proceeding with print");
                        var webView = pdfViewer.WebView;
                        webView.AfterPrint += (sender, afterPrintEventArgs) =>
                        {
                            Log.Information("EO.PDF PdfViewer WebView indicated print call has completed with status: {afterPrint}", afterPrintEventArgs.Result);
                            if (afterPrintEventArgs.Result == PrintResult.OK)
                            {
                                result.SetResult(null);
                            }
                            else
                            {
                                result.SetException(new Exception("EO.PDF PdfViewer WebView indicated print failure " + afterPrintEventArgs.Result.ToString()));
                            }
                        };
                        
                        //might this throw an error itself - I could add a try/catch here to properly propogate an exception into my TaskCompletionSource
                        pdfViewer.Print(printerSettings, pageSettings);
                        Log.Debug("EO.PDF PdfViewer print command issued");
                    });
                });
            }
            catch (Exception e)
            {
                result.SetException(e);
            }

            return result.Task;
        }
        
    }


public class DoxPdfViewer:PdfViewer
    {
        private static Lazy<MethodInfo> webViewHostPropertyGetter;

        static DoxPdfViewer()
        {
            webViewHostPropertyGetter = new Lazy<MethodInfo>(() => {
                var type = typeof(WebViewHost);
                var method = type
                    .GetProperties(BindingFlags.Instance | BindingFlags.NonPublic)
                    .Where(p => p.PropertyType == typeof(WebView))
                    .First()
                    .GetGetMethod(true);
                return method;
            });
        }

        public DoxPdfViewer()
        {
            this.webView = new Lazy<WebView>(() =>
            {
                var getter = webViewHostPropertyGetter.Value;
                
                //grab its value
                var webViewFieldValue = (WebView)getter.Invoke(this, new object[0]);
                return webViewFieldValue;
            });
        }

        private readonly Lazy<WebView> webView;
        public WebView WebView => this.webView.Value;
    }
eo_support
Posted: Wednesday, January 18, 2017 12:07:57 PM
Rank: Administration
Groups: Administration

Joined: 5/27/2007
Posts: 24,067
Hi,

We will look into it and see if we can get AfterPrint event working for PDF. The actual printing for PDF is done by the plugin so it does not follow the exact path as the normal WebView printing flow.

As to memory issue, there will be only a single ThreadRunner and it is (and the corresponding Form) is not destroyed until your process exit or AppDomain is unloaded. This is by design because given the fact that printing is a relatively slow task and it's unlikely that you will numerous printers on the same system that you want to print simultaneously. As a result, all printing jobs are serialized through the single ThreadRunner object.

Thanks!
Civica Pty Ltd
Posted: Wednesday, January 18, 2017 5:32:13 PM
Rank: Member
Groups: Member

Joined: 7/9/2014
Posts: 20
Hi,

Great to hear. Anything you can do for some AfterPrint would be appreciated. A ManualTask being returned by the Print() method would be splendid.

Thanks for the discussion about memory and resources. It makes sense to serialise the printing since, as you say, it's unlikely to have >1 going at a time.

Looking forward to hearing from you. With the workaround or code change to be able to detect printing conclusion, do you think it's something you could provide in the next couple of days or is it likely to be weeks?

Thank you.
eo_support
Posted: Wednesday, January 18, 2017 5:33:35 PM
Rank: Administration
Groups: Administration

Joined: 5/27/2007
Posts: 24,067
Yes. We should be able to have this either this week or next week.

Thanks!
eo_support
Posted: Thursday, January 19, 2017 5:07:51 PM
Rank: Administration
Groups: Administration

Joined: 5/27/2007
Posts: 24,067
Hi,

This is just to let you know that we have posted build .39 that changed PdfDocument.Print to return a WaitableTask. Please download it from our download page and let us know how it goes. Please note that it returns EO.Base.WaitableTask, which is a new class that servers as the base class of the original EO.WebBrowser.WaitableTask.

Thanks!
Civica Pty Ltd
Posted: Thursday, January 19, 2017 10:05:13 PM
Rank: Member
Groups: Member

Joined: 7/9/2014
Posts: 20
That's amazing. I really, really appreciate it. I'll fetch the new release and give it a try.
Civica Pty Ltd
Posted: Sunday, January 22, 2017 7:18:28 AM
Rank: Member
Groups: Member

Joined: 7/9/2014
Posts: 20
Hi,

I've only done a bit of limited test with the .39 release and it seems to have done the trick.

I noticed there's a .40 release (and the .39 release is missing) so I'm about to install that just in case there were further tweaks.

Anyway, I think you can consider this solved. If I have further trouble I'll open a new thread.

Thanks very much for the prompt help!
eo_support
Posted: Monday, January 23, 2017 7:25:25 AM
Rank: Administration
Groups: Administration

Joined: 5/27/2007
Posts: 24,067
You are very welcome. Thanks for confirming. Please let us know if there is anything else.


You cannot post new topics in this forum.
You cannot reply to topics in this forum.
You cannot delete your posts in this forum.
You cannot edit your posts in this forum.
You cannot create polls in this forum.
You cannot vote in polls in this forum.