How can I get information about the current print job from within a document print listener?

I have a Basic listener which has been linked to the document print event via Tools/Customize.
When I print the document the listener is called three times with an apparently identical argument, viz: EventName: “OnPrint” (which I already know), ViewController: Null, Supplement: Empty. This leaves Source, which is a reference to the current document.
What I want to know is:

  1. How do I get the information that was entered in the Print dialog - printer name, number of copies, etc…
  2. How to tell LibreOffice not to use its default printing procedure, as printing will be handled by the print listener.
  3. Which of the three call to the listener is currently being processed. I can probably handle this with a global variable, although it would be an unsightly bodge, but it would be interesting to know at which point in the processing of the print job each of these calls was made.

To explain why I want to do this, I have what amounts to a serial number in one of the cells of the worksheet, which should be incremented each time the sheet is printed. To do this I had planned to intercept the standard print procedure, examine the number of copies field, then print as many copies as required individually, incrementing the serial number cell each time.

If anyone can suggest an alternative approach then I would be interested but, since, apart from getting the print job parameters and inhibiting default printing, the entire job could be done in around ten lines of code, it seems unlikely that any alternative could be simpler.

  1. Personally I am not the one being best prepared to answer this question, but …
  2. Did you study the documentations for the services an interfaces listed under LibreOffice: com::sun::star::view Module Reference and having names starting with “Print” or “XPrint”?
  3. I wouldn’t expect a listener called by a running print job to be the right place for choosing the printer or for otherwise declining the “default printing procedure”.
  4. You might also post the code you already have/tried.

Your link refers to a document which looks as if it was created by Doxygen from the LibreOffice source. I have looked at several of these pages, although I cannot remember whether this was amongst them. If I were planning to write an extension in “C” and was prepared to spend a substantial time studying then then I would probably find it very useful.

Regarding your point 2, if the listener cannot modifier the behaviour of the event that it handles then what [invective redacted] use is it in the first place. To illustrate, you might want to prevent printing to an inappropriate printer but all you can actually do is pop up a message box saying “You have just printed your cash flow analysis on a sheet of sticky labels. Ha! Ha! Ha!”
As for code I have already tried, there isn’t any, because I don’t have the necessary information (the code to update the cell is trivial, works, and irrelevant to this query). What I have done is to run DBG_methods and DBG_properties on the “Source” field, and search for plausible sources of information.

An event notification that does not provide details of the event itself is useless. Imagine a mouse event that only tells its listener: “OnMouse”. Does this refer to a left or right click, or hovering over a control or simply that the mouse cursor moved over part of the application window? The same type of argument applies to the “OnPrint” event, or indeed any event.
I assume that this is as obvious to whoever created the event mechanism as it is to me and that a means of obtaining this information has been provided, which I have failed to find.

Would you expect me to advocate everything concerning LibreOffice telling you that is the best possible world?

Anyway, the .Source of the event passed to the listener supports XPrintJob. There is relevant information - and you can cancel the job.

If you want to use a different printer, you may need to set it for the printable object, and to start a new printjob.

Over.

1 Like

Thank you for researching this for me. Unfortunately I am still stuck, as I cannot work out how to get access to the XPrintJob. From your comment I would expect to need something like:

sub print_listener(ev as object)	' ev is a com.sun.star.document.DocumentEvent
	dim src as object
	dim xpj as object
	
	src = ev.source					' src is an ScModelObj object.
	xpj = src.XPrintJob()
	
end sub

should work, nor have I been able to find any documentation for XPrintJob or PrintJob in the context of LibreOffice Basic.

Event.Source is the object “exporting” the interface XPrintJob. Interfaces don’t need to be explicitly addressed. They are avaiable if a supported service gives access.
With src = Event.Source you should be able to use the methods and properties specified n the documentation as the XPrintJob interface like in src.cancelJob().
Again: I’m not an expert in the filed. I even can’t remember when I last time used a PrintJobListener.

2 Likes

In general, see Information and resources for LibreOffice macros - The Document Foundation Wiki for resources on LO BASIC programming. If you need to figure something out, using MRI (or XRay) is simply a must.

XRay: Bernard Marcelly Web site
MRI: Releases · hanya/MRI · GitHub

In Andrew Pitonyak’s book (www.pitonyak.org/oo.php the OOME4) he actually talks about loading the print dialog and reading values as a workaround that evidently was necessary way back in the day. Sections 13.14/13.15 etc. (page 340 or so). I can’t say if this would hold answers for your specific question, but since you haven’t mentioned it, it seems you haven’t read it yet to see.

1 Like

Got it!
I apologise for my obtuseness. Basically my concept of an interface came from Java, where a class implementing a particular interface guarantees that it implements the methods defined by that interface, but those methods are part of the class, the interface tells you that certain methods are available but does no, itself, contribute any code. The actual methods must be implemented independently by each class that supports the interface.
Here, it seems, the interface has its own code. Presumably every object that supports the interface does so by means of a standard protocol.
I was assuming that DBG_methods was listing every method that could be accessed through that object, when I couldn’t find the method I wanted I looked for it in another object that I could reach via the original object.
I think I may have finally cleared a conceptual hurdle after spending several days falling on my nose.
Thanks.

No, the interfaces do not have code.
The difference is not in if the code is in interface vs in implementation (it’s in implementations, and Java had a lot of influence on OOo API), but in the language used (Basic is a language with weak/dynamic typing, and so you have not to cast objects to specific types to make related members available; the same would be true for Python, but if you used Java/C++, you would need the explicit casts and/or queries to reference the specific interface of the object).

I am just reading about the CreateUnoListener function which seems to accord with what you are saying, that is, the listener method must implement itself the methods defined. I was confused because Lupp seemed to be implying that the ScModelObj object implemented the XPrinterJob interface but in fact it does not, though it does implement the XPrintable interface.
I am still working on this, but I suspect that I will find that connecting a print listener via Tools/Customize is pointless, as the listener will not receive information about print jobs.

You must be referring to this. No, @Lupp didn’t imply that “ScModelObj object implemented the XPrinterJob interface”; I believe that he only implied that src was a different object (overlooked that comment? Had a different debug result for some reason?)

Disclaimer: I didn’t check myself, neither I looked up the code - so I don’t know which is the true object passed there. (Could it be related to the fixes to tdf#117280, landed in 7.3?)

Also possible, that we might review the decision we made in the said bug, and replace the “Supplement” from the com.sun.star.view.PrintableState value into a reference to the respective XPrintJob maybe. Needs a bug report, and a review to understand if that’s possible and desirable.

I think that Is there a way to get the current sheet name WITHOUT using "ActiveSheet"? - #12 by sokol92 is the better reference. The code by @sokol92 registers the listener, that actually receives the XPrintJob in event’s Source.

This has been the root of my problem. If a print handler is installed via Tools/Customize/Events then it will receive a com.sun.star.document.DocumentEvent. When the listener is created with CreateUnoListener and installed by AddPrintJobListener then it receives a com.sun.star.view.PrintJobEvent which gives access to the print job.

In fact I should not have been using the Tools/ method, as I want the handler to be used for one document only and Tools/Customize would seem to be intended for a global handler.

No - it depends on which “Save in:” variant you choose in the customization dialog.
And one of the events here (e.g., document opening) may be used to call the registration of the listener.

Yes, I realised that when I was finding out how to run a macro when a document is loaded. I had assumed that it was global because several of the options seem to be only applicable in a global context. What, for example, would happen if I save “Start application” in a document?

I realise that this is the wrong place, so I will try to be brief. The fundamental problem for me in this instance and generally, is documentation. Not tat it is inadequate or badly written, but that the basic design principle is flawed or inappropriate. The purpose of a reference manual for a product such as LO, or for that matter a microwave oven, is to describe fully what the product does, and where it is relevant, how it does it. It is emphatically is not to tell the user how to use the product. The single essential criterion for an adequate reference manual for, for example LO Basic, is that it should be possible to write any legal macro, using only the manual, and know that it will work.

When I use LO Basic most of my time seems to be spent writing small code fragments to discover what is actually going on. When I encounter a new structure I am likely to run DBG_methods and DBG_properties on it, tabulate and sort the results and store them in a document. This is of limited use, as there are no descriptions and the types are usually generic structures, but at least I have a list of names.

During the course of investigating this issue I have found three quite different methods for setting up a printer event listener, and I am fairly sure that there is at least one more, of the three only one was useful but I failed to discover this from the documentation. An adequate programmer’s manual would list all the members of every structure and every type of every argument and return value of every method. With that information I would see that the Tools/Customize method either was unsuitable because it did not provide the print job information, or had a bug because it did not match the documentation.

You have wrong expectations. You consider what you use to be a kind of “supported product” that is expected to be prepared in the perfect way for end user consumption; you apply the same level of expectations as you would to a purchased microwave oven.

And while I agree that providing such level of support (including documentation) would be great, the expectation itself is wrong: instead of “the design principle is flawed, and what you provide is inadequate”, the only correct expectation for a FLOSS product offered you as-is from an organization that is not a big corporation (that would be using the free product as attractor for its paid services) would be “I realize that I get whatever volunteers could provide; and in the best case, I would also try to contribute whenever I see something that needs improving”.

The documentation is simply what we have. And no, we do not have paid stuff that is employed to fulfil customers’ needs (including preparing documentation of some established quality); the volunteers work hard - on what they know and think needs improvement. And if there’s a shortage that you feel important, your only options are either wait for a miracle like some volunteer deciding to fix, or to try to make it happen actively (not by a rant, but e.g. by doing things yourself - becoming a part of the team, and making it better for everyone - or by finding someone to do that (e.g., crowdfunding, or becoming a customer of someone who make living providing customer services and contribute to the project)).

In fact, I fail to see why you wrote what you wrote. I now find it much less appealing to try to help questions like yours, spending time and debugging the code to see the options, just to read later that “what you provide is poor”. Sad.

I suspect that this entire exercise is futile. It seems to me that by the time the listener receives its first event LO is already committed to printing the document. Calling cancelJob seems to have no effect, and neither does changing the value of “CopyCount” in the printer options. Anyway, I have already spent too much time on this, I shall probably put it aside until I can work out an alternative approach.

Thank you to everyone for your time. Including the time spent responding to my comment about documentation.