Xpra: Ticket #913: html5 printing

Looking at #598 it seems as though we can register a printer to be created on the Xpra server and get a PDF output from it.

I think this could be integrated into the HTML5 client by creating a "browser printer" and loading the PDF into a new window / tab that the user then initiates printing again through the local browser (possibly using https://mozilla.github.io/pdf.js/ )

Wed, 08 Jul 2015 15:11:52 GMT - Antoine Martin:

Yes, the printer definition we send to the server is minimal at present, so we don't need to worry about knowing anything about the local printers (paper size, etc). So all you would need to send is the printer name. The more difficult part is going to be feeding the PDF data packet to the page, but I suspect pdf.js will support that.

Wed, 08 Jul 2015 16:21:19 GMT - Josh:

Yes, it should be able to be rendered by pdf.js, or in Webkit browsers natively through a base64 encoded data uri.

Seems that most detail needed for implementation starts around here https://www.xpra.org/trac/browser/xpra/trunk/src/xpra/client/client_base.py#L723 would just need to make a fake "html5 printer" dict to send to server in the right format, something like

  "HTML5 client": {
    "printer-info" : "Print to PDF in client browser",
    "printer-make-and-model": "HTML5 client version"

Wed, 05 Aug 2015 14:26:21 GMT - Josh:

@antoine: the mimetype of the file data sent when printing is always application/postscript... is the data really postscript or PDF?

Thu, 06 Aug 2015 04:31:00 GMT - Antoine Martin:

Yes, it is hard-coded in the "xpraforwarder" cups backend. IIRC I had tried to use pdf and had some problems on one of the platforms I think (win32?).

I'll try to see if I can move this to the client-side platform code only and send the "real" mimetype instead. (supporting 0.15 will make this interesting, or maybe I won't bother)

Sun, 09 Aug 2015 14:25:24 GMT - Antoine Martin: attachment set

example of a file we generate when printing

Sun, 09 Aug 2015 17:17:25 GMT - Antoine Martin:

OK, AFAIK we can't easily inspect the data which is fed to the cups pdf ppd filter, or the file that is fed to the xpraforwarder (potential enhancement there), but we can see the resulting file client side (which should be identical to the one the xpraforwarder sees) using:

XPRA_DELETE_PRINTER_FILE=0 xpra attach -d printing ...

This should show you what filename was used, and you can then inspect it.

The file attached to this ticket is a "hello world" example printed from gedit. The header looks like this:

%%HiResBoundingBox: 0 0 596.00 842.00
%%Creator: GPL Ghostscript 916 (ps2write)
%%LanguageLevel: 2
%%CreationDate: D:20150809212358+07'00'
%%For: (antoine)
%%Title: (Unsaved Document 1)
%RBINumCopies: 1
%%Pages: (atend)
%%BoundingBox: (atend)

So... we are sending postscript and not PDF. No idea why the output of the cups-pdf filter is not actually PDF! (I guess that explains why I had to force the mimetype to postscript)

I have even tested the backend using raw mode (important fix required for raw mode: r10248):

XPRA_PPD_FILE="" xpra start ...

And gedit was actually sending PDF to the print queue! So PDF + PDF filter -> Postscript!?

Options I can think of:

@joshiggins: what is your preference here?


Mon, 10 Aug 2015 14:34:24 GMT - Josh:

I am by no means an expert in CUPS but...

There is some nice info in here Bug #820820 “cups-pdf should not convert PDF print jobs to PS th...”

It seems as though many applications are already sending PDF or at least, will get converted by *topdf filters in modern CUPS PDF as Standard Print Job Format which probably explains why gedit is actually resulting in PDF to the queue (in which case, the least cost filter would be pdftopdf).

This makes me think that CUPS-PDF is not needed at all... by increasing the cost factor of pstops filter will result in a PS -> PDF conversion, which is apparently default in newer distros shipping CUPS >=1.6.

We could support most configurations by simply checking the mimetype against a list supported by the client as you suggest, and running ps2pdf to convert if necessary. But in reality, what I understand is that we should mostly be getting PDF nowadays anyway.

I have no idea why PDF + PDF filter -> Postscript (is CUPS-PDF being invoked at all?) but in any case we shouldn't rely on this quirk - AFAICT the current printing method uses just postscript so we shouldn't even need it.

To summarise (sorry!)

Tue, 11 Aug 2015 04:39:47 GMT - Antoine Martin:

Adding printer without PPD, in modern distro we should expect PDF input to the backend script (built in *topdf filters)? - Need to confirm/test this

Yes, I have tested this using the XPRA_PPD_FILE="", but this breaks printing text files via "lpr" - more on this below.

Best to check mimetype(s) that the client supports and whether this is what we get in the backend script

I agree... We'll need a simple map: {"application/ps" : "pstopdf", ...}

For server running with older CUPS we don't support PDF (thus printing in HTML5 client also) and dump CUPS-PDF

I don't understand this. Can you elaborate?

Thanks for the ticket link. It would be nice if they could fix the filter... but there have been no updates in this ticket for almost 2 years. So let's forget about this for now.

Another option I can think of would be to run without filter (XPRA_PPDF_FILE="") and emulate what cups does: run "texttopdf" or whatever is needed on the input (and skip all filters for pdf). Then we also avoid the roundtrip via ps. Not sure if we lose any options like page setup (A4 vs A3 and such).

Tue, 11 Aug 2015 12:10:49 GMT - Josh:

Can you elaborate?

I was thinking that we could use a PDF printing workflow if available, and degrade gracefully to PS workflow if the server had an old CUPS. Within the backend script, we would just have to match the client supported mimetype with the data we got (because it is guaranteed to be PS or PDF, unless we printed in raw mode, which we shouldn't?). We don't need CUPS-PDF because all the *topdf filters are built in (unless it is an older CUPS, in which case we fall back to PS but lose printing support in the HTML5 client). Does that make sense?

It seems that there is also a PPD file already for us that handles PDF:

joshiggins@josht440:/usr/share/cups$ lpinfo --make-and-model 'Generic PDF Printer' -m
lsb/usr/cupsfilters/Generic-PDF_Printer-PDF.ppd Generic PDF Printer

On Ubuntu, this is under /usr/share/ppd/cupsfilters/ (I found it in the same place on F22 also) and low and behold, it just works, even with text file input from lpr.

For Postscript output, old CUPS will have /usr/share/cups/model/postscript.ppd or new CUPS uses the driver drv:///sample.drv/generic.ppd.

Therefore, I would propose:

This way we can hopefully not break PS on platforms where pdf was not working, and get PDF support without external dependencies on server with supported CUPS. I could probably have a pretty good stab at some of this if that sounds reasonable...

Tue, 11 Aug 2015 12:25:11 GMT - Josh:

Of course, the ugly solution is to run XPRA_PPDF_FILE="/usr/share/ppd/cupsfilters/Generic-PDF_Printer-PDF.ppd" xpra start ... and ignore the mimetype in the HTML5 client...

Tue, 11 Aug 2015 13:18:14 GMT - Antoine Martin: owner, status changed

I think that's a good plan, and if you can ignore the mimetype for now as a workaround then I'll deal with the server changes later.

Mon, 24 Aug 2015 08:27:43 GMT - Antoine Martin: owner, status changed

Done in r10432, see commit message for details.

The new "mimetypes" feature is per forwarded printer rather than in the hello packet, which is more flexible. If the client supports both pdf and postscript, we choose pdf as of r10433.

@joshiggins: you should be able to simply add: mimetypes : ["application/pdf"] to your virtual printer definition to get PDF as output.

If that works for you, please re-assign to afarr for testing.

Mon, 24 Aug 2015 19:33:38 GMT - Josh:

@antoine: The mimetype feature appears to be working well but it doesn't look like the PPD file is actually passed to the lpadmin command, the created printer is appearing as raw.

2015-08-24 20:28:43,909 using printer definition '['-P', '/usr/share/ppd/cupsfilters/Generic-PDF_Printer-PDF.ppd']' for application/pdf
2015-08-24 20:28:43,910 pycups_printing adding printer: ['-p', 'HTML5-client', '-E', '-v', 'xpraforwarder:/tmp?mimetype=application%2Fpdf&socket-path=%2Fhome%2Fjoshiggins%2F.xpra%2Fubuntu-10&remote-printer=HTML5+client&remote-device-uri=None&socket-dir=%2Fhome%2Fjoshiggins%2F.xpra&source=ed27963d2c843e8de873ea15c08ad3b6&display=%3A10', '-D', 'Print to PDF in client browser', '-L', 'via xpra', '-o', 'printer-is-shared=false', '-u', 'allow:joshiggins']
2015-08-24 20:28:43,911 exec_lpadmin(['-p', 'HTML5-client', '-E', '-v', 'xpraforwarder:/tmp?mimetype=application%2Fpdf&socket-path=%2Fhome%2Fjoshiggins%2F.xpra%2Fubuntu-10&remote-printer=HTML5+client&remote-device-uri=None&socket-dir=%2Fhome%2Fjoshiggins%2F.xpra&source=ed27963d2c843e8de873ea15c08ad3b6&display=%3A10', '-D', 'Print to PDF in client browser', '-L', 'via xpra', '-o', 'printer-is-shared=false', '-u', 'allow:joshiggins']) command=['/usr/sbin/lpadmin', '-p', 'HTML5-client', '-E', '-v', 'xpraforwarder:/tmp?mimetype=application%2Fpdf&socket-path=%2Fhome%2Fjoshiggins%2F.xpra%2Fubuntu-10&remote-printer=HTML5+client&remote-device-uri=None&socket-dir=%2Fhome%2Fjoshiggins%2F.xpra&source=ed27963d2c843e8de873ea15c08ad3b6&display=%3A10', '-D', 'Print to PDF in client browser', '-L', 'via xpra', '-o', 'printer-is-shared=false', '-u', 'allow:joshiggins']
2015-08-24 20:28:43,932 client does not support any csc modes with vp9
2015-08-24 20:28:43,933 client does not support any csc modes with vp8
2015-08-24 20:28:43,968 returncode(['/usr/sbin/lpadmin', '-p', 'HTML5-client', '-E', '-v', 'xpraforwarder:/tmp?mimetype=application%2Fpdf&socket-path=%2Fhome%2Fjoshiggins%2F.xpra%2Fubuntu-10&remote-printer=HTML5+client&remote-device-uri=None&socket-dir=%2Fhome%2Fjoshiggins%2F.xpra&source=ed27963d2c843e8de873ea15c08ad3b6&display=%3A10', '-D', 'Print to PDF in client browser', '-L', 'via xpra', '-o', 'printer-is-shared=false', '-u', 'allow:joshiggins'])=0

Mon, 24 Aug 2015 19:41:04 GMT - Josh:

p.s. r10436 brings html5 printing support to working state for Chrome and Firefox by opening a new window with the PDF as a base64 encoded data URI.

This doesn't play as well with IE, but neither does pdf.js.

Tue, 25 Aug 2015 04:25:19 GMT - Antoine Martin:

Doh, r10437 fixes that. Not much difference in the output (I had forgotten to re-test with raw applications like 'lpr' after some last minute code style changes!).

@joshiggins: if you're happy with the state of things, please re-assign to afarr for testing.

@afarr: it is worth spending a bit of time to see the output of the printing setup with different settings, under different OSes, as you may want to use 'raw' with your app, or not.

Testing recap:

Tue, 25 Aug 2015 09:04:19 GMT - Josh:

@antoine: It works really well for PDF printing and in the HTML5 client!

But I think that the postscript side might be broken - is it safe to rely on a quirk of the CUPS-PDF PPD that is actually outputs postscript (whether expected or not)? There are some other options for postscript output...

joshiggins@ubuntu:/usr/share/ppd/cupsfilters$ lpinfo --make-and-model 'Generic Postscript Printer' -m
drv:///sample.drv/generic.ppd Generic PostScript Printer
foomatic-db-compressed-ppds:0/ppd/foomatic-ppd/Generic-PostScript_Printer-Postscript.ppd Generic PostScript Printer Foomatic/Postscript (recommended)

Tue, 25 Aug 2015 09:06:04 GMT - Antoine Martin:

@joshiggins: very good point, maybe we should try to find all the other ones first?

Tue, 25 Aug 2015 09:15:34 GMT - Josh:

The problem is that generic.ppd and Generic-PostScript_Printer-Postscript.ppd are provided by drivers and don't have real PPD files somewhere on the system so the current detection code won't work.

Would it be sensible to call lpinfo with the make and model identifiers for PDF and postscript instead of re-implementing search code?

Tue, 25 Aug 2015 09:28:46 GMT - Antoine Martin:

That explains why I couldn't find them!

We could parse the lpinfo output I guess. Execing external commands is always a bit problematic (they can hang, the output format can change, etc..), but I can't think of another way.

Some questions you may be able to help me with:

PS: r10444 updates the rpm and deb package dependencies to include "cups-filters" as well as "cups-pdf" (which we may be able to drop later?).

Mon, 07 Sep 2015 16:42:50 GMT - Josh:

Sorry for the delay in getting back to this.

what would the lpadmin command look like for using the "Generic Postscript Printer" ppd since there is no file?

It seems to just use the -m switch a la lpadmin -p "testps" -m drv:///sample.drv/generic.ppd

packaging: what is the debian / ubuntu package we can depend on?

As far as i can tell (i.e. dpkg -S) we should depend on "cups-filters" to get the PDF ppd and the Postscript driver in sample.drv is provided by "cups-common".

Mon, 07 Sep 2015 16:45:47 GMT - Antoine Martin: owner, status changed

Thanks, I'll get on it tomorrow.

Tue, 08 Sep 2015 12:21:06 GMT - Antoine Martin:

@joshiggins: just one more thing... since there is no ppd file we can check, how do we know if the lpadmin command is going to succeed at runtime?

Thu, 10 Sep 2015 06:47:43 GMT - Antoine Martin: owner, status changed

Mostly implemented in r10582 + some later fixups (made a mess with too many uncommited changes sitting in my local tree), see commit message for details.

This new "generic printers" code can be disabled using the env var XPRA_PRINTERS_GENERIC=0.

I had to add config file options so we can pre-define the printer ppd files / driver strings because running "lpinfo" every time more than doubled the server statup time! (this is also a bit more user friendly than the env var hack it supersedes) Mine now looks like this:

$ grep "printer =" /etc/xpra/xpra.conf  | grep -v "^#"
postscript-printer = drv:///sample.drv/generic.ppd
pdf-printer = /usr/share/ppd/cupsfilters/Generic-PDF_Printer-PDF.ppd

@joshiggins: I can't seem to get any meaningful information out of lpinfo for the generic PDF printer:

$ lpinfo --make-and-model 'Generic PDF Printer' -m
lsb/opt/cupsfilters/Generic-PDF_Printer-PDF.ppd Generic PDF Printer
lsb/usr/cupsfilters/Generic-PDF_Printer-PDF.ppd Generic PDF Printer

Those paths are invalid! (and I have absolutely no idea how we are supposed to convert them into valid paths)

So the generic PDF printer we detect is still based on detected cupsfilters paths rather than the "lpinfo" output. (I guess I could split that string to figure out the ppd file name we should be looking for - meh)

You can run the pycups script from the command line to see what gets detected:

$ ./xpra/platform/pycups_printing.py -v
get_printer_definitions() UNPROBED_PRINTER_DEFS={}, GENERIC=True
get_lpinfo_drv(Generic PostScript Printer) command=['lpinfo', '--make-and-model', 'Generic PostScript Printer', '-m']
lpinfo out=drv:///sample.drv/generic.ppd Generic PostScript Printer\nfoomatic:Generic-PostScript_Printer-Postscript.ppd Generic PostScript Printer Foomatic/Postscript\n
lpinfo err=
get_lpinfo_drv(Generic PDF Printer) command=['lpinfo', '--make-and-model', 'Generic PDF Printer', '-m']
lpinfo out=lsb/opt/cupsfilters/Generic-PDF_Printer-PDF.ppd Generic PDF Printer\nlsb/usr/cupsfilters/Generic-PDF_Printer-PDF.ppd Generic PDF Printer\n
lpinfo err=
pycups settings: PRINTER_DEF={'application/postscript': ['-m', 'drv:///sample.drv/generic.ppd'], 'application/pdf': ['-P', '/usr/share/ppd/cupsfilters/Generic-PDF_Printer-PDF.ppd']}
SELinux is present
SELinux enforce=0
SELinux is present but not in enforcing mode
* application/postscript          : ['-m', 'drv:///sample.drv/generic.ppd']
* application/pdf                 : ['-P', '/usr/share/ppd/cupsfilters/Generic-PDF_Printer-PDF.ppd']

I have tested with a Linux client with:

XPRA_PRINTING_PREFERRED_MIMETYPE="application/postscript" \
    xpra  attach -d printing ...

And then verified:

Fri, 13 Nov 2015 13:41:41 GMT - Antoine Martin: milestone changed

Can we close this for 0.16?

Wed, 18 Nov 2015 18:49:24 GMT - Josh:

In the current functionality it's working well for me so yes, I think it can be closed. We can revisit improvements to this in a new ticket.

Wed, 18 Nov 2015 18:49:56 GMT - Josh: status changed; resolution set

Sat, 23 Jan 2021 05:09:37 GMT - migration script:

this ticket has been moved to: https://github.com/Xpra-org/xpra/issues/913