Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch printer front-end for SecureDrop Workstation #2156

Open
eloquence opened this issue Jul 18, 2019 · 17 comments
Open

Switch printer front-end for SecureDrop Workstation #2156

eloquence opened this issue Jul 18, 2019 · 17 comments

Comments

@eloquence
Copy link
Member

The current proof-of-concept printing implementation in freedomofpress/securedrop-workstation#277 uses xpp as a printer front-end. We should compare this with other front-end options in terms of:

  • usability
  • aesthetics
  • functionality

and then make a final decision about what front-end to use for the beta.

@eloquence
Copy link
Member Author

eloquence commented Jul 18, 2019

Screenshots of all dialogs in xpp (taken on Ubuntu 18.04):

Screenshot from 2019-07-18 15-46-59
Screenshot from 2019-07-18 15-47-02
Screenshot from 2019-07-18 15-47-04
Screenshot from 2019-07-18 15-47-07
Screenshot from 2019-07-18 15-47-10
Screenshot from 2019-07-18 15-47-12
Screenshot from 2019-07-18 15-47-17
Screenshot from 2019-07-18 15-47-25

Screenshots of all dialogs in gpr (taken on Ubuntu 18.04; cannot show "Configuration" options without configuring a PPD printer)

Screenshot from 2019-07-18 15-49-48
Screenshot from 2019-07-18 15-49-41

Screenshots of all dialogs in gtkpl (taken on Ubuntu 18.04)

Screenshot from 2019-07-18 15-51-49
Screenshot from 2019-07-18 15-51-52
Screenshot from 2019-07-18 15-51-56
Screenshot from 2019-07-18 15-51-58
Screenshot from 2019-07-18 15-52-01
Screenshot from 2019-07-18 15-52-04
Screenshot from 2019-07-18 15-52-07
Screenshot from 2019-07-18 15-52-09
Screenshot from 2019-07-18 15-52-11

@eloquence
Copy link
Member Author

eloquence commented Jul 18, 2019

I have excluded kprinter4, the KDE printing utility available in Ubuntu 18.04, because a) I was not able to get it to actually run, b) the dependencies likely make it prohibitive to install.

Another option that I think is worth considering, however, is to create a very lightweight wrapper around GTK's printing dialog, in combination with the Poppler PDF library or a PostScript library. I was able to use this example code to print PDFs with the GTK dialogs. Here are the GTK dialogs for comparison with the above front-ends, including an example of a preview:

GTK dialogs based on simple Python script using GLib, Gtk, and Poppler

Screenshot from 2019-07-18 16-09-27
Screenshot from 2019-07-18 16-09-30
Screenshot from 2019-07-18 16-09-32
Screenshot from 2019-07-18 16-09-36
Screenshot from 2019-07-18 16-09-39
Screenshot from 2019-07-18 15-57-06

If we used this option, we'd have to "just" find a way to convert every supported file format to PDF prior to printing, but we're already doing some processing to get files to work with lpr.

@eloquence
Copy link
Member Author

eloquence commented Jul 18, 2019

NB: I would argue that all the above choices, with the possible exception of gpr, satisfy the must-have requirements for a first iteration:

  • printing specific pages
  • printing multiple copies
  • setting print quality

I did not find a print quality option in gpr, but it's possible that this is available in the printer options when a PPD printer is configured. In terms of aesthetics and usability, I think the GTK dialogs are clearly preferable to all other options, using clearer labeling, sensible font and color choices, and making the most common choices (e.g., page range selection) immediately available. It's also most similar to printer dialogs on other operating systems and applications.

@kushaldas
Copy link
Contributor

We should discuss this soon. The UI of the current xpp is really bad and I could not find any preview option.

@eloquence eloquence changed the title Select final print front-end for SecureDrop Workstation Switch printer front-end for SecureDrop Workstation Aug 11, 2022
@eloquence
Copy link
Member Author

Update from 2022-08-11 review with @tina-ux @nathandyer @L3th3 @eloquence @zenmonkeykstop:

  • This is still a relevant issue. We are currently using xpp in the closed beta.
  • Let's discuss further in the context of the overall printer epic.

@tina-ux tina-ux added the ux label Oct 31, 2022
@zenmonkeykstop
Copy link
Contributor

Still an open issue - we have added xpp to our bookworm apt repo to make it available for 4.2, but it should get replaced.

@legoktm legoktm transferred this issue from freedomofpress/securedrop-workstation Aug 7, 2024
@legoktm legoktm added this to the 0.15.0 milestone Aug 7, 2024
@deeplow deeplow self-assigned this Oct 4, 2024
@deeplow
Copy link
Contributor

deeplow commented Oct 14, 2024

I got the chance to talk to the lead maintainer of OpenPrinting. I found out that XPP as the front-end in fact does very little. All that it does is surface to the user the printer and document options. CUPS does the remaining processing. However, as we know, some formats CUPS cannot understand. That's why we're already converting LibreOffice files to PDF (which is exactly what the LibreOffice print dialogue does before sending it to cups).

GTK dialogs based on simple Python script using GLib, Gtk, and Poppler

I couldn't find any minimally modern-looking print dialogues, so if this dialogue minimally satisfies our needs, it could be a way to go, if we end up going with the "simply replace XPP approach" (which may only be a temporary solution).

But it is not as complicated as I was originally thinking since it doesn't actually require implementing non-PDF file converters. (CC @zenmonkeykstop).

@deeplow
Copy link
Contributor

deeplow commented Nov 18, 2024

GTK dialogs based on simple Python script using GLib, Gtk, and Poppler

The need for creating a custom prompt lead me down a rabbit hole. The available alternatives (gpr, gtkpl, etc.) look too outdated and I don't think every application would need to implement its own custom print dialog using whatever UI toolkit they use (GTK, Qt, etc.).

There had to be something else. Something desktop-environment agnostic which applications can just call and the system deals with tit. The approach I found is to use flatpak portals. In reality, these are "XDG"/ freedesktop specifications (thus not directly tied to flatpak) - they are just GBus calls. And I don't think we have to install anything extra. It should be installed by default.

For example, with the following line we can open a dialog:

gdbus call --session --dest org.freedesktop.portal.Desktop --object-path=/org/freedesktop/portal/desktop --method org.freedesktop.portal.Print.PreparePrint "program-name" "title" [] [] []

left

The image on the left is fedora and the image on the right is Debian. As you can see, there are slight differences, but I'm guessing this is because they are using different versions of xdg-desktop-portal-gtk.

We can also pass in parameters and a file descriptor, but unfortunately DBus is not very well documented from my experience. I've got some luck learning about how to use it by running dbus-monitor --session and then running the Evince (PDF viewer) flatpak and seeing how it call a print dialog. If we go down this router, the next steps would be to find out how to pass in the file descriptor properly, but that should be about it.

After the initial print dialog call, we then need to issue a second call for actually printing. I haven't exactly understood how to make this call, but inspecting the DBus "traffic" from Evince, it looks something like this:

print_portal

Comparison with alternatives

Advantages:

Disadvantages:

  • added development complexity due to the need to parse GDBus messages. There are some ways to do this in Python, but they require python libraries and some are even specific to UI Frameworks (like the GTK one -- see here an implementation example)

References:

@deeplow
Copy link
Contributor

deeplow commented Nov 18, 2024

Qt Dialog

I've also looked into what the default Qt print dialog would look like.

Default view "Options >>" expanded
qt_dialog2 Qt_dialog
Clicking on "Properties" button Double-sided Options tab
qt_settings double_sided

Regarding requirements:

  • ✔️ printing specific pages
  • ✔️ printing multiple copies
  • ❌ setting print quality

ℹ️ update: the Qt dialog cannot handle when the printer is disconnected and reconnected while the dialog is up, which could be helpful for faster troubleshooting. Furthermore, the GTK dialog does show status information (e.g. connecting to printer...) beside the printer.

boilerplate Qt5 python code used

First you have to install PyQt5. Assuming you're on Debian, you need to run sudo apt install python3-pyqt5.

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout, QPushButton, QLabel
from PyQt5.QtGui import QPainter
from PyQt5.QtPrintSupport import QPrintDialog, QPrinter
from PyQt5.QtCore import Qt

class HelloWorldPrintDialogWidget(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle("Your Options Tab Title")
        layout = QVBoxLayout()

        self.label = QLabel("Print more text?")
        layout.addWidget(self.label)

        self.print_more_text_button = QPushButton("Print More Text")
        self.print_more_text_button.setCheckable(True)
        self.print_more_text_button.toggled.connect(self.setPrintMoreText)
        layout.addWidget(self.print_more_text_button)

        self.setLayout(layout)
        self.print_more_text = False

    def setPrintMoreText(self, state):
        self.print_more_text = state

    def printMoreText(self):
        return self.print_more_text

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle("Printing Tutorial")
        self.setGeometry(100, 100, 400, 300)

        self.optionsWidget = HelloWorldPrintDialogWidget()

        print_button = QPushButton("Print Hello World", self)
        print_button.clicked.connect(self.printDialog)
        self.setCentralWidget(print_button)

    def printDialog(self):
        printer = QPrinter()
        printer.setFullPage(True)

        print_dialog = QPrintDialog(printer, self)
        print_dialog.setWindowTitle("Print Hello World")
        print_dialog.exec_()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    mainWin = MainWindow()
    mainWin.show()
    sys.exit(app.exec_())

@deeplow
Copy link
Contributor

deeplow commented Nov 20, 2024

GTK dialogs based on simple Python script using GLib, Gtk, and Poppler

After much searching, I finally found something which creates a GTK dialog without the need for any code of ours. The program is yad (yet another dialog). It's a dialog tool that unlike zenity, actually has print dialog options 🙌. With a simple call like this:

yad --print --filename ~/QubesIncoming/sd-dev/building-qubes.pdf

We get the standard GTK prompt, without the need for digging into DBus or create our own custom GTK print dialog.

Furthermore, installing it in sd-bookworm-large-template should only add 173KB because we already have installed the GTK and other dependencies. I don't know what others think but in my eyes, this looks like the most suitable XPP replacement candidate. It should satisfy all requirements (after all, it's a default GTK dialog)


Update: I'm having basic issues with yad. When printing PDF files it is literally just printing the raw file (literally printing the start of the file as if it were a binary text file) and I simply cannot print image files (yad:9966): Pango-WARNING **: 15:25:53.310: Invalid UTF-8 string passed to pango_layout_set_text(). For this reason alone, we should discard this print dialog.

@rocodes
Copy link
Contributor

rocodes commented Nov 28, 2024

Hey @deeplow, thank you so much for looking into all these options. (And for the sample qt code).

It looks like yad has had recent releases/maintenance, but one maintainer at the moment based on github activity, and some potential limitations (no signed releases/commits, no CI, etc).

I think feel more confident with a printer frontend that was already upstreamed into Debian and/or had a little more of a process around it re code review and release, or (I hate to say it) maybe our own tiny qt package if that's really all it takes is the snippet above. What are your thoughts?

@deeplow
Copy link
Contributor

deeplow commented Nov 28, 2024

FYI, it is in fact in Debian.

We could go the Qt way as well. I would argue it doesn't look as polished compared to the GTK one, but it gets the job done (except for the print quality).

@rocodes
Copy link
Contributor

rocodes commented Nov 28, 2024

Cool, thanks for letting me know. It's not exact, but I'm looking for the balance of usability/ease of inclusion vs rough dependency addition heuristics (even in an untrusted vm). With the limited activity, no security policy/ci/code-signing/etc in the repo, if there are no other suitable alternatives, I'm personally leaning towards the Qt or GTK way, whatever is easiest, because they are all an improvement over the current, and because ideally this will be the state only until we (eventually?) rework the print workflow to print from viewer, where we'll be able to use the print dialogs of whatever application is opening the file. But open to others' thoughts.

@deeplow
Copy link
Contributor

deeplow commented Nov 28, 2024

That's true. This is a temporary solution. So as long as it is an improvement and satisfies the requirements, we should be fine.

@deeplow
Copy link
Contributor

deeplow commented Dec 2, 2024

I just came across a limitation of the Qt dialog compared to the GTK one:

ℹ️ update: the Qt dialog cannot handle when the printer is disconnected and reconnected while the dialog is up, which could be helpful for faster troubleshooting. Furthermore, the GTK dialog does show status information (e.g. connecting to printer...) beside the printer.

Depending on how we go about handling driverless printining, printer status information and printer status could be delegated to the printer itself instead of the client needing a back-and-forth to communicate this information to the user. CC @nathandyer, I can imagine this being helpful. Don't know if you have oppinions on the GTK / Qt dialog options.

@nathandyer
Copy link

Thanks @deeplow! Although I'm generally a fan of the GTK way of doing things, for this purpose and in this environment, I don't have any strong feelings in preferring one toolkit over another. I agree that showing status information next to the printer, and being able to handle disconnects more elegantly, makes the GTK dialog a stronger option. I also think the layout is a little more straightforward (fewer collapsable panels and additional windows/dialog boxes for viewing properties and setting print options).

That being said, I don't think those benefits are make-or-break, and if Qt is easier to implement or works better in our existing Qt-based world, it probably makes sense to go with that option instead.

@deeplow
Copy link
Contributor

deeplow commented Dec 10, 2024

Thanks for taking a look. Sadly I spoke too soon about the Qt implementation. For some reason I forgot to test if it actually printed correctly. It turns out that I was just setting up the dialog and the print actually does nothing. It's just a blank dialog. So we also need document rendering-code. I have found this example which does that, but it was something I was kind of avoiding because CUPS can already handle printing.

I would have expected for there to be a way to have Qt / GTK dialogs using the CUPS back-end and not having to make a rendered for any kind of file we print. This is disappointing and the documentation it not being very helpful. There may be a way to do it, but I am not finding any reference to it.

Supposedly GTK already has the CUPS Common Printing Dialog Backend in version 4.0 (which I don't know how well it runs on Qubes). But I have seen no docs on how to take advantage of it in python at least. I am guessing this should be automatic and not really a concern for us. On the Qt front from what I can see, it still hasn't been merged.

@deeplow deeplow moved this from Cycle Backlog to In Progress in SecureDrop dev cycle Jan 13, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: In Progress
Development

No branches or pull requests

8 participants